From a87c640311fe8d9b884ce97c9ed6bf37156423f1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 22 Jul 2022 13:20:10 +0300 Subject: [PATCH 001/339] [m2 native] user actor installation (#491) --- Cargo.lock | 203 +++++++++++++++++-------------------- Cargo.toml | 18 ++-- actors/init/Cargo.toml | 3 +- actors/init/src/lib.rs | 72 ++++++++++++- actors/init/src/state.rs | 46 ++++++++- actors/init/src/types.rs | 20 ++++ runtime/Cargo.toml | 3 +- runtime/src/runtime/fvm.rs | 5 + runtime/src/runtime/mod.rs | 3 + runtime/src/test_utils.rs | 5 + test_vm/Cargo.toml | 3 + test_vm/src/lib.rs | 5 + 12 files changed, 264 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 878bca6da..a1eba62fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "arrayref" @@ -67,14 +67,14 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43" +checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" dependencies = [ "async-channel", "async-executor", "async-io", - "async-mutex", + "async-lock", "blocking", "futures-lite", "num_cpus", @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" dependencies = [ "concurrent-queue", "futures-lite", @@ -109,20 +109,11 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - [[package]] name = "async-std" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-channel", "async-global-executor", @@ -137,7 +128,6 @@ dependencies = [ "kv-log-macro", "log", "memchr", - "num_cpus", "once_cell", "pin-project-lite", "pin-utils", @@ -147,15 +137,15 @@ dependencies = [ [[package]] name = "async-task" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -187,9 +177,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base-x" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "base64" @@ -272,9 +262,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "byteorder" @@ -325,16 +315,16 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.18" +version = "3.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "69c5a7f9997be616e47f0577ee38c91decb33392c5be4866494f34cdf329a9aa" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim", "termcolor", "textwrap", @@ -342,9 +332,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ "heck", "proc-macro-error", @@ -355,9 +345,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -397,19 +387,19 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" dependencies = [ "cfg-if", - "lazy_static", + "once_cell", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" dependencies = [ "generic-array", "typenum", @@ -538,9 +528,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "event-listener" @@ -983,8 +973,7 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09e5aa7de45452676d18fcb70b750acd65faae7a4fe18fe784b4c85f869fb" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "ahash", "anyhow", @@ -1000,8 +989,7 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1587207c7455ea70a762d4360f502b6728ea6e2dfbec2b66b6d4d943ee29ae" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1013,8 +1001,7 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "anyhow", "cid", @@ -1039,8 +1026,7 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9cfcb4ae444801009182fded790dd56aff9c9a567e54510e2ccf14fb9daf8e" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "anyhow", "cid", @@ -1057,8 +1043,7 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b5c939897aa1bfd63e7cb9c458ba10689371af3278ff20d66c6f8ca152c6c0" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "anyhow", "byteorder", @@ -1077,9 +1062,8 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "1.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd162cff8cab78c3798762bc6879429ecbd0f5515856bcb3bc31d197208db70" +version = "2.0.0-alpha.1" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1093,8 +1077,7 @@ dependencies = [ [[package]] name = "fvm_shared" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c2485c563703b730df0502e2e8fcea86bc183b7018a080c6a5b15b7a29c5d0" +source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" dependencies = [ "anyhow", "bimap", @@ -1132,9 +1115,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "js-sys", @@ -1157,9 +1140,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" [[package]] name = "heck" @@ -1190,9 +1173,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -1210,9 +1193,9 @@ dependencies = [ [[package]] name = "integer-encoding" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e85a1509a128c855368e135cffcde7eac17d8e1083f41e2b98c58bc1a5074be" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" dependencies = [ "async-trait", "futures-util", @@ -1235,18 +1218,18 @@ checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kv-log-macro" @@ -1371,9 +1354,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ "num-traits", ] @@ -1412,9 +1395,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-bigint", @@ -1443,15 +1426,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "os_str_bytes" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "parking" @@ -1526,18 +1509,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -1574,9 +1557,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -1585,9 +1568,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "ryu" @@ -1603,9 +1586,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" dependencies = [ "serde_derive", ] @@ -1630,9 +1613,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" dependencies = [ "proc-macro2", "quote", @@ -1653,9 +1636,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa", "ryu", @@ -1739,9 +1722,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", @@ -1851,9 +1834,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-xid" @@ -1891,15 +1874,15 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1907,9 +1890,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", @@ -1922,9 +1905,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" dependencies = [ "cfg-if", "js-sys", @@ -1934,9 +1917,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1944,9 +1927,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" dependencies = [ "proc-macro2", "quote", @@ -1957,15 +1940,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 70eca5005..ba5cf24a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ calibrationnet = [] devnet = [] testing = [] testing-fake-proofs = [] +m2-native = [] [workspace] members = [ @@ -47,14 +48,15 @@ members = [ "test_vm", ] -#[patch.crates-io] -#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } +[patch.crates-io] +## TODO point this to master +fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } ## 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. diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 97b905afa..8663e105d 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -28,6 +28,7 @@ fvm_ipld_encoding = "0.2.2" [dev-dependencies] fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } + [features] fil-actor = [] - +m2-native = [] diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index a0be862ec..10e597ee7 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -6,7 +6,6 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; @@ -31,6 +30,8 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, Exec = 2, + #[cfg(feature = "m2-native")] + InstallCode = 3, } /// Init actor @@ -106,6 +107,55 @@ impl Actor { Ok(ExecReturn { id_address: Address::new_id(id_address), robust_address }) } + + #[cfg(feature = "m2-native")] + pub fn install(rt: &mut RT, params: InstallParams) -> Result + where + BS: Blockstore, + RT: Runtime, + { + use cid::multihash::Code; + use fvm_ipld_blockstore::Block; + + 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)).map_err(|e| { + e.downcast_default( + ExitCode::USR_SERIALIZATION, + "failed to put code into the bockstore", + ) + })?; + + if st.is_installed_actor(rt.store(), &code_cid).map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_STATE, + "failed to check state for installed actor", + ) + })? { + return Ok((code_cid, false)); + } + + rt.install_actor(&code_cid).map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_ARGUMENT, + "failed to check state for installed actor", + ) + })?; + + st.add_installed_actor(rt.store(), code_cid).map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_STATE, + "failed to add installed actor to state", + ) + })?; + Ok((code_cid, true)) + })?; + + Ok(InstallReturn { code_cid, installed }) + } } impl ActorCode for Actor { @@ -127,16 +177,24 @@ impl ActorCode for Actor { let res = Self::exec(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + #[cfg(feature = "m2-native")] + Some(Method::InstallCode) => { + let res = Self::install(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } } +#[cfg(not(feature = "m2-native"))] fn can_exec(rt: &RT, caller: &Cid, exec: &Cid) -> bool where BS: Blockstore, RT: Runtime, { + use fvm_shared::actor::builtin::Type; + rt.resolve_builtin_actor_type(exec) .map(|typ| match typ { Type::Multisig | Type::PaymentChannel => true, @@ -145,3 +203,15 @@ where }) .unwrap_or(false) } + +#[cfg(feature = "m2-native")] +fn can_exec(rt: &RT, caller: &Cid, exec: &Cid) -> bool +where + BS: Blockstore, + RT: Runtime, +{ + // 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 a08b75e53..e1b29567b 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0, MIT use anyhow::anyhow; +#[cfg(feature = "m2-native")] +use cid::multihash::Code; use cid::Cid; use fil_actors_runtime::{ make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, @@ -9,6 +11,8 @@ use fil_actors_runtime::{ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; +#[cfg(feature = "m2-native")] +use fvm_ipld_encoding::CborStore; use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::{Address, Protocol}; use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; @@ -19,6 +23,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 { @@ -26,7 +32,15 @@ impl State { let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) .flush() .map_err(|e| anyhow!("failed to create empty map: {}", e))?; - 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)?; + Ok(Self { + address_map: empty_map, + next_id: FIRST_NON_SINGLETON_ADDR, + network_name, + #[cfg(feature = "m2-native")] + installed_actors, + }) } /// Allocates a new ID address and stores a mapping of the argument address to it. @@ -69,6 +83,36 @@ impl State { Ok(map.get(&addr.to_bytes())?.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, + ) -> anyhow::Result { + let installed: Vec = match store.get_cbor(&self.installed_actors)? { + 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, + ) -> anyhow::Result<()> { + let mut installed: Vec = match store.get_cbor(&self.installed_actors)? { + Some(v) => v, + None => Vec::new(), + }; + installed.push(cid); + self.installed_actors = store.put_cbor(&installed, Code::Blake2b256)?; + Ok(()) + } } impl Cbor for State {} diff --git a/actors/init/src/types.rs b/actors/init/src/types.rs index 2464d9e49..07b08505f 100644 --- a/actors/init/src/types.rs +++ b/actors/init/src/types.rs @@ -30,3 +30,23 @@ pub struct ExecReturn { impl Cbor for ExecReturn {} impl Cbor for ExecParams {} + +/// 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, +} + +#[cfg(feature = "m2-native")] +impl Cbor for InstallParams {} +#[cfg(feature = "m2-native")] +impl Cbor for InstallReturn {} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b8c47361f..bbe9ca297 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -27,7 +27,7 @@ thiserror = "1.0.30" getrandom = { version = "0.2.5", features = ["js"] } hex = { version = "0.4.3", optional = true } anyhow = "1.0.56" -fvm_sdk = { version = "1.0.0-rc.3", optional = true } +fvm_sdk = { version = "2.0.0-alpha.1", optional = true } blake2b_simd = "1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" @@ -47,6 +47,7 @@ hex = "0.4.3" [features] default = [] fil-actor = ["fvm_sdk"] +m2-native = ["fvm_sdk/m2-native"] # Enable 2k sectors sector-2k = [] diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index aaba6ff13..71c290b86 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -411,6 +411,11 @@ where fvm::crypto::compute_unsealed_sector_cid(proof_type, pieces) .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")) + } } #[cfg(not(feature = "fake-proofs"))] diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index b37b67924..6bc25d61d 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -196,6 +196,9 @@ pub trait Primitives { signer: &Address, plaintext: &[u8], ) -> Result<(), 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/test_utils.rs b/runtime/src/test_utils.rs index ad64e166e..fc7654bc2 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1075,6 +1075,11 @@ impl Primitives for MockRuntime { } Ok(exp.cid) } + + #[cfg(feature = "m2-native")] + fn install_actor(&self, _code_cid: &Cid) -> anyhow::Result<(), anyhow::Error> { + Ok(()) + } } impl Verifier for MockRuntime { diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index afcde88b3..adc37df53 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -46,3 +46,6 @@ regex = "1" [dev-dependencies] cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } multihash = { version = "0.16.1", default-features = false } + +[features] +m2-native = [] diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 26fd8d108..c43325421 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -960,6 +960,11 @@ impl Primitives for InvocationCtx<'_, '_> { ) -> Result { 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") + } } impl Verifier for InvocationCtx<'_, '_> { From 427bc99af3990f0f8a71cd95f84ec8ca096df6fe Mon Sep 17 00:00:00 2001 From: raulk Date: Thu, 28 Jul 2022 15:14:57 +0100 Subject: [PATCH 002/339] add devnet-wasm and wallaby networks with associated features to build scripts (#504) * add devnet-m2-native network with associated features to build scripts * transitively activate m2-native on runtime * add devnet-m2-native bundle build target * fix output target for m2-native bundle * pacify clippy. * runtime: Cargo.toml sort dependencies. * Cargo.toml: point patches to master. * fix devnet-m2-bundle by propagating Cargo features. * add build preset / bundle configurations for wallaby and devnet-wasm. QoL finishing touches: - The 1:N network/features mapping in runtime/build.rs wasn't cutting it any longer. I expanded this data structure to M:N. - Added docs to runtime/build.rs. Co-authored-by: vyzo --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- .gitignore | 2 +- Cargo.lock | 14 ++++++------ Cargo.toml | 15 ++++++------- Makefile | 6 +++++ actors/init/Cargo.toml | 6 ++--- actors/init/src/lib.rs | 2 +- build.rs | 26 +++++++++++++++++++++- runtime/Cargo.toml | 41 +++++++++++++++++------------------ runtime/build.rs | 31 ++++++++++++++++++-------- 11 files changed, 94 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b791772e0..5fd84c2ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,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', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb112fa25..355743475 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', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 609dd565a..5b6079d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ target *.wasm .idea/ **/.DS_Store -output/builtin-actors.car +output/*.car diff --git a/Cargo.lock b/Cargo.lock index a1eba62fa..d9cf4357e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -973,7 +973,7 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "ahash", "anyhow", @@ -989,7 +989,7 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.2" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1001,7 +1001,7 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "anyhow", "cid", @@ -1026,7 +1026,7 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "anyhow", "cid", @@ -1043,7 +1043,7 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "anyhow", "byteorder", @@ -1063,7 +1063,7 @@ dependencies = [ [[package]] name = "fvm_sdk" version = "2.0.0-alpha.1" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1077,7 +1077,7 @@ dependencies = [ [[package]] name = "fvm_shared" version = "0.8.0" -source = "git+https://github.com/filecoin-project/ref-fvm#cfbb6b11878afc90afde964da6b22c4709db5123" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" dependencies = [ "anyhow", "bimap", diff --git a/Cargo.toml b/Cargo.toml index ba5cf24a8..c79b4dd98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,14 +49,13 @@ members = [ ] [patch.crates-io] -## TODO point this to master -fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } ## 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. diff --git a/Makefile b/Makefile index 82cda1b9e..6496b0bfe 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,12 @@ 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-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/actors/init/Cargo.toml b/actors/init/Cargo.toml index 8663e105d..4aa90ce4d 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } @@ -30,5 +30,5 @@ fvm_ipld_encoding = "0.2.2" fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } [features] -fil-actor = [] -m2-native = [] +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 10e597ee7..26b2ddce2 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -205,7 +205,7 @@ where } #[cfg(feature = "m2-native")] -fn can_exec(rt: &RT, caller: &Cid, exec: &Cid) -> bool +fn can_exec(_rt: &RT, _caller: &Cid, _exec: &Cid) -> bool where BS: Blockstore, RT: Runtime, diff --git a/build.rs b/build.rs index 067f6a328..80dd9d872 100644 --- a/build.rs +++ b/build.rs @@ -27,6 +27,16 @@ const ACTORS: &[(&Package, &ID)] = &[ const WASM_FEATURES: &[&str] = &["+bulk-memory", "+crt-static"]; +/// 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"]), + /*("devnet-evm", &["m2-fevm"]),*/ + ("wallaby", &["m2-native"]), +]; + const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; /// Returns the configured network name, checking both the environment and feature flags. @@ -43,6 +53,10 @@ 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 = "testing") { Some("testing") } else if cfg!(feature = "testing-fake-proofs") { @@ -106,6 +120,16 @@ fn main() -> Result<(), Box> { WASM_FEATURES.iter().flat_map(|flag| ["-Ctarget-feature=", *flag, " "]).collect::() + "-Clink-arg=--export-table"; + // 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") @@ -113,7 +137,7 @@ fn main() -> Result<(), Box> { .arg("--target=wasm32-unknown-unknown") .arg("--profile=wasm") .arg("--locked") - .arg("--features=fil-actor") + .arg("--features=".to_owned() + &features.join(",")) .arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap()) .env("RUSTFLAGS", rustflags) .env(NETWORK_ENV, network_name) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index bbe9ca297..aac3941b5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,34 +8,33 @@ edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] -fvm_ipld_hamt = "0.5.1" -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_shared = { version = "0.8.0", default-features = false } -num-traits = "0.2.14" -num-derive = "0.3.3" -serde = { version = "1.0.136", features = ["derive"] } -lazy_static = "1.4.0" -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.56" -fvm_sdk = { version = "2.0.0-alpha.1", optional = true } +base64 = "0.13.0" blake2b_simd = "1.0" +byteorder = "1.4.3" +cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } +fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" +fvm_ipld_hamt = "0.5.1" +fvm_sdk = { version = "2.0.0-alpha.1", optional = true } +fvm_shared = { version = "0.8.0", default-features = false } +getrandom = { version = "0.2.5", features = ["js"] } +hex = { version = "0.4.3", optional = true } +indexmap = { version = "1.8.0", features = ["serde-1"] } +integer-encoding = { version = "3.0.3", default-features = false } +itertools = "0.10" +lazy_static = "1.4.0" +log = "0.4.14" multihash = { version = "0.16.1", default-features = false } +num-derive = "0.3.3" +num-traits = "0.2.14" rand = "0.8.5" -serde_repr = "0.1.8" regex = "1" -itertools = "0.10" +serde = { version = "1.0.136", features = ["derive"] } +serde_repr = "0.1.8" +thiserror = "1.0.30" +unsigned-varint = "0.7.1" [dependencies.sha2] version = "0.10" diff --git a/runtime/build.rs b/runtime/build.rs index 767b810bf..7f7324e11 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,14 @@ 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", "wallaby"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), + (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), ( - "testing", + &["devnet", "devnet-wasm" /*devnet-fevm*/], + &["sector-2k", "sector-8m", "small-deals", "short-precommit", "min-power-2k"], + ), + ( + &["testing"], &[ "sector-2k", "sector-8m", @@ -29,7 +32,7 @@ static NETWORKS: &[(&str, &[&str])] = &[ ], ), ( - "testing-fake-proofs", + &["testing-fake-proofs"], &[ "sector-2k", "sector-8m", @@ -46,12 +49,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); } From c28b82076e062a5a27c0dc1dec9d9a9acc55b6a5 Mon Sep 17 00:00:00 2001 From: raulk Date: Thu, 28 Jul 2022 15:27:29 +0100 Subject: [PATCH 003/339] [next] bump crates versions to 10.0.0-alpha.1 (#507) --- Cargo.lock | 30 +++++++++++++++--------------- Cargo.toml | 24 ++++++++++++------------ actors/account/Cargo.toml | 4 ++-- actors/cron/Cargo.toml | 4 ++-- actors/init/Cargo.toml | 4 ++-- actors/market/Cargo.toml | 4 ++-- actors/miner/Cargo.toml | 4 ++-- actors/multisig/Cargo.toml | 4 ++-- actors/paych/Cargo.toml | 4 ++-- actors/power/Cargo.toml | 4 ++-- actors/reward/Cargo.toml | 4 ++-- actors/system/Cargo.toml | 4 ++-- actors/verifreg/Cargo.toml | 4 ++-- runtime/Cargo.toml | 2 +- state/Cargo.toml | 26 +++++++++++++------------- test_vm/Cargo.toml | 28 ++++++++++++++-------------- 16 files changed, 77 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05bccdd2b..58edc315e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,7 +549,7 @@ dependencies = [ [[package]] name = "fil_actor_account" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", @@ -582,7 +582,7 @@ dependencies = [ [[package]] name = "fil_actor_cron" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", @@ -596,7 +596,7 @@ dependencies = [ [[package]] name = "fil_actor_init" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -613,7 +613,7 @@ dependencies = [ [[package]] name = "fil_actor_market" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -639,7 +639,7 @@ dependencies = [ [[package]] name = "fil_actor_miner" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "byteorder", @@ -667,7 +667,7 @@ dependencies = [ [[package]] name = "fil_actor_multisig" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -685,7 +685,7 @@ dependencies = [ [[package]] name = "fil_actor_paych" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -702,7 +702,7 @@ dependencies = [ [[package]] name = "fil_actor_power" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -723,7 +723,7 @@ dependencies = [ [[package]] name = "fil_actor_reward" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", @@ -739,7 +739,7 @@ dependencies = [ [[package]] name = "fil_actor_system" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -754,7 +754,7 @@ dependencies = [ [[package]] name = "fil_actor_verifreg" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -771,7 +771,7 @@ dependencies = [ [[package]] name = "fil_actors_runtime" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "base64", @@ -806,7 +806,7 @@ dependencies = [ [[package]] name = "fil_builtin_actors_bundle" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "cid", "clap", @@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "fil_builtin_actors_state" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "cid", @@ -1754,7 +1754,7 @@ dependencies = [ [[package]] name = "test_vm" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" dependencies = [ "anyhow", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index 847c2ac6e..e0fc58b45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_builtin_actors_bundle" description = "Bundle of FVM-compatible Wasm bytecode for Filecoin builtin actors" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["Protocol Labs", "Filecoin Core Devs"] edition = "2021" @@ -13,17 +13,17 @@ exclude = ["examples", ".github"] publish = false [target.'cfg(target_arch = "wasm32")'.dependencies] -fil_actor_account = { version = "9.0.0-alpha.1", path = "./actors/account", features = ["fil-actor"] } -fil_actor_verifreg = { version = "9.0.0-alpha.1", path = "./actors/verifreg", features = ["fil-actor"] } -fil_actor_cron = { version = "9.0.0-alpha.1", path = "./actors/cron", features = ["fil-actor"] } -fil_actor_market = { version = "9.0.0-alpha.1", path = "./actors/market", features = ["fil-actor"] } -fil_actor_multisig = { version = "9.0.0-alpha.1", path = "./actors/multisig", features = ["fil-actor"] } -fil_actor_paych = { version = "9.0.0-alpha.1", path = "./actors/paych", features = ["fil-actor"] } -fil_actor_power = { version = "9.0.0-alpha.1", path = "./actors/power", features = ["fil-actor"] } -fil_actor_miner = { version = "9.0.0-alpha.1", path = "./actors/miner", features = ["fil-actor"] } -fil_actor_reward = { version = "9.0.0-alpha.1", path = "./actors/reward", features = ["fil-actor"] } -fil_actor_system = { version = "9.0.0-alpha.1", path = "./actors/system", features = ["fil-actor"] } -fil_actor_init = { version = "9.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } +fil_actor_account = { version = "10.0.0-alpha.1", path = "./actors/account", features = ["fil-actor"] } +fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "./actors/verifreg", features = ["fil-actor"] } +fil_actor_cron = { version = "10.0.0-alpha.1", path = "./actors/cron", features = ["fil-actor"] } +fil_actor_market = { version = "10.0.0-alpha.1", path = "./actors/market", features = ["fil-actor"] } +fil_actor_multisig = { version = "10.0.0-alpha.1", path = "./actors/multisig", features = ["fil-actor"] } +fil_actor_paych = { version = "10.0.0-alpha.1", path = "./actors/paych", features = ["fil-actor"] } +fil_actor_power = { version = "10.0.0-alpha.1", path = "./actors/power", features = ["fil-actor"] } +fil_actor_miner = { version = "10.0.0-alpha.1", path = "./actors/miner", features = ["fil-actor"] } +fil_actor_reward = { version = "10.0.0-alpha.1", path = "./actors/reward", features = ["fil-actor"] } +fil_actor_system = { version = "10.0.0-alpha.1", path = "./actors/system", features = ["fil-actor"] } +fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } [build-dependencies] fil_actor_bundler = "3.0.4" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index febbc1dcc..f0b62a434 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_account" description = "Builtin account actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -13,7 +13,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 08825ce03..c698c653d 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_cron" description = "Builtin cron actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 72dbc5e50..196bdf0ae 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_init" description = "Builtin init actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime" } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 476b32a25..e97c9d0f4 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_market" description = "Builtin market actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index cc838b001..7c6f32b8f 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_miner" description = "Builtin miner actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index a1003310e..40b47e092 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_multisig" description = "Builtin multisig actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index cb651062a..3825737ce 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_paych" description = "Builtin paych actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index eaa6c47c8..3d806f417 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_power" description = "Builtin power actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index d4d71faa0..a432671fc 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_reward" description = "Builtin reward actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 455c633d4..8eb1735ed 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_system" description = "Builtin system actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 8cccfd726..26335cc60 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actor_verifreg" description = "Builtin verifreg actor for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,7 +14,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_shared = { version = "0.8.0", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index aac3941b5..e14c3d0ec 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_actors_runtime" description = "System actors for the Filecoin protocol" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2021" diff --git a/state/Cargo.toml b/state/Cargo.toml index 55923f6e0..60cc0621b 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fil_builtin_actors_state" description = "Builtin Actor state utils for Filecoin" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] edition = "2018" @@ -14,18 +14,18 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actor_account = { version = "9.0.0-alpha.1", path = "../actors/account"} -fil_actor_verifreg = { version = "9.0.0-alpha.1", path = "../actors/verifreg"} -fil_actor_cron = { version = "9.0.0-alpha.1", path = "../actors/cron"} -fil_actor_market = { version = "9.0.0-alpha.1", path = "../actors/market"} -fil_actor_multisig = { version = "9.0.0-alpha.1", path = "../actors/multisig"} -fil_actor_paych = { version = "9.0.0-alpha.1", path = "../actors/paych"} -fil_actor_power = { version = "9.0.0-alpha.1", path = "../actors/power"} -fil_actor_miner = { version = "9.0.0-alpha.1", path = "../actors/miner"} -fil_actor_reward = { version = "9.0.0-alpha.1", path = "../actors/reward"} -fil_actor_system = { version = "9.0.0-alpha.1", path = "../actors/system"} -fil_actor_init = { version = "9.0.0-alpha.1", path = "../actors/init"} -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../runtime"} +fil_actor_account = { version = "10.0.0-alpha.1", path = "../actors/account"} +fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "../actors/verifreg"} +fil_actor_cron = { version = "10.0.0-alpha.1", path = "../actors/cron"} +fil_actor_market = { version = "10.0.0-alpha.1", path = "../actors/market"} +fil_actor_multisig = { version = "10.0.0-alpha.1", path = "../actors/multisig"} +fil_actor_paych = { version = "10.0.0-alpha.1", path = "../actors/paych"} +fil_actor_power = { version = "10.0.0-alpha.1", path = "../actors/power"} +fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner"} +fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} +fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} +fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index ca4392937..97524ccb3 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test_vm" description = "Reference vm for integration testing builtin actors" -version = "9.0.0-alpha.1" +version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["Protocol Labs", "Filecoin Core Devs"] edition = "2021" @@ -11,19 +11,19 @@ publish = false [lib] [dependencies] -fil_builtin_actors_state = { version = "9.0.0-alpha.1", path = "../state"} -fil_actors_runtime = { version = "9.0.0-alpha.1", path = "../runtime", features = [ "test_utils" ] } -fil_actor_init = { version = "9.0.0-alpha.1", path = "../actors/init" } -fil_actor_cron = { version = "9.0.0-alpha.1", path = "../actors/cron" } -fil_actor_system = { version = "9.0.0-alpha.1", path = "../actors/system" } -fil_actor_account = { version = "9.0.0-alpha.1", path = "../actors/account" } -fil_actor_multisig = { version = "9.0.0-alpha.1", path = "../actors/multisig" } -fil_actor_paych = { version = "9.0.0-alpha.1", path = "../actors/paych" } -fil_actor_reward = { version = "9.0.0-alpha.1", path = "../actors/reward" } -fil_actor_power = { version = "9.0.0-alpha.1", path = "../actors/power" } -fil_actor_market = { version = "9.0.0-alpha.1", path = "../actors/market" } -fil_actor_verifreg = { version = "9.0.0-alpha.1", path = "../actors/verifreg" } -fil_actor_miner = { version = "9.0.0-alpha.1", path = "../actors/miner" } +fil_builtin_actors_state = { version = "10.0.0-alpha.1", path = "../state"} +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime", features = [ "test_utils" ] } +fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init" } +fil_actor_cron = { version = "10.0.0-alpha.1", path = "../actors/cron" } +fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system" } +fil_actor_account = { version = "10.0.0-alpha.1", path = "../actors/account" } +fil_actor_multisig = { version = "10.0.0-alpha.1", path = "../actors/multisig" } +fil_actor_paych = { version = "10.0.0-alpha.1", path = "../actors/paych" } +fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward" } +fil_actor_power = { version = "10.0.0-alpha.1", path = "../actors/power" } +fil_actor_market = { version = "10.0.0-alpha.1", path = "../actors/market" } +fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "../actors/verifreg" } +fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner" } lazy_static = "1.4.0" fvm_shared = { version = "0.8.0", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } From cf7892212fa4f3b432a91f46277908d811556a9a Mon Sep 17 00:00:00 2001 From: raulk Date: Thu, 28 Jul 2022 20:52:45 +0100 Subject: [PATCH 004/339] adjust Wallaby parameters. (#508) * adjust Wallaby parameters. - minimum consensus power is 16GiB. - short precommit challenge delay (10 epochs). * fix: minimum consensus power for wallaby Co-authored-by: Aayush --- runtime/build.rs | 6 +++++- runtime/src/runtime/policy.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/runtime/build.rs b/runtime/build.rs index 7f7324e11..e2193242a 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -11,7 +11,11 @@ static NETWORKS: &[(&[&str], &[&str])] = &[ "min-power-2k", ], ), - (&["butterflynet", "wallaby"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), + (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), + ( + &["wallaby"], + &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"], + ), (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), ( &["devnet", "devnet-wasm" /*devnet-fevm*/], diff --git a/runtime/src/runtime/policy.rs b/runtime/src/runtime/policy.rs index 73b29f1c8..bdce767b1 100644 --- a/runtime/src/runtime/policy.rs +++ b/runtime/src/runtime/policy.rs @@ -267,7 +267,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. @@ -385,11 +386,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; From 36ec85aa6e3735447a7fb0dfab2ffcba83906d20 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 18 Aug 2022 13:20:04 +0300 Subject: [PATCH 005/339] basic EVM smart contract actor (#517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raúl Kripalani --- Cargo.lock | 543 ++++++-- Cargo.toml | 3 +- actors/account/Cargo.toml | 3 +- actors/cron/Cargo.toml | 3 +- actors/evm/Cargo.toml | 50 + actors/evm/src/interpreter/LICENSE | 201 +++ actors/evm/src/interpreter/README.md | 27 + actors/evm/src/interpreter/bytecode.rs | 52 + actors/evm/src/interpreter/execution.rs | 221 +++ .../interpreter/instructions/arithmetic.rs | 158 +++ .../src/interpreter/instructions/bitwise.rs | 116 ++ .../src/interpreter/instructions/boolean.rs | 77 ++ .../evm/src/interpreter/instructions/call.rs | 98 ++ .../src/interpreter/instructions/context.rs | 109 ++ .../src/interpreter/instructions/control.rs | 83 ++ .../evm/src/interpreter/instructions/hash.rs | 23 + .../evm/src/interpreter/instructions/log.rs | 14 + .../src/interpreter/instructions/memory.rs | 123 ++ .../evm/src/interpreter/instructions/mod.rs | 11 + .../evm/src/interpreter/instructions/stack.rs | 31 + .../src/interpreter/instructions/storage.rs | 83 ++ .../evm/src/interpreter/instructions/table.rs | 0 actors/evm/src/interpreter/memory.rs | 40 + actors/evm/src/interpreter/message.rs | 73 + actors/evm/src/interpreter/mod.rs | 34 + actors/evm/src/interpreter/opcode.rs | 1214 +++++++++++++++++ actors/evm/src/interpreter/output.rs | 114 ++ actors/evm/src/interpreter/stack.rs | 82 ++ actors/evm/src/interpreter/system.rs | 91 ++ actors/evm/src/interpreter/transaction.rs | 788 +++++++++++ actors/evm/src/interpreter/uints.rs | 295 ++++ actors/evm/src/lib.rs | 190 +++ actors/evm/src/state.rs | 39 + actors/evm/tests/basic.rs | 50 + actors/evm/tests/simplecoin.hex | 1 + actors/evm/tests/simplecoin.sol | 28 + actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 3 +- actors/miner/Cargo.toml | 3 +- actors/multisig/Cargo.toml | 3 +- actors/paych/Cargo.toml | 3 +- actors/power/Cargo.toml | 3 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 3 +- actors/verifreg/Cargo.toml | 3 +- build.rs | 1 + runtime/Cargo.toml | 2 +- runtime/src/test_utils.rs | 2 + state/Cargo.toml | 3 +- state/src/check.rs | 1 + test_vm/Cargo.toml | 3 +- test_vm/src/lib.rs | 2 + 52 files changed, 4958 insertions(+), 149 deletions(-) create mode 100644 actors/evm/Cargo.toml create mode 100644 actors/evm/src/interpreter/LICENSE create mode 100644 actors/evm/src/interpreter/README.md create mode 100644 actors/evm/src/interpreter/bytecode.rs create mode 100644 actors/evm/src/interpreter/execution.rs create mode 100644 actors/evm/src/interpreter/instructions/arithmetic.rs create mode 100644 actors/evm/src/interpreter/instructions/bitwise.rs create mode 100644 actors/evm/src/interpreter/instructions/boolean.rs create mode 100644 actors/evm/src/interpreter/instructions/call.rs create mode 100644 actors/evm/src/interpreter/instructions/context.rs create mode 100644 actors/evm/src/interpreter/instructions/control.rs create mode 100644 actors/evm/src/interpreter/instructions/hash.rs create mode 100644 actors/evm/src/interpreter/instructions/log.rs create mode 100644 actors/evm/src/interpreter/instructions/memory.rs create mode 100644 actors/evm/src/interpreter/instructions/mod.rs create mode 100644 actors/evm/src/interpreter/instructions/stack.rs create mode 100644 actors/evm/src/interpreter/instructions/storage.rs create mode 100644 actors/evm/src/interpreter/instructions/table.rs create mode 100644 actors/evm/src/interpreter/memory.rs create mode 100644 actors/evm/src/interpreter/message.rs create mode 100644 actors/evm/src/interpreter/mod.rs create mode 100644 actors/evm/src/interpreter/opcode.rs create mode 100644 actors/evm/src/interpreter/output.rs create mode 100644 actors/evm/src/interpreter/stack.rs create mode 100644 actors/evm/src/interpreter/system.rs create mode 100644 actors/evm/src/interpreter/transaction.rs create mode 100644 actors/evm/src/interpreter/uints.rs create mode 100644 actors/evm/src/lib.rs create mode 100644 actors/evm/src/state.rs create mode 100644 actors/evm/tests/basic.rs create mode 100644 actors/evm/tests/simplecoin.hex create mode 100644 actors/evm/tests/simplecoin.sol diff --git a/Cargo.lock b/Cargo.lock index 58edc315e..2bb3de25a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" [[package]] name = "arrayref" @@ -39,12 +39,15 @@ name = "arrayvec" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +dependencies = [ + "serde", +] [[package]] name = "async-channel" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ "concurrent-queue", "event-listener", @@ -143,9 +146,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -237,6 +240,15 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -272,6 +284,15 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +dependencies = [ + "serde", +] + [[package]] name = "cache-padded" version = "1.2.0" @@ -301,9 +322,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cid" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc949bff6704880faf064c42a4854032ab07bfcf3a4fcb82a57470acededb69c" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", @@ -315,9 +336,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.10" +version = "3.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c5a7f9997be616e47f0577ee38c91decb33392c5be4866494f34cdf329a9aa" +checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" dependencies = [ "atty", "bitflags", @@ -332,9 +353,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.7" +version = "3.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa" dependencies = [ "heck", "proc-macro-error", @@ -354,9 +375,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" dependencies = [ "cache-padded", ] @@ -367,6 +388,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core2" version = "0.4.0" @@ -387,38 +414,54 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", "once_cell", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "cs_serde_bytes" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebca5e1e931005a5d90cc90831a22619c94fdeb645435c22eae52956dee29675" +checksum = "b7b5b4802671350eaced846f9fa82233c214b5d358ea85289b81c3aed58a0c6c" dependencies = [ "serde", ] [[package]] name = "ctor" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", @@ -516,13 +559,35 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer", + "block-buffer 0.10.2", "crypto-common", ] @@ -534,15 +599,15 @@ checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -563,8 +628,7 @@ dependencies = [ [[package]] name = "fil_actor_bundler" version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715d3ad31a6e4715a49747e136d5daa1a6bbe7c078385bca7e11215c57884a35" +source = "git+https://github.com/filecoin-project/builtin-actors-bundler?branch=next#f2fbadfa810d1544d1a60ebef41ffa4fe08dbf95" dependencies = [ "anyhow", "async-std", @@ -594,6 +658,39 @@ dependencies = [ "serde", ] +[[package]] +name = "fil_actor_evm" +version = "10.0.0-alpha.1" +dependencies = [ + "anyhow", + "arrayvec", + "bytes", + "cid", + "derive_more", + "fil_actors_runtime", + "fixed-hash", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_ipld_hamt", + "fvm_sdk", + "fvm_shared", + "hex", + "hex-literal", + "impl-serde", + "libsecp256k1", + "log", + "multihash", + "num-derive", + "num-traits", + "rlp", + "serde", + "serde_tuple", + "sha3", + "strum", + "strum_macros", + "uint", +] + [[package]] name = "fil_actor_init" version = "10.0.0-alpha.1" @@ -799,7 +896,7 @@ dependencies = [ "regex", "serde", "serde_repr", - "sha2", + "sha2 0.10.2", "thiserror", "unsigned-varint", ] @@ -813,6 +910,7 @@ dependencies = [ "fil_actor_account", "fil_actor_bundler", "fil_actor_cron", + "fil_actor_evm", "fil_actor_init", "fil_actor_market", "fil_actor_miner", @@ -850,6 +948,15 @@ dependencies = [ "serde", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -868,9 +975,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" dependencies = [ "futures-channel", "futures-core", @@ -883,9 +990,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" dependencies = [ "futures-core", "futures-sink", @@ -893,15 +1000,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" dependencies = [ "futures-core", "futures-task", @@ -910,9 +1017,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" [[package]] name = "futures-lite" @@ -931,9 +1038,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" dependencies = [ "proc-macro2", "quote", @@ -942,21 +1049,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" dependencies = [ "futures-channel", "futures-core", @@ -973,7 +1080,7 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "ahash", "anyhow", @@ -989,7 +1096,7 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1001,7 +1108,7 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "anyhow", "cid", @@ -1010,9 +1117,9 @@ dependencies = [ [[package]] name = "fvm_ipld_car" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade8c2f76a9e014f2fdaeb693a0884f49eca5ee663958e1c8c1db8d23290817c" +checksum = "0af5e410b349533e8b1a2b46a7ca3d66cd68c53a477e2cf3d005845c1a346428" dependencies = [ "cid", "futures", @@ -1026,7 +1133,7 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "anyhow", "cid", @@ -1043,7 +1150,7 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "anyhow", "byteorder", @@ -1056,14 +1163,14 @@ dependencies = [ "multihash", "once_cell", "serde", - "sha2", + "sha2 0.10.2", "thiserror", ] [[package]] name = "fvm_sdk" version = "2.0.0-alpha.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1076,8 +1183,8 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "0.8.0" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#e9657a7d1927f154d3020024229fc1122e0b947b" +version = "0.9.0" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" dependencies = [ "anyhow", "bimap", @@ -1105,9 +1212,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -1140,9 +1247,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -1165,12 +1272,48 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[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-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -1212,15 +1355,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" dependencies = [ "wasm-bindgen", ] @@ -1248,9 +1391,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libipld-core" @@ -1267,6 +1410,54 @@ dependencies = [ "thiserror", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "log" version = "0.4.17" @@ -1296,19 +1487,19 @@ dependencies = [ [[package]] name = "multihash" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3db354f401db558759dfc1e568d010a5d4146f4d3f637be1275ec4a3cf09689" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" dependencies = [ "blake2b_simd", "blake2s_simd", "blake3", "core2", - "digest", + "digest 0.10.3", "multihash-derive", "serde", "serde-big-array", - "sha2", + "sha2 0.10.2", "sha3", "unsigned-varint", ] @@ -1426,15 +1617,21 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" + +[[package]] +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "parking" @@ -1475,10 +1672,11 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -1509,18 +1707,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1572,11 +1770,42 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "rlp" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[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 = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "scopeguard" @@ -1584,11 +1813,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" + [[package]] name = "serde" -version = "1.0.139" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" dependencies = [ "serde_derive", ] @@ -1604,18 +1839,18 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" dependencies = [ "proc-macro2", "quote", @@ -1636,9 +1871,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ "itoa", "ryu", @@ -1647,9 +1882,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -1677,6 +1912,19 @@ dependencies = [ "syn", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.2" @@ -1685,24 +1933,27 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.3", ] [[package]] name = "sha3" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +checksum = "0a31480366ec990f395a61b7c08122d99bd40544fdb5abcfc1b06bb29994312c" dependencies = [ - "digest", + "digest 0.10.3", "keccak", ] [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "socket2" @@ -1714,17 +1965,48 @@ dependencies = [ "winapi", ] +[[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" + +[[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 = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -1761,6 +2043,7 @@ dependencies = [ "cid", "fil_actor_account", "fil_actor_cron", + "fil_actor_evm", "fil_actor_init", "fil_actor_market", "fil_actor_miner", @@ -1799,18 +2082,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" dependencies = [ "proc-macro2", "quote", @@ -1832,11 +2115,23 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "uint" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-xid" @@ -1880,9 +2175,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1890,13 +2185,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -1905,9 +2200,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" dependencies = [ "cfg-if", "js-sys", @@ -1917,9 +2212,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1927,9 +2222,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -1940,15 +2235,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index e0fc58b45..3dbe8d638 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,10 @@ fil_actor_miner = { version = "10.0.0-alpha.1", path = "./actors/miner", feature fil_actor_reward = { version = "10.0.0-alpha.1", path = "./actors/reward", features = ["fil-actor"] } fil_actor_system = { version = "10.0.0-alpha.1", path = "./actors/system", features = ["fil-actor"] } fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } +fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] } [build-dependencies] -fil_actor_bundler = "3.0.4" +fil_actor_bundler = { git = "https://github.com/filecoin-project/builtin-actors-bundler", branch = "next" } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } [dependencies] diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index f0b62a434..cd4f14c80 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" @@ -26,4 +26,3 @@ fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector [features] fil-actor = [] - diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index c698c653d..90202ecd7 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" @@ -27,4 +27,3 @@ fvm_ipld_encoding = "0.2.2" fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = [] - diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml new file mode 100644 index 000000000..d7f601ba6 --- /dev/null +++ b/actors/evm/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "fil_actor_evm" +description = "Builtin EVM actor for Filecoin" +version = "10.0.0-alpha.1" +license = "MIT OR Apache-2.0" +authors = ["Protocol Labs", "Filecoin Core Devs"] +edition = "2021" +repository = "https://github.com/filecoin-project/builtin-actors" +keywords = ["filecoin", "web3", "wasm", "evm"] + +[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_sdk = { version = "2.0.0-alpha.1" } +fvm_shared = { version = "0.9.0", default-features = false } +fvm_ipld_hamt = "0.5.1" +serde = { version = "1.0.136", features = ["derive"] } +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.56" +log = "0.4.14" +fvm_ipld_blockstore = "0.1.1" +fvm_ipld_encoding = "0.2.2" +sha3 = { version = "0.10", default-features = false } +rlp = { version = "0.5.1", default-features = false } +bytes = { version = "1.1.0", features = ["serde"], default-features = false } +strum = "0.24" +strum_macros = "0.24" +multihash = { version = "0.16.1", default-features = false } +derive_more = "0.99" +uint = { version = "0.9.3", default-features = false } +fixed-hash = { version = "0.7.0", default-features = false } +impl-serde = { version = "0.3.2", default-features = false } +arrayvec = { version = "0.7.2", features = ["serde"] } +hex = "0.4.3" + +[dev-dependencies] +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } +libsecp256k1 = { version = "0.7.0", features = ["static-context"] } +hex-literal = "0.3.4" + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] +m2-native = ["fil_actors_runtime/m2-native"] 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..841a9e92a --- /dev/null +++ b/actors/evm/src/interpreter/bytecode.rs @@ -0,0 +1,52 @@ +#![allow(dead_code)] + +use {super::opcode::OpCode, crate::interpreter::output::StatusCode, std::ops::Deref}; + +pub struct Bytecode<'c> { + code: &'c [u8], + jumpdest: Vec, +} + +impl<'c> Bytecode<'c> { + pub fn new(bytecode: &'c [u8]) -> Result { + // only jumps to those addresses are valid. This is a security + // feature by EVM to disallow jumps to arbitary code addresses. + // todo: create the jumpdest table only once during initial contract deployment + let mut jumpdest = vec![false; bytecode.len()]; + let mut i = 0; + while i < bytecode.len() { + if bytecode[i] == OpCode::JUMPDEST.code { + jumpdest[i] = true; + i += 1; + } else if bytecode[i] >= OpCode::PUSH1.code && bytecode[i] <= OpCode::PUSH32.code { + i += (bytecode[i] - OpCode::PUSH1.code) as usize + 2; + } else { + i += 1; + } + } + + Ok(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<'c> Deref for Bytecode<'c> { + type Target = [u8]; + + fn deref(&self) -> &'c Self::Target { + self.code + } +} + +impl<'c> AsRef<[u8]> for Bytecode<'c> { + fn as_ref(&self) -> &'c [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..c19a23c2c --- /dev/null +++ b/actors/evm/src/interpreter/execution.rs @@ -0,0 +1,221 @@ +#![allow(dead_code)] + +use { + super::instructions::*, + super::opcode::OpCode, + super::CallKind, + super::StatusCode, + crate::interpreter::instructions::log::*, + crate::interpreter::instructions::stack::*, + crate::interpreter::memory::Memory, + crate::interpreter::stack::Stack, + crate::interpreter::{Bytecode, Output, System}, + bytes::Bytes, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +/// EVM execution runtime. +#[derive(Clone, Debug)] +pub struct ExecutionState { + pub stack: Stack, + pub memory: Memory, + pub input_data: Bytes, + pub return_data: Bytes, + pub output_data: Bytes, +} + +impl ExecutionState { + pub fn new(input_data: Bytes) -> Self { + Self { + stack: Stack::default(), + memory: Memory::default(), + input_data, + return_data: Default::default(), + output_data: Bytes::new(), + } + } +} + +pub fn execute<'r, BS: Blockstore, RT: Runtime>( + bytecode: &Bytecode, + runtime: &mut ExecutionState, + system: &'r System<'r, BS, RT>, +) -> Result { + let mut pc = 0; // program counter + let mut reverted = false; + + loop { + if pc >= bytecode.len() { + break; + } + + let op = OpCode::try_from(bytecode[pc])?; + match op { + OpCode::STOP => break, + OpCode::ADD => arithmetic::add(&mut runtime.stack), + OpCode::MUL => arithmetic::mul(&mut runtime.stack), + OpCode::SUB => arithmetic::sub(&mut runtime.stack), + OpCode::DIV => arithmetic::div(&mut runtime.stack), + OpCode::SDIV => arithmetic::sdiv(&mut runtime.stack), + OpCode::MOD => arithmetic::modulo(&mut runtime.stack), + OpCode::SMOD => arithmetic::smod(&mut runtime.stack), + OpCode::ADDMOD => arithmetic::addmod(&mut runtime.stack), + OpCode::MULMOD => arithmetic::mulmod(&mut runtime.stack), + OpCode::EXP => arithmetic::exp(runtime)?, + OpCode::SIGNEXTEND => arithmetic::signextend(&mut runtime.stack), + OpCode::LT => boolean::lt(&mut runtime.stack), + OpCode::GT => boolean::gt(&mut runtime.stack), + OpCode::SLT => boolean::slt(&mut runtime.stack), + OpCode::SGT => boolean::sgt(&mut runtime.stack), + OpCode::EQ => boolean::eq(&mut runtime.stack), + OpCode::ISZERO => boolean::iszero(&mut runtime.stack), + OpCode::AND => boolean::and(&mut runtime.stack), + OpCode::OR => boolean::or(&mut runtime.stack), + OpCode::XOR => boolean::xor(&mut runtime.stack), + OpCode::NOT => boolean::not(&mut runtime.stack), + OpCode::BYTE => bitwise::byte(&mut runtime.stack), + OpCode::SHL => bitwise::shl(&mut runtime.stack), + OpCode::SHR => bitwise::shr(&mut runtime.stack), + OpCode::SAR => bitwise::sar(&mut runtime.stack), + OpCode::KECCAK256 => hash::keccak256(runtime)?, + OpCode::ADDRESS => context::address(runtime, system)?, + OpCode::BALANCE => storage::balance(runtime, system)?, + OpCode::CALLER => context::caller(runtime, system)?, + OpCode::CALLVALUE => context::call_value(runtime, system), + OpCode::CALLDATALOAD => call::calldataload(runtime), + OpCode::CALLDATASIZE => call::calldatasize(runtime), + OpCode::CALLDATACOPY => call::calldatacopy(runtime)?, + OpCode::CODESIZE => call::codesize(&mut runtime.stack, bytecode.as_ref()), + OpCode::CODECOPY => call::codecopy(runtime, bytecode.as_ref())?, + OpCode::EXTCODESIZE => storage::extcodesize(runtime, system)?, + OpCode::EXTCODECOPY => memory::extcodecopy(runtime, system)?, + OpCode::RETURNDATASIZE => control::returndatasize(runtime), + OpCode::RETURNDATACOPY => control::returndatacopy(runtime)?, + OpCode::EXTCODEHASH => storage::extcodehash(runtime, system)?, + OpCode::BLOCKHASH => context::blockhash(runtime, system)?, + OpCode::ORIGIN => context::origin(runtime, system), + OpCode::COINBASE => context::coinbase(runtime, system)?, + OpCode::GASPRICE => context::gas_price(runtime, system)?, + OpCode::TIMESTAMP => context::timestamp(runtime, system)?, + OpCode::NUMBER => context::block_number(runtime, system)?, + OpCode::DIFFICULTY => context::difficulty(runtime, system)?, + OpCode::GASLIMIT => context::gas_limit(runtime, system)?, + OpCode::CHAINID => context::chain_id(runtime, system)?, + OpCode::BASEFEE => context::base_fee(runtime, system)?, + OpCode::SELFBALANCE => storage::selfbalance(runtime, system)?, + OpCode::POP => stack::pop(&mut runtime.stack), + OpCode::MLOAD => memory::mload(runtime)?, + OpCode::MSTORE => memory::mstore(runtime)?, + OpCode::MSTORE8 => memory::mstore8(runtime)?, + OpCode::JUMP => { + pc = control::jump(&mut runtime.stack, bytecode)?; + continue; // don't increment PC after the jump + } + OpCode::JUMPI => { + // conditional jump + if let Some(dest) = control::jumpi(&mut runtime.stack, bytecode)? { + pc = dest; // condition met, set program counter + continue; // don't increment PC after jump + } + } + OpCode::PC => control::pc(&mut runtime.stack, pc), + OpCode::MSIZE => memory::msize(runtime), + OpCode::SLOAD => storage::sload(runtime, system)?, + OpCode::SSTORE => storage::sstore(runtime, system)?, + OpCode::GAS => control::gas(runtime), + OpCode::JUMPDEST => {} // marker opcode for valid jumps addresses + OpCode::PUSH1 => pc += push::<1>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH2 => pc += push::<2>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH3 => pc += push::<3>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH4 => pc += push::<4>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH5 => pc += push::<5>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH6 => pc += push::<6>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH7 => pc += push::<7>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH8 => pc += push::<8>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH9 => pc += push::<9>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH10 => pc += push::<10>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH11 => pc += push::<11>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH12 => pc += push::<12>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH13 => pc += push::<13>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH14 => pc += push::<14>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH15 => pc += push::<15>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH16 => pc += push::<16>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH17 => pc += push::<17>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH18 => pc += push::<18>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH19 => pc += push::<19>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH20 => pc += push::<20>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH21 => pc += push::<21>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH22 => pc += push::<22>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH23 => pc += push::<23>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH24 => pc += push::<24>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH25 => pc += push::<25>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH26 => pc += push::<26>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH27 => pc += push::<27>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH28 => pc += push::<28>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH29 => pc += push::<29>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH30 => pc += push::<30>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH31 => pc += push::<31>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::PUSH32 => pc += push::<32>(&mut runtime.stack, &bytecode[pc + 1..]), + OpCode::DUP1 => dup::<1>(&mut runtime.stack), + OpCode::DUP2 => dup::<2>(&mut runtime.stack), + OpCode::DUP3 => dup::<3>(&mut runtime.stack), + OpCode::DUP4 => dup::<4>(&mut runtime.stack), + OpCode::DUP5 => dup::<5>(&mut runtime.stack), + OpCode::DUP6 => dup::<6>(&mut runtime.stack), + OpCode::DUP7 => dup::<7>(&mut runtime.stack), + OpCode::DUP8 => dup::<8>(&mut runtime.stack), + OpCode::DUP9 => dup::<9>(&mut runtime.stack), + OpCode::DUP10 => dup::<10>(&mut runtime.stack), + OpCode::DUP11 => dup::<11>(&mut runtime.stack), + OpCode::DUP12 => dup::<12>(&mut runtime.stack), + OpCode::DUP13 => dup::<13>(&mut runtime.stack), + OpCode::DUP14 => dup::<14>(&mut runtime.stack), + OpCode::DUP15 => dup::<15>(&mut runtime.stack), + OpCode::DUP16 => dup::<16>(&mut runtime.stack), + OpCode::SWAP1 => swap::<1>(&mut runtime.stack), + OpCode::SWAP2 => swap::<2>(&mut runtime.stack), + OpCode::SWAP3 => swap::<3>(&mut runtime.stack), + OpCode::SWAP4 => swap::<4>(&mut runtime.stack), + OpCode::SWAP5 => swap::<5>(&mut runtime.stack), + OpCode::SWAP6 => swap::<6>(&mut runtime.stack), + OpCode::SWAP7 => swap::<7>(&mut runtime.stack), + OpCode::SWAP8 => swap::<8>(&mut runtime.stack), + OpCode::SWAP9 => swap::<9>(&mut runtime.stack), + OpCode::SWAP10 => swap::<10>(&mut runtime.stack), + OpCode::SWAP11 => swap::<11>(&mut runtime.stack), + OpCode::SWAP12 => swap::<12>(&mut runtime.stack), + OpCode::SWAP13 => swap::<13>(&mut runtime.stack), + OpCode::SWAP14 => swap::<14>(&mut runtime.stack), + OpCode::SWAP15 => swap::<15>(&mut runtime.stack), + OpCode::SWAP16 => swap::<16>(&mut runtime.stack), + OpCode::LOG0 => log(runtime, system, 0)?, + OpCode::LOG1 => log(runtime, system, 1)?, + OpCode::LOG2 => log(runtime, system, 2)?, + OpCode::LOG3 => log(runtime, system, 3)?, + OpCode::LOG4 => log(runtime, system, 4)?, + OpCode::CREATE => storage::create(runtime, system, false)?, + OpCode::CREATE2 => storage::create(runtime, system, true)?, + OpCode::CALL => call::call(runtime, system, CallKind::Call, false)?, + OpCode::CALLCODE => call::call(runtime, system, CallKind::CallCode, false)?, + OpCode::DELEGATECALL => call::call(runtime, system, CallKind::DelegateCall, false)?, + OpCode::STATICCALL => call::call(runtime, system, CallKind::Call, true)?, + OpCode::RETURN | OpCode::REVERT => { + control::ret(runtime)?; + reverted = op == OpCode::REVERT; + break; + } + OpCode::INVALID => return Err(StatusCode::InvalidInstruction), + OpCode::SELFDESTRUCT => storage::selfdestruct(runtime, system)?, + _ => return Err(StatusCode::UndefinedInstruction), + } + + pc += 1; // advance + } + + Ok(Output { + reverted, + status_code: StatusCode::Success, + output_data: runtime.output_data.clone(), + }) +} diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs new file mode 100644 index 000000000..d9d47ab90 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -0,0 +1,158 @@ +use { + crate::interpreter::output::StatusCode, crate::interpreter::stack::Stack, + crate::interpreter::uints::*, crate::interpreter::ExecutionState, crate::interpreter::U256, +}; + +#[inline] +pub fn add(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + stack.push(a.overflowing_add(b).0); +} + +#[inline] +pub fn mul(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + stack.push(a.overflowing_mul(b).0); +} + +#[inline] +pub fn sub(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + stack.push(a.overflowing_sub(b).0); +} + +#[inline] +pub fn div(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + if *b == U256::zero() { + *b = U256::zero() + } else { + *b = a / *b + } +} + +#[inline] +pub fn sdiv(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + let v = i256_div(a, b); + stack.push(v); +} + +#[inline] +pub fn modulo(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + *b = if *b == U256::zero() { U256::zero() } else { a % *b }; +} + +#[inline] +pub fn smod(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + if *b == U256::zero() { + *b = U256::zero() + } else { + *b = i256_mod(a, *b); + }; +} + +#[inline] +pub fn addmod(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + let c = stack.pop(); + + let v = if c == U256::zero() { + U256::zero() + } else { + let mut a_be = [0u8; 32]; + let mut b_be = [0u8; 32]; + let mut c_be = [0u8; 32]; + + a.to_big_endian(&mut a_be); + b.to_big_endian(&mut b_be); + c.to_big_endian(&mut c_be); + + let a = U512::from_big_endian(&a_be); + let b = U512::from_big_endian(&b_be); + let c = U512::from_big_endian(&c_be); + + let v = a + b % c; + let mut v_be = [0u8; 64]; + v.to_big_endian(&mut v_be); + U256::from_big_endian(&v_be) + }; + + stack.push(v); +} + +#[inline] +pub fn mulmod(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.pop(); + let c = stack.pop(); + + let v = if c == U256::zero() { + U256::zero() + } else { + let mut a_be = [0u8; 32]; + let mut b_be = [0u8; 32]; + let mut c_be = [0u8; 32]; + + a.to_big_endian(&mut a_be); + b.to_big_endian(&mut b_be); + c.to_big_endian(&mut c_be); + + let a = U512::from_big_endian(&a_be); + let b = U512::from_big_endian(&b_be); + let c = U512::from_big_endian(&c_be); + + let v = a * b % c; + let mut v_be = [0u8; 64]; + v.to_big_endian(&mut v_be); + U256::from_big_endian(&v_be) + }; + + stack.push(v); +} + +#[inline] +pub fn signextend(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + if a < U256::from(32) { + let bit_index = (8 * u256_low(a) as u8 + 7) as u16; + let hi = u256_high(*b); + let lo = u256_low(*b); + let bit = if bit_index > 0x7f { hi } else { lo } & (1 << (bit_index % 128)) != 0; + let mask = (U256::from(1) << bit_index) - U256::from(1); + *b = if bit { *b | !mask } else { *b & mask } + } +} + +#[inline] +pub fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> { + let mut base = state.stack.pop(); + let mut power = state.stack.pop(); + + let mut v = U256::from(1); + + while power > U256::zero() { + if (power & U256::from(1)) != U256::zero() { + v = v.overflowing_mul(base).0; + } + power >>= 1; + base = base.overflowing_mul(base).0; + } + + state.stack.push(v); + + Ok(()) +} diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs new file mode 100644 index 000000000..53ceeb424 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -0,0 +1,116 @@ +use { + crate::interpreter::stack::Stack, crate::interpreter::uints::Sign, + crate::interpreter::uints::*, crate::interpreter::U256, +}; + +#[inline] +pub fn byte(stack: &mut Stack) { + let i = stack.pop(); + let x = stack.get_mut(0); + + if i >= U256::from(32) { + *x = U256::zero(); + return; + } + + let mut i = u256_low(i); + + let x_word = if i >= 16 { + i -= 16; + u256_low(*x) + } else { + u256_high(*x) + }; + + *x = U256::from((x_word >> (120 - i * 8)) & 0xFF); +} + +#[inline] +pub fn shl(stack: &mut Stack) { + let shift = stack.pop(); + let value = stack.get_mut(0); + + if *value == U256::zero() || shift >= U256::from(256) { + *value = U256::zero(); + } else { + *value <<= shift + }; +} + +#[inline] +pub fn shr(stack: &mut Stack) { + let shift = stack.pop(); + let value = stack.get_mut(0); + + if *value == U256::zero() || shift >= U256::from(256) { + *value = U256::zero() + } else { + *value >>= shift + }; +} + +#[inline] +pub fn sar(stack: &mut Stack) { + let shift = stack.pop(); + let mut value = stack.pop(); + + let value_sign = i256_sign::(&mut value); + + stack.push(if value == U256::zero() || shift >= U256::from(256) { + match value_sign { + // value is 0 or >=1, pushing 0 + Sign::Plus | Sign::Zero => U256::zero(), + // value is <0, pushing -1 + Sign::Minus => two_compl(U256::from(1)), + } + } else { + let shift = shift.as_u128(); + + match value_sign { + Sign::Plus | Sign::Zero => value >> shift, + Sign::Minus => { + let shifted = ((value.overflowing_sub(U256::from(1)).0) >> shift) + .overflowing_add(U256::from(1)) + .0; + two_compl(shifted) + } + } + }); +} + +#[cfg(test)] +mod tests { + use {super::*, crate::interpreter::uints::u128_words_to_u256}; + + #[test] + fn test_instruction_byte() { + let value = U256::from_big_endian(&(1u8..=32u8).map(|x| 5 * x).collect::>()); + + for i in 0u16..32 { + let mut stack = Stack::new(); + stack.push(value); + stack.push(U256::from(i)); + + byte(&mut stack); + let result = stack.pop(); + + assert_eq!(result, U256::from(5 * (i + 1))); + } + + let mut stack = Stack::new(); + stack.push(value); + stack.push(U256::from(100u128)); + + byte(&mut stack); + let result = stack.pop(); + assert_eq!(result, U256::zero()); + + let mut stack = Stack::new(); + stack.push(value); + stack.push(u128_words_to_u256(1, 0)); + + byte(&mut stack); + let result = stack.pop(); + 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..6fb0958b7 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -0,0 +1,77 @@ +use { + crate::interpreter::stack::Stack, crate::interpreter::uints::*, crate::interpreter::U256, + std::cmp::Ordering, +}; + +#[inline] +pub fn lt(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + *b = if a.lt(b) { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub fn gt(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + *b = if a.gt(b) { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub(crate) fn slt(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + *b = if i256_cmp(a, *b) == Ordering::Less { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub(crate) fn sgt(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + *b = if i256_cmp(a, *b) == Ordering::Greater { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub fn eq(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + + *b = if a.eq(b) { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub fn iszero(stack: &mut Stack) { + let a = stack.get_mut(0); + *a = if *a == U256::zero() { U256::from(1) } else { U256::zero() } +} + +#[inline] +pub(crate) fn and(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + *b = a & *b; +} + +#[inline] +pub(crate) fn or(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + *b = a | *b; +} + +#[inline] +pub(crate) fn xor(stack: &mut Stack) { + let a = stack.pop(); + let b = stack.get_mut(0); + *b = a ^ *b; +} + +#[inline] +pub(crate) fn not(stack: &mut Stack) { + let v = stack.get_mut(0); + *v = !*v; +} diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs new file mode 100644 index 000000000..4d03d7195 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -0,0 +1,98 @@ +use { + super::memory::get_memory_region, crate::interpreter::output::StatusCode, + crate::interpreter::stack::Stack, crate::interpreter::CallKind, + crate::interpreter::ExecutionState, crate::interpreter::System, crate::interpreter::U256, + fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn calldataload(state: &mut ExecutionState) { + let index = state.stack.pop(); + let input_len = state.input_data.len(); + + state.stack.push({ + if index > U256::from(input_len) { + U256::zero() + } else { + let index_usize = index.as_usize(); + let end = core::cmp::min(index_usize + 32, input_len); + + let mut data = [0; 32]; + data[..end - index_usize].copy_from_slice(&state.input_data[index_usize..end]); + + U256::from_big_endian(&data) + } + }); +} + +#[inline] +pub fn calldatasize(state: &mut ExecutionState) { + state.stack.push(u128::try_from(state.input_data.len()).unwrap().into()); +} + +#[inline] +pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { + let mem_index = state.stack.pop(); + let input_index = state.stack.pop(); + let size = state.stack.pop(); + + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + + if let Some(region) = ®ion { + let input_len = U256::from(state.input_data.len()); + let src = core::cmp::min(input_len, input_index); + let copy_size = core::cmp::min(size, input_len - src).as_usize(); + let src = src.as_usize(); + + if copy_size > 0 { + state.memory[region.offset..region.offset + copy_size] + .copy_from_slice(&state.input_data[src..src + copy_size]); + } + + if region.size.get() > copy_size { + state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); + } + } + + Ok(()) +} + +#[inline] +pub fn codesize(stack: &mut Stack, code: &[u8]) { + stack.push(U256::from(code.len())) +} + +#[inline] +pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> { + let mem_index = state.stack.pop(); + let input_index = state.stack.pop(); + let size = state.stack.pop(); + + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + + if let Some(region) = region { + let src = core::cmp::min(U256::from(code.len()), input_index).as_usize(); + let copy_size = core::cmp::min(region.size.get(), code.len() - src); + + if copy_size > 0 { + state.memory[region.offset..region.offset + copy_size] + .copy_from_slice(&code[src..src + copy_size]); + } + + if region.size.get() > copy_size { + state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); + } + } + + Ok(()) +} + +#[inline] +pub fn call<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, + _kind: CallKind, + _is_static: bool, +) -> Result<(), StatusCode> { + todo!(); +} diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs new file mode 100644 index 000000000..a0c5d91fd --- /dev/null +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -0,0 +1,109 @@ +use { + crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn caller<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn call_value<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(&platform.rt.message().value_received())); +} + +#[inline] +pub fn address<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn origin<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.message().caller().id().unwrap())) +} + +#[inline] +pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn gas_price<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn timestamp<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn block_number<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn base_fee<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs new file mode 100644 index 000000000..c13cde067 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -0,0 +1,83 @@ +use { + super::memory::get_memory_region, crate::interpreter::output::StatusCode, + crate::interpreter::stack::Stack, crate::interpreter::Bytecode, + crate::interpreter::ExecutionState, crate::interpreter::U256, +}; + +#[inline] +pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { + let offset = *state.stack.get(0); + let size = *state.stack.get(1); + + if let Some(region) = + super::memory::get_memory_region(state, offset, size).map_err(|_| StatusCode::OutOfGas)? + { + state.output_data = + state.memory[region.offset..region.offset + region.size.get()].to_vec().into(); + } + + Ok(()) +} + +#[inline] +pub fn returndatasize(state: &mut ExecutionState) { + state.stack.push(U256::from(state.return_data.len())); +} + +#[inline] +pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { + let mem_index = state.stack.pop(); + let input_index = state.stack.pop(); + let size = state.stack.pop(); + + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + + if input_index > U256::from(state.return_data.len()) { + return Err(StatusCode::InvalidMemoryAccess); + } + let src = input_index.as_usize(); + + if src + region.as_ref().map(|r| r.size.get()).unwrap_or(0) > state.return_data.len() { + return Err(StatusCode::InvalidMemoryAccess); + } + + 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 gas(_state: &mut ExecutionState) { + todo!() +} + +#[inline] +pub fn pc(stack: &mut Stack, pc: usize) { + stack.push(U256::from(pc)) +} + +#[inline] +pub fn jump(stack: &mut Stack, bytecode: &Bytecode) -> Result { + let dst = stack.pop().as_usize(); + if !bytecode.valid_jump_destination(dst) { + return Err(StatusCode::BadJumpDestination); + } + Ok(dst) +} + +#[inline] +pub fn jumpi(stack: &mut Stack, bytecode: &Bytecode) -> Result, StatusCode> { + if *stack.get(1) != U256::zero() { + let ret = Ok(Some(jump(stack, bytecode)?)); + stack.pop(); + ret + } else { + stack.pop(); + stack.pop(); + + Ok(None) + } +} diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs new file mode 100644 index 000000000..21cd1cdf9 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -0,0 +1,23 @@ +use { + super::memory::get_memory_region, + crate::interpreter::ExecutionState, + crate::interpreter::StatusCode, + crate::interpreter::U256, + sha3::{Digest, Keccak256}, +}; + +pub fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> { + let index = state.stack.pop(); + let size = state.stack.pop(); + + let region = get_memory_region(state, index, size) // + .map_err(|_| StatusCode::OutOfGas)?; + + state.stack.push(U256::from_big_endian(&*Keccak256::digest(if let Some(region) = region { + &state.memory[region.offset..region.offset + region.size.get()] + } else { + &[] + }))); + + Ok(()) +} diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs new file mode 100644 index 000000000..da1212dc6 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -0,0 +1,14 @@ +use { + crate::interpreter::{ExecutionState, StatusCode, System}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn log<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, + _num_topics: usize, +) -> Result<(), StatusCode> { + todo!() +} diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs new file mode 100644 index 000000000..68475c648 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -0,0 +1,123 @@ +#!allow[clippy::result-unit-err] + +use { + crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, + std::num::NonZeroUsize, +}; + +/// The size of the EVM 256-bit word in bytes. +const WORD_SIZE: usize = 32; + +pub struct MemoryRegion { + pub offset: usize, + pub size: NonZeroUsize, +} + +/// Returns number of words what would fit to provided number of bytes, +/// i.e. it rounds up the number bytes to number of words. +#[inline] +pub fn num_words(size_in_bytes: usize) -> usize { + (size_in_bytes + (WORD_SIZE - 1)) / WORD_SIZE +} + +#[inline] +fn grow_memory(state: &mut ExecutionState, new_size: usize) -> Result<(), ()> { + let new_words = num_words(new_size); + state.memory.grow((new_words * WORD_SIZE) as usize); + Ok(()) +} + +#[inline] +fn get_memory_region_u64( + state: &mut ExecutionState, + offset: U256, + size: NonZeroUsize, +) -> Result { + if offset > U256::from(u32::MAX) { + return Err(()); + } + + let new_size = offset.as_usize() + size.get(); + let current_size = state.memory.len(); + if new_size > current_size { + grow_memory(state, new_size)?; + } + + Ok(MemoryRegion { offset: offset.as_usize(), size }) +} + +#[inline] +#[allow(clippy::result_unit_err)] +pub fn get_memory_region( + state: &mut ExecutionState, + offset: U256, + size: U256, +) -> Result, ()> { + if size == U256::zero() { + return Ok(None); + } + + if size > U256::from(u32::MAX) { + return Err(()); + } + + get_memory_region_u64(state, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some) +} + +#[inline] +pub fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> { + let index = state.stack.pop(); + + let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) + .map_err(|_| StatusCode::OutOfGas)?; + let value = + U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); + + state.stack.push(value); + + Ok(()) +} + +#[inline] +pub fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> { + let index = state.stack.pop(); + let value = state.stack.pop(); + + let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) + .map_err(|_| StatusCode::OutOfGas)?; + + let mut bytes = [0u8; WORD_SIZE]; + value.to_big_endian(&mut bytes); + state.memory[region.offset..region.offset + WORD_SIZE].copy_from_slice(&bytes); + + Ok(()) +} + +#[inline] +pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { + let index = state.stack.pop(); + let value = state.stack.pop(); + + let region = get_memory_region_u64(state, index, NonZeroUsize::new(1).unwrap()) + .map_err(|_| StatusCode::OutOfGas)?; + + let value = (value.low_u32() & 0xff) as u8; + + state.memory[region.offset] = value; + + Ok(()) +} + +#[inline] +pub fn msize(state: &mut ExecutionState) { + state.stack.push(u64::try_from(state.memory.len()).unwrap().into()); +} + +pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!(); +} diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs new file mode 100644 index 000000000..4955e5da9 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -0,0 +1,11 @@ +pub mod arithmetic; +pub mod bitwise; +pub mod boolean; +pub mod call; +pub mod context; +pub mod control; +pub mod hash; +pub mod log; +pub mod memory; +pub mod stack; +pub mod storage; diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs new file mode 100644 index 000000000..231a7c4de --- /dev/null +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -0,0 +1,31 @@ +use {crate::interpreter::stack::Stack, crate::interpreter::U256}; + +#[inline] +pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> usize { + let pushval = &code[..LEN]; + stack.push(match pushval.len() { + 0 => U256::zero(), + 32 => U256::from_big_endian(pushval), + _ => { + let mut padded = [0; 32]; + padded[32 - pushval.len()..].copy_from_slice(pushval); + U256::from_big_endian(&padded) + } + }); + LEN +} + +#[inline] +pub(crate) fn dup(stack: &mut Stack) { + stack.push(*stack.get(HEIGHT - 1)); +} + +#[inline] +pub(crate) fn swap(stack: &mut Stack) { + stack.swap_top(HEIGHT); +} + +#[inline] +pub(crate) fn pop(stack: &mut Stack) { + stack.pop(); +} diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs new file mode 100644 index 000000000..be3007c3e --- /dev/null +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -0,0 +1,83 @@ +use { + crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn sload<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + // where? + let location = state.stack.pop(); + + // get from storage and place on stack + let value = match platform.get_storage(location)? { + Some(val) => val, + None => U256::zero(), + }; + state.stack.push(value); + Ok(()) +} + +#[inline] +pub fn sstore<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + let location = state.stack.pop(); + let value = state.stack.pop(); + let opt_value = if value == U256::zero() { None } else { Some(value) }; + + platform.set_storage(location, opt_value)?; + Ok(()) +} + +#[inline] +pub fn balance<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn selfbalance<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} + +pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!(); +} + +#[inline] +pub fn create<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, + _create2: bool, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!() +} diff --git a/actors/evm/src/interpreter/instructions/table.rs b/actors/evm/src/interpreter/instructions/table.rs new file mode 100644 index 000000000..e69de29bb diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs new file mode 100644 index 000000000..b2a346727 --- /dev/null +++ b/actors/evm/src/interpreter/memory.rs @@ -0,0 +1,40 @@ +use { + bytes::BytesMut, + derive_more::{Deref, DerefMut}, +}; + +const PAGE_SIZE: usize = 4 * 1024; + +#[derive(Clone, Debug, Deref, DerefMut)] +pub struct Memory(BytesMut); + +impl Default for Memory { + fn default() -> Self { + Self(BytesMut::with_capacity(PAGE_SIZE)) + } +} + +impl Memory { + #[inline] + pub fn grow(&mut self, size: usize) { + let cap = self.0.capacity(); + if size > cap { + let required_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + self.0.reserve((PAGE_SIZE * required_pages) - self.0.len()); + } + self.0.resize(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 + 1); + assert_eq!(mem.capacity(), PAGE_SIZE * 3); + } +} diff --git a/actors/evm/src/interpreter/message.rs b/actors/evm/src/interpreter/message.rs new file mode 100644 index 000000000..66bf402db --- /dev/null +++ b/actors/evm/src/interpreter/message.rs @@ -0,0 +1,73 @@ +#![allow(dead_code)] + +use { + crate::interpreter::transaction::TransactionAction, + crate::interpreter::SignedTransaction, + crate::interpreter::{H160, U256}, + bytes::Bytes, + fil_actors_runtime::ActorError, + std::fmt::Debug, +}; + +/// The kind of call-like instruction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CallKind { + Call, + DelegateCall, + CallCode, + Create, + Create2 { salt: U256 }, +} + +/// The message describing an EVM call, +/// including a zero-depth call from transaction origin. +#[derive(Clone, Debug, PartialEq)] +pub struct Message { + /// The kind of the call. For zero-depth calls `CallKind::Call` SHOULD be + /// used. + pub kind: CallKind, + + /// Static call mode. + pub is_static: bool, + + /// The call depth. + pub depth: i32, + + /// The amount of gas for message execution. + pub gas: i64, + + /// The destination (recipient) of the message. + pub recipient: H160, + + /// The sender of the message. + pub sender: H160, + + /// Message input data. + pub input_data: Bytes, + + /// The amount of Ether transferred with the message. + pub value: U256, +} + +impl TryFrom for Message { + type Error = ActorError; + + fn try_from(tx: SignedTransaction) -> Result { + Ok(Message { + kind: match tx.transaction.action() { + TransactionAction::Call(_) => CallKind::Call, + TransactionAction::Create => CallKind::Create, + }, + is_static: false, + depth: 0, + gas: tx.transaction.gas_limit() as i64, + recipient: match tx.transaction.action() { + TransactionAction::Call(addr) => addr, + TransactionAction::Create => H160::zero(), + }, + sender: tx.sender_address()?, + input_data: tx.transaction.input(), + value: tx.transaction.value(), + }) + } +} diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs new file mode 100644 index 000000000..68250f0e0 --- /dev/null +++ b/actors/evm/src/interpreter/mod.rs @@ -0,0 +1,34 @@ +pub mod bytecode; +pub mod execution; +pub mod instructions; +pub mod memory; +pub mod message; +pub mod opcode; +pub mod output; +pub mod stack; +pub mod system; +pub mod transaction; +pub mod uints; + +pub use { + bytecode::Bytecode, + execution::{execute, ExecutionState}, + message::{CallKind, Message}, + output::{Output, StatusCode}, + system::System, + transaction::{ + SignedTransaction, Transaction, TransactionAction, TransactionRecoveryId, + TransactionSignature, + }, + uints::{H160, H256, U256, U512}, +}; + +#[macro_export] +macro_rules! abort { + ($code:ident, $msg:literal $(, $ex:expr)*) => { + fvm_sdk::vm::abort( + fvm_shared::error::ExitCode::$code.value(), + Some(format!($msg, $($ex,)*).as_str()), + ) + }; +} diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs new file mode 100644 index 000000000..6427e89bb --- /dev/null +++ b/actors/evm/src/interpreter/opcode.rs @@ -0,0 +1,1214 @@ +//! EVM Opcodes as of Berlin Hard Fork +//! +//! On filecoin we will never have to replay blocks that are older +//! than the release date of the FVM-EVM runtime, so supporting +//! historic behavior is not needed. + +use crate::interpreter::output::StatusCode; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct OpCode { + /// the byte representing the opcode in binary + pub code: u8, + + /// cost of executing the opcode, subtracted from the + /// total gas limit when running bytecode. + pub price: u16, + + /// The number of stack items the instruction accesses during execution. + pub stack_height_required: u8, + + /// The stack height change caused by the instruction execution. Can be + /// negative. + pub stack_height_change: i8, + + /// Human readable name of the opcode. + pub name: &'static str, +} + +impl From for u8 { + fn from(op: OpCode) -> Self { + op.code + } +} + +impl PartialEq for OpCode { + fn eq(&self, other: &u8) -> bool { + self.code == *other + } +} + +const _COLD_SLOAD_COST: u16 = 2100; +const _COLD_ACCOUNT_ACCESS_COST: u16 = 2600; +const WARM_STORAGE_READ_COST: u16 = 100; + +impl OpCode { + pub const ADD: OpCode = OpCode { + code: 0x01, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "ADD", + }; + pub const ADDMOD: OpCode = OpCode { + code: 0x08, + price: 8, + stack_height_required: 3, + stack_height_change: -2, + name: "ADDMOD", + }; + pub const ADDRESS: OpCode = OpCode { + code: 0x30, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "ADDRESS", + }; + pub const AND: OpCode = OpCode { + code: 0x16, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "AND", + }; + pub const BALANCE: OpCode = OpCode { + code: 0x31, + price: WARM_STORAGE_READ_COST, + stack_height_required: 1, + stack_height_change: 0, + name: "BALANCE", + }; + pub const BASEFEE: OpCode = OpCode { + code: 0x48, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "BASEFEE", + }; + pub const BLOCKHASH: OpCode = OpCode { + code: 0x40, + price: 20, + stack_height_required: 1, + stack_height_change: 0, + name: "BLOCKHASH", + }; + pub const BYTE: OpCode = OpCode { + code: 0x1a, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "BYTE", + }; + pub const CALL: OpCode = OpCode { + code: 0xf1, + price: WARM_STORAGE_READ_COST, + stack_height_required: 7, + stack_height_change: -6, + name: "CALL", + }; + pub const CALLCODE: OpCode = OpCode { + code: 0xf2, + price: WARM_STORAGE_READ_COST, + stack_height_required: 7, + stack_height_change: -6, + name: "CALLCODE", + }; + pub const CALLDATACOPY: OpCode = OpCode { + code: 0x37, + price: 3, + stack_height_required: 3, + stack_height_change: -3, + name: "CALLDATACOPY", + }; + pub const CALLDATALOAD: OpCode = OpCode { + code: 0x35, + price: 3, + stack_height_required: 1, + stack_height_change: 0, + name: "CALLDATALOAD", + }; + pub const CALLDATASIZE: OpCode = OpCode { + code: 0x36, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "CALLDATASIZE", + }; + pub const CALLER: OpCode = OpCode { + code: 0x33, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "CALLER", + }; + pub const CALLVALUE: OpCode = OpCode { + code: 0x34, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "CALLVALUE", + }; + pub const CHAINID: OpCode = OpCode { + code: 0x46, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "CHAINID", + }; + pub const CODECOPY: OpCode = OpCode { + code: 0x39, + price: 3, + stack_height_required: 3, + stack_height_change: -3, + name: "CODECOPY", + }; + pub const CODESIZE: OpCode = OpCode { + code: 0x38, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "CODESIZE", + }; + pub const COINBASE: OpCode = OpCode { + code: 0x41, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "COINBASE", + }; + pub const CREATE: OpCode = OpCode { + code: 0xf0, + price: 32000, + stack_height_required: 3, + stack_height_change: -2, + name: "CREATE", + }; + pub const CREATE2: OpCode = OpCode { + code: 0xf5, + price: 32000, + stack_height_required: 4, + stack_height_change: -3, + name: "CREATE2", + }; + pub const DELEGATECALL: OpCode = OpCode { + code: 0xf4, + price: WARM_STORAGE_READ_COST, + stack_height_required: 6, + stack_height_change: -5, + name: "DELEGATECALL", + }; + pub const DIFFICULTY: OpCode = OpCode { + code: 0x44, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "DIFFICULTY", + }; + pub const DIV: OpCode = OpCode { + code: 0x04, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "DIV", + }; + pub const DUP1: OpCode = OpCode { + code: 0x80, + price: 3, + stack_height_required: 1, + stack_height_change: 1, + name: "DUP1", + }; + pub const DUP10: OpCode = OpCode { + code: 0x89, + price: 3, + stack_height_required: 10, + stack_height_change: 1, + name: "DUP10", + }; + pub const DUP11: OpCode = OpCode { + code: 0x8a, + price: 3, + stack_height_required: 11, + stack_height_change: 1, + name: "DUP11", + }; + pub const DUP12: OpCode = OpCode { + code: 0x8b, + price: 3, + stack_height_required: 12, + stack_height_change: 1, + name: "DUP12", + }; + pub const DUP13: OpCode = OpCode { + code: 0x8c, + price: 3, + stack_height_required: 13, + stack_height_change: 1, + name: "DUP13", + }; + pub const DUP14: OpCode = OpCode { + code: 0x8d, + price: 3, + stack_height_required: 14, + stack_height_change: 1, + name: "DUP14", + }; + pub const DUP15: OpCode = OpCode { + code: 0x8e, + price: 3, + stack_height_required: 15, + stack_height_change: 1, + name: "DUP15", + }; + pub const DUP16: OpCode = OpCode { + code: 0x8f, + price: 3, + stack_height_required: 16, + stack_height_change: 1, + name: "DUP16", + }; + pub const DUP2: OpCode = OpCode { + code: 0x81, + price: 3, + stack_height_required: 2, + stack_height_change: 1, + name: "DUP2", + }; + pub const DUP3: OpCode = OpCode { + code: 0x82, + price: 3, + stack_height_required: 3, + stack_height_change: 1, + name: "DUP3", + }; + pub const DUP4: OpCode = OpCode { + code: 0x83, + price: 3, + stack_height_required: 4, + stack_height_change: 1, + name: "DUP4", + }; + pub const DUP5: OpCode = OpCode { + code: 0x84, + price: 3, + stack_height_required: 5, + stack_height_change: 1, + name: "DUP5", + }; + pub const DUP6: OpCode = OpCode { + code: 0x85, + price: 3, + stack_height_required: 6, + stack_height_change: 1, + name: "DUP6", + }; + pub const DUP7: OpCode = OpCode { + code: 0x86, + price: 3, + stack_height_required: 7, + stack_height_change: 1, + name: "DUP7", + }; + pub const DUP8: OpCode = OpCode { + code: 0x87, + price: 3, + stack_height_required: 8, + stack_height_change: 1, + name: "DUP8", + }; + pub const DUP9: OpCode = OpCode { + code: 0x88, + price: 3, + stack_height_required: 9, + stack_height_change: 1, + name: "DUP9", + }; + pub const EQ: OpCode = OpCode { + code: 0x14, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "EQ", + }; + pub const EXP: OpCode = OpCode { + code: 0x0a, + price: 10, + stack_height_required: 2, + stack_height_change: -1, + name: "EXP", + }; + pub const EXTCODECOPY: OpCode = OpCode { + code: 0x3c, + price: WARM_STORAGE_READ_COST, + stack_height_required: 4, + stack_height_change: -4, + name: "EXTCODECOPY", + }; + pub const EXTCODEHASH: OpCode = OpCode { + code: 0x3f, + price: WARM_STORAGE_READ_COST, + stack_height_required: 1, + stack_height_change: 0, + name: "EXTCODEHASH", + }; + pub const EXTCODESIZE: OpCode = OpCode { + code: 0x3b, + price: WARM_STORAGE_READ_COST, + stack_height_required: 1, + stack_height_change: 0, + name: "EXTCODESIZE", + }; + pub const GAS: OpCode = OpCode { + code: 0x5a, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "GAS", + }; + pub const GASLIMIT: OpCode = OpCode { + code: 0x45, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "GASLIMIT", + }; + pub const GASPRICE: OpCode = OpCode { + code: 0x3a, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "GASPRICE", + }; + pub const GT: OpCode = OpCode { + code: 0x11, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "GT", + }; + pub const INVALID: OpCode = OpCode { + code: 0xfe, + price: 0, + stack_height_required: 0, + stack_height_change: 0, + name: "INVALID", + }; + pub const ISZERO: OpCode = OpCode { + code: 0x15, + price: 3, + stack_height_required: 1, + stack_height_change: 0, + name: "ISZERO", + }; + pub const JUMP: OpCode = OpCode { + code: 0x56, + price: 8, + stack_height_required: 1, + stack_height_change: -1, + name: "JUMP", + }; + pub const JUMPDEST: OpCode = OpCode { + code: 0x5b, + price: 1, + stack_height_required: 0, + stack_height_change: 0, + name: "JUMPDEST", + }; + pub const JUMPI: OpCode = OpCode { + code: 0x57, + price: 10, + stack_height_required: 2, + stack_height_change: -2, + name: "JUMPI", + }; + pub const KECCAK256: OpCode = OpCode { + code: 0x20, + price: 30, + stack_height_required: 2, + stack_height_change: -1, + name: "KECCAK256", + }; + pub const LOG0: OpCode = OpCode { + code: 0xa0, + price: 375, + stack_height_required: 2, + stack_height_change: -2, + name: "LOG0", + }; + pub const LOG1: OpCode = OpCode { + code: 0xa1, + price: 2 * 375, + stack_height_required: 3, + stack_height_change: -3, + name: "LOG1", + }; + pub const LOG2: OpCode = OpCode { + code: 0xa2, + price: 3 * 375, + stack_height_required: 4, + stack_height_change: -4, + name: "LOG2", + }; + pub const LOG3: OpCode = OpCode { + code: 0xa3, + price: 4 * 375, + stack_height_required: 5, + stack_height_change: -5, + name: "LOG3", + }; + pub const LOG4: OpCode = OpCode { + code: 0xa4, + price: 5 * 375, + stack_height_required: 6, + stack_height_change: -6, + name: "LOG4", + }; + pub const LT: OpCode = OpCode { + code: 0x10, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "LT", + }; + pub const MLOAD: OpCode = OpCode { + code: 0x51, + price: 3, + stack_height_required: 1, + stack_height_change: 0, + name: "MLOAD", + }; + pub const MOD: OpCode = OpCode { + code: 0x06, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "MOD", + }; + pub const MSIZE: OpCode = OpCode { + code: 0x59, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "MSIZE", + }; + pub const MSTORE: OpCode = OpCode { + code: 0x52, + price: 3, + stack_height_required: 2, + stack_height_change: -2, + name: "MSTORE", + }; + pub const MSTORE8: OpCode = OpCode { + code: 0x53, + price: 3, + stack_height_required: 2, + stack_height_change: -2, + name: "MSTORE8", + }; + pub const MUL: OpCode = OpCode { + code: 0x02, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "MUL", + }; + pub const MULMOD: OpCode = OpCode { + code: 0x09, + price: 8, + stack_height_required: 3, + stack_height_change: -2, + name: "MULMOD", + }; + pub const NOT: OpCode = OpCode { + code: 0x19, + price: 3, + stack_height_required: 1, + stack_height_change: 0, + name: "NOT", + }; + pub const NUMBER: OpCode = OpCode { + code: 0x43, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "NUMBER", + }; + pub const OR: OpCode = OpCode { + code: 0x17, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "OR", + }; + pub const ORIGIN: OpCode = OpCode { + code: 0x32, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "ORIGIN", + }; + pub const PC: OpCode = OpCode { + code: 0x58, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "PC", + }; + pub const POP: OpCode = OpCode { + code: 0x50, + price: 2, + stack_height_required: 1, + stack_height_change: -1, + name: "POP", + }; + pub const PUSH1: OpCode = OpCode { + code: 0x60, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH1", + }; + pub const PUSH10: OpCode = OpCode { + code: 0x69, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH10", + }; + pub const PUSH11: OpCode = OpCode { + code: 0x6a, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH11", + }; + pub const PUSH12: OpCode = OpCode { + code: 0x6b, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH12", + }; + pub const PUSH13: OpCode = OpCode { + code: 0x6c, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH13", + }; + pub const PUSH14: OpCode = OpCode { + code: 0x6d, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH14", + }; + pub const PUSH15: OpCode = OpCode { + code: 0x6e, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH15", + }; + pub const PUSH16: OpCode = OpCode { + code: 0x6f, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH16", + }; + pub const PUSH17: OpCode = OpCode { + code: 0x70, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH17", + }; + pub const PUSH18: OpCode = OpCode { + code: 0x71, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH18", + }; + pub const PUSH19: OpCode = OpCode { + code: 0x72, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH19", + }; + pub const PUSH2: OpCode = OpCode { + code: 0x61, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH2", + }; + pub const PUSH20: OpCode = OpCode { + code: 0x73, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH20", + }; + pub const PUSH21: OpCode = OpCode { + code: 0x74, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH21", + }; + pub const PUSH22: OpCode = OpCode { + code: 0x75, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH22", + }; + pub const PUSH23: OpCode = OpCode { + code: 0x76, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH23", + }; + pub const PUSH24: OpCode = OpCode { + code: 0x77, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH24", + }; + pub const PUSH25: OpCode = OpCode { + code: 0x78, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH25", + }; + pub const PUSH26: OpCode = OpCode { + code: 0x79, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH26", + }; + pub const PUSH27: OpCode = OpCode { + code: 0x7a, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH27", + }; + pub const PUSH28: OpCode = OpCode { + code: 0x7b, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH28", + }; + pub const PUSH29: OpCode = OpCode { + code: 0x7c, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH29", + }; + pub const PUSH3: OpCode = OpCode { + code: 0x62, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH3", + }; + pub const PUSH30: OpCode = OpCode { + code: 0x7d, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH30", + }; + pub const PUSH31: OpCode = OpCode { + code: 0x7e, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH31", + }; + pub const PUSH32: OpCode = OpCode { + code: 0x7f, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH32", + }; + pub const PUSH4: OpCode = OpCode { + code: 0x63, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH4", + }; + pub const PUSH5: OpCode = OpCode { + code: 0x64, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH5", + }; + pub const PUSH6: OpCode = OpCode { + code: 0x65, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH6", + }; + pub const PUSH7: OpCode = OpCode { + code: 0x66, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH7", + }; + pub const PUSH8: OpCode = OpCode { + code: 0x67, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH8", + }; + pub const PUSH9: OpCode = OpCode { + code: 0x68, + price: 3, + stack_height_required: 0, + stack_height_change: 1, + name: "PUSH9", + }; + pub const RETURN: OpCode = OpCode { + code: 0xf3, + price: 0, + stack_height_required: 2, + stack_height_change: -2, + name: "RETURN", + }; + pub const RETURNDATACOPY: OpCode = OpCode { + code: 0x3e, + price: 3, + stack_height_required: 3, + stack_height_change: -3, + name: "RETURNDATACOPY", + }; + pub const RETURNDATASIZE: OpCode = OpCode { + code: 0x3d, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "RETURNDATASIZE", + }; + pub const REVERT: OpCode = OpCode { + code: 0xfd, + price: 0, + stack_height_required: 2, + stack_height_change: -2, + name: "REVERT", + }; + pub const SAR: OpCode = OpCode { + code: 0x1d, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SAR", + }; + pub const SDIV: OpCode = OpCode { + code: 0x05, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "SDIV", + }; + pub const SELFBALANCE: OpCode = OpCode { + code: 0x47, + price: 5, + stack_height_required: 0, + stack_height_change: 1, + name: "SELFBALANCE", + }; + pub const SELFDESTRUCT: OpCode = OpCode { + code: 0xff, + price: 5000, + stack_height_required: 1, + stack_height_change: -1, + name: "SELFDESTRUCT", + }; + pub const SGT: OpCode = OpCode { + code: 0x13, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SGT", + }; + pub const SHL: OpCode = OpCode { + code: 0x1b, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SHL", + }; + pub const SHR: OpCode = OpCode { + code: 0x1c, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SHR", + }; + pub const SIGNEXTEND: OpCode = OpCode { + code: 0x0b, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "SIGNEXTEND", + }; + pub const SLOAD: OpCode = OpCode { + code: 0x54, + price: WARM_STORAGE_READ_COST, + stack_height_required: 1, + stack_height_change: 0, + name: "SLOAD", + }; + pub const SLT: OpCode = OpCode { + code: 0x12, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SLT", + }; + pub const SMOD: OpCode = OpCode { + code: 0x07, + price: 5, + stack_height_required: 2, + stack_height_change: -1, + name: "SMOD", + }; + pub const SSTORE: OpCode = OpCode { + code: 0x55, + price: 0, + stack_height_required: 2, + stack_height_change: -2, + name: "SSTORE", + }; + pub const STATICCALL: OpCode = OpCode { + code: 0xfa, + price: WARM_STORAGE_READ_COST, + stack_height_required: 6, + stack_height_change: -5, + name: "STATICCALL", + }; + pub const STOP: OpCode = OpCode { + code: 0x00, + price: 0, + stack_height_required: 0, + stack_height_change: 0, + name: "STOP", + }; + pub const SUB: OpCode = OpCode { + code: 0x03, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "SUB", + }; + pub const SWAP1: OpCode = OpCode { + code: 0x90, + price: 3, + stack_height_required: 2, + stack_height_change: 0, + name: "SWAP1", + }; + pub const SWAP10: OpCode = OpCode { + code: 0x99, + price: 3, + stack_height_required: 11, + stack_height_change: 0, + name: "SWAP10", + }; + pub const SWAP11: OpCode = OpCode { + code: 0x9a, + price: 3, + stack_height_required: 12, + stack_height_change: 0, + name: "SWAP11", + }; + pub const SWAP12: OpCode = OpCode { + code: 0x9b, + price: 3, + stack_height_required: 13, + stack_height_change: 0, + name: "SWAP12", + }; + pub const SWAP13: OpCode = OpCode { + code: 0x9c, + price: 3, + stack_height_required: 14, + stack_height_change: 0, + name: "SWAP13", + }; + pub const SWAP14: OpCode = OpCode { + code: 0x9d, + price: 3, + stack_height_required: 15, + stack_height_change: 0, + name: "SWAP14", + }; + pub const SWAP15: OpCode = OpCode { + code: 0x9e, + price: 3, + stack_height_required: 16, + stack_height_change: 0, + name: "SWAP15", + }; + pub const SWAP16: OpCode = OpCode { + code: 0x9f, + price: 3, + stack_height_required: 17, + stack_height_change: 0, + name: "SWAP16", + }; + pub const SWAP2: OpCode = OpCode { + code: 0x91, + price: 3, + stack_height_required: 3, + stack_height_change: 0, + name: "SWAP2", + }; + pub const SWAP3: OpCode = OpCode { + code: 0x92, + price: 3, + stack_height_required: 4, + stack_height_change: 0, + name: "SWAP3", + }; + pub const SWAP4: OpCode = OpCode { + code: 0x93, + price: 3, + stack_height_required: 5, + stack_height_change: 0, + name: "SWAP4", + }; + pub const SWAP5: OpCode = OpCode { + code: 0x94, + price: 3, + stack_height_required: 6, + stack_height_change: 0, + name: "SWAP5", + }; + pub const SWAP6: OpCode = OpCode { + code: 0x95, + price: 3, + stack_height_required: 7, + stack_height_change: 0, + name: "SWAP6", + }; + pub const SWAP7: OpCode = OpCode { + code: 0x96, + price: 3, + stack_height_required: 8, + stack_height_change: 0, + name: "SWAP7", + }; + pub const SWAP8: OpCode = OpCode { + code: 0x97, + price: 3, + stack_height_required: 9, + stack_height_change: 0, + name: "SWAP8", + }; + pub const SWAP9: OpCode = OpCode { + code: 0x98, + price: 3, + stack_height_required: 10, + stack_height_change: 0, + name: "SWAP9", + }; + pub const TIMESTAMP: OpCode = OpCode { + code: 0x42, + price: 2, + stack_height_required: 0, + stack_height_change: 1, + name: "TIMESTAMP", + }; + pub const XOR: OpCode = OpCode { + code: 0x18, + price: 3, + stack_height_required: 2, + stack_height_change: -1, + name: "XOR", + }; +} + +impl std::fmt::Display for OpCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + +impl TryFrom for OpCode { + type Error = StatusCode; + + fn try_from(value: u8) -> Result { + // todo: optimize and turn it into a jump table + const OPCODES: [OpCode; 143] = [ + OpCode::STOP, + OpCode::ADD, + OpCode::MUL, + OpCode::SUB, + OpCode::DIV, + OpCode::SDIV, + OpCode::MOD, + OpCode::SMOD, + OpCode::ADDMOD, + OpCode::MULMOD, + OpCode::EXP, + OpCode::SIGNEXTEND, + OpCode::LT, + OpCode::GT, + OpCode::SLT, + OpCode::SGT, + OpCode::EQ, + OpCode::ISZERO, + OpCode::AND, + OpCode::OR, + OpCode::XOR, + OpCode::NOT, + OpCode::BYTE, + OpCode::SHL, + OpCode::SHR, + OpCode::SAR, + OpCode::KECCAK256, + OpCode::ADDRESS, + OpCode::BALANCE, + OpCode::ORIGIN, + OpCode::CALLER, + OpCode::CALLVALUE, + OpCode::CALLDATALOAD, + OpCode::CALLDATASIZE, + OpCode::CALLDATACOPY, + OpCode::CODESIZE, + OpCode::CODECOPY, + OpCode::GASPRICE, + OpCode::EXTCODESIZE, + OpCode::EXTCODECOPY, + OpCode::RETURNDATASIZE, + OpCode::RETURNDATACOPY, + OpCode::EXTCODEHASH, + OpCode::BLOCKHASH, + OpCode::COINBASE, + OpCode::TIMESTAMP, + OpCode::NUMBER, + OpCode::DIFFICULTY, + OpCode::GASLIMIT, + OpCode::CHAINID, + OpCode::SELFBALANCE, + OpCode::BASEFEE, + OpCode::POP, + OpCode::MLOAD, + OpCode::MSTORE, + OpCode::MSTORE8, + OpCode::SLOAD, + OpCode::SSTORE, + OpCode::JUMP, + OpCode::JUMPI, + OpCode::PC, + OpCode::MSIZE, + OpCode::GAS, + OpCode::JUMPDEST, + OpCode::PUSH1, + OpCode::PUSH2, + OpCode::PUSH3, + OpCode::PUSH4, + OpCode::PUSH5, + OpCode::PUSH6, + OpCode::PUSH7, + OpCode::PUSH8, + OpCode::PUSH9, + OpCode::PUSH10, + OpCode::PUSH11, + OpCode::PUSH12, + OpCode::PUSH13, + OpCode::PUSH14, + OpCode::PUSH15, + OpCode::PUSH16, + OpCode::PUSH17, + OpCode::PUSH18, + OpCode::PUSH19, + OpCode::PUSH20, + OpCode::PUSH21, + OpCode::PUSH22, + OpCode::PUSH23, + OpCode::PUSH24, + OpCode::PUSH25, + OpCode::PUSH26, + OpCode::PUSH27, + OpCode::PUSH28, + OpCode::PUSH29, + OpCode::PUSH30, + OpCode::PUSH31, + OpCode::PUSH32, + OpCode::DUP1, + OpCode::DUP2, + OpCode::DUP3, + OpCode::DUP4, + OpCode::DUP5, + OpCode::DUP6, + OpCode::DUP7, + OpCode::DUP8, + OpCode::DUP9, + OpCode::DUP10, + OpCode::DUP11, + OpCode::DUP12, + OpCode::DUP13, + OpCode::DUP14, + OpCode::DUP15, + OpCode::DUP16, + OpCode::SWAP1, + OpCode::SWAP2, + OpCode::SWAP3, + OpCode::SWAP4, + OpCode::SWAP5, + OpCode::SWAP6, + OpCode::SWAP7, + OpCode::SWAP8, + OpCode::SWAP9, + OpCode::SWAP10, + OpCode::SWAP11, + OpCode::SWAP12, + OpCode::SWAP13, + OpCode::SWAP14, + OpCode::SWAP15, + OpCode::SWAP16, + OpCode::LOG0, + OpCode::LOG1, + OpCode::LOG2, + OpCode::LOG3, + OpCode::LOG4, + OpCode::CREATE, + OpCode::CALL, + OpCode::CALLCODE, + OpCode::RETURN, + OpCode::DELEGATECALL, + OpCode::CREATE2, + OpCode::STATICCALL, + OpCode::REVERT, + OpCode::INVALID, + OpCode::SELFDESTRUCT, + ]; + + for op in OPCODES { + if op == value { + return Ok(op); + } + } + + Err(StatusCode::UndefinedInstruction) + } +} diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs new file mode 100644 index 000000000..eef10235d --- /dev/null +++ b/actors/evm/src/interpreter/output.rs @@ -0,0 +1,114 @@ +use { + bytes::Bytes, + fvm_ipld_encoding::Cbor, + serde::{Deserialize, Serialize}, + std::fmt::Debug, + strum_macros::Display, +}; + +/// Output of EVM execution. +#[derive(Clone, PartialEq)] +pub struct Output { + /// EVM exited with this status code. + pub status_code: StatusCode, + /// Output data returned. + pub output_data: Bytes, + // indicates if revert was requested + pub reverted: bool, +} + +impl Debug for Output { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Output") + .field("status_code", &self.status_code) + .field("output_data", &hex::encode(&self.output_data)) + .field("reverted", &self.reverted) + .finish() + } +} + +/// Message status code. +#[must_use] +#[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)] +pub enum StatusCode { + /// Execution finished with success. + #[strum(serialize = "success")] + Success, + + /// Generic execution failure. + #[strum(serialize = "failure")] + Failure, + + /// Execution terminated with REVERT opcode. + #[strum(serialize = "revert")] + Revert, + + /// The execution has run out of gas. + #[strum(serialize = "out of gas")] + OutOfGas, + + /// The designated INVALID instruction has been hit during execution. + /// + /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) + /// defines the instruction 0xfe as INVALID instruction to indicate execution + /// abortion coming from high-level languages. This status code is reported + /// in case this INVALID instruction has been encountered. + #[strum(serialize = "invalid instruction")] + InvalidInstruction, + + /// An undefined instruction has been encountered. + #[strum(serialize = "undefined instruction")] + UndefinedInstruction, + + /// The execution has attempted to put more items on the EVM stack + /// than the specified limit. + #[strum(serialize = "stack overflow")] + StackOverflow, + + /// Execution of an opcode has required more items on the EVM stack. + #[strum(serialize = "stack underflow")] + StackUnderflow, + + /// Execution has violated the jump destination restrictions. + #[strum(serialize = "bad jump destination")] + BadJumpDestination, + + /// Tried to read outside memory bounds. + /// + /// An example is RETURNDATACOPY reading past the available buffer. + #[strum(serialize = "invalid memory access")] + InvalidMemoryAccess, + + /// Call depth has exceeded the limit (if any) + #[strum(serialize = "call depth exceeded")] + CallDepthExceeded, + + /// Tried to execute an operation which is restricted in static mode. + #[strum(serialize = "static mode violation")] + StaticModeViolation, + + /// A call to a precompiled or system contract has ended with a failure. + /// + /// An example: elliptic curve functions handed invalid EC points. + #[strum(serialize = "precompile failure")] + PrecompileFailure, + + /// Contract validation has failed. + #[strum(serialize = "contract validation failure")] + ContractValidationFailure, + + /// An argument to a state accessing method has a value outside of the + /// accepted range of values. + #[strum(serialize = "argument out of range")] + ArgumentOutOfRange, + + /// The caller does not have enough funds for value transfer. + #[strum(serialize = "insufficient balance")] + InsufficientBalance, + + /// EVM implementation generic internal error. + #[strum(serialize = "internal error")] + InternalError(String), +} + +impl Cbor for StatusCode {} diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs new file mode 100644 index 000000000..0482db106 --- /dev/null +++ b/actors/evm/src/interpreter/stack.rs @@ -0,0 +1,82 @@ +#![allow(dead_code)] + +use {crate::interpreter::U256, arrayvec::ArrayVec, serde::Serialize}; + +/// Ethereum Yellow Paper (9.1) +pub const MAX_STACK_SIZE: usize = 1024; + +/// EVM stack. +#[derive(Clone, Debug, Default, Serialize)] +pub struct Stack(pub ArrayVec); + +impl Stack { + #[inline] + pub const fn new() -> Self { + Self(ArrayVec::new_const()) + } + + #[inline] + const fn get_pos(&self, pos: usize) -> usize { + self.len() - 1 - pos + } + + #[inline] + pub fn get(&self, pos: usize) -> &U256 { + let pos = self.get_pos(pos); + self.0.get(pos).unwrap() + } + + #[inline] + pub fn get_mut(&mut self, pos: usize) -> &mut U256 { + let pos = self.get_pos(pos); + self.0.get_mut(pos).unwrap() + } + + #[inline(always)] + pub const fn len(&self) -> usize { + self.0.len() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn push(&mut self, v: U256) { + self.0.push(v) + } + + #[inline] + pub fn pop(&mut self) -> U256 { + self.0.pop().unwrap() + } + + #[inline] + pub fn swap_top(&mut self, pos: usize) { + let top = self.0.len() - 1; + let pos = self.get_pos(pos); + self.0.swap(top, pos); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn stack() { + let mut stack = Stack::default(); + + let items: [u128; 4] = [0xde, 0xad, 0xbe, 0xef]; + + for (i, item) in items.iter().copied().enumerate() { + stack.push(item.into()); + assert_eq!(stack.len(), i + 1); + } + + assert_eq!(*stack.get(2), U256::from(0xad)); + assert_eq!(stack.pop(), U256::from(0xef)); + assert_eq!(*stack.get(2), U256::from(0xde)); + } +} diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs new file mode 100644 index 000000000..7b0128e53 --- /dev/null +++ b/actors/evm/src/interpreter/system.rs @@ -0,0 +1,91 @@ +#![allow(dead_code)] + +use { + crate::interpreter::{Message, StatusCode, U256}, + cid::Cid, + fil_actors_runtime::{runtime::Runtime, ActorError}, + fvm_ipld_blockstore::Blockstore, + fvm_ipld_hamt::Hamt, + std::cell::RefCell, +}; + +#[derive(Clone, Copy, Debug)] +pub enum StorageStatus { + /// The value of a storage item has been left unchanged: 0 -> 0 and X -> X. + Unchanged, + /// The value of a storage item has been modified: X -> Y. + Modified, + /// A storage item has been modified after being modified before: X -> Y -> Z. + ModifiedAgain, + /// A new storage item has been added: 0 -> X. + Added, + /// A storage item has been deleted: X -> 0. + Deleted, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Call<'a> { + Call(&'a Message), + Create(&'a Message), +} + +/// Platform Abstraction Layer +/// that bridges the FVM world to EVM world +pub struct System<'r, BS: Blockstore, RT: Runtime> { + pub rt: &'r RT, + state: RefCell>, +} + +impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { + pub fn new(rt: &'r RT, state_cid: Cid) -> anyhow::Result { + Ok(Self { rt, state: RefCell::new(Hamt::load(&state_cid, rt.store())?) }) + } + + pub fn flush_state(&self) -> Result { + self.state.borrow_mut().flush().map_err(|e| ActorError::illegal_state(e.to_string())) + } + + /// Get value of a storage key. + pub fn get_storage(&self, key: U256) -> Result, StatusCode> { + let mut key_bytes = [0u8; 32]; + key.to_big_endian(&mut key_bytes); + + Ok(self + .state + .borrow() + .get(&key) + .map_err(|e| StatusCode::InternalError(e.to_string()))? + .cloned()) + } + + /// Set value of a storage key. + pub fn set_storage(&self, key: U256, value: Option) -> Result { + let mut key_bytes = [0u8; 32]; + key.to_big_endian(&mut key_bytes); + + let prev_value = self + .state + .borrow() + .get(&key) + .map_err(|e| StatusCode::InternalError(e.to_string()))? + .cloned(); + + let mut storage_status = + if prev_value == value { StorageStatus::Unchanged } else { StorageStatus::Modified }; + + if value.is_none() { + self.state + .borrow_mut() + .delete(&key) + .map_err(|e| StatusCode::InternalError(e.to_string()))?; + storage_status = StorageStatus::Deleted; + } else { + self.state + .borrow_mut() + .set(key, value.unwrap()) + .map_err(|e| StatusCode::InternalError(e.to_string()))?; + } + + Ok(storage_status) + } +} diff --git a/actors/evm/src/interpreter/transaction.rs b/actors/evm/src/interpreter/transaction.rs new file mode 100644 index 000000000..6bcbb4451 --- /dev/null +++ b/actors/evm/src/interpreter/transaction.rs @@ -0,0 +1,788 @@ +#![allow(dead_code)] + +use { + crate::interpreter::{H160, H256, U256}, + bytes::Bytes, + fil_actors_runtime::ActorError, + fvm_shared::crypto::signature::SECP_PUB_LEN, + rlp::{DecoderError, Rlp, RlpStream}, + sha3::{Digest, Keccak256}, + std::{fmt::Debug, ops::Deref}, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TransactionAction { + Call(H160), + Create, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct AccessListItem { + pub address: H160, + pub slots: Vec, +} + +pub enum Transaction { + Legacy { + chain_id: Option, + nonce: u64, + gas_price: U256, + gas_limit: u64, + action: TransactionAction, + value: U256, + input: Bytes, + }, + EIP2930 { + chain_id: u64, + nonce: u64, + gas_price: U256, + gas_limit: u64, + action: TransactionAction, + value: U256, + input: Bytes, + access_list: Vec, + }, + EIP1559 { + chain_id: u64, + nonce: u64, + max_priority_fee_per_gas: U256, + max_fee_per_gas: U256, + gas_limit: u64, + action: TransactionAction, + value: U256, + input: Bytes, + access_list: Vec, + }, +} + +#[derive(Debug)] +pub struct TransactionRecoveryId(pub u64); + +#[derive(Debug)] +pub struct TransactionSignature { + pub v: TransactionRecoveryId, + pub r: H256, + pub s: H256, +} + +#[derive(Debug)] +pub struct SignedTransaction { + pub transaction: Transaction, + pub signature: TransactionSignature, +} + +impl rlp::Encodable for TransactionAction { + fn rlp_append(&self, s: &mut RlpStream) { + match self { + Self::Call(address) => { + s.encoder().encode_value(&address[..]); + } + Self::Create => s.encoder().encode_value(&[]), + } + } +} + +impl rlp::Decodable for TransactionAction { + fn decode(rlp: &Rlp) -> Result { + if rlp.is_empty() { + if rlp.is_data() { + Ok(TransactionAction::Create) + } else { + Err(DecoderError::RlpExpectedToBeData) + } + } else { + Ok(TransactionAction::Call(rlp.as_val()?)) + } + } +} + +impl rlp::Encodable for AccessListItem { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.address); + s.append_list(&self.slots); + } +} + +impl rlp::Decodable for AccessListItem { + fn decode(rlp: &Rlp) -> Result { + Ok(Self { address: rlp.val_at(0)?, slots: rlp.list_at(1)? }) + } +} + +impl Transaction { + /// Calculates the hash of the transaction fields without the signature. + /// This value is the input to the signing function and the signature + /// is caluculated over this hash and the sender private key. + pub fn hash(&self) -> H256 { + let mut s = RlpStream::new(); + match self { + Transaction::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } => { + if let Some(chain_id) = chain_id { + s.begin_list(9); + s.append(nonce); + s.append(gas_price); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append(chain_id); + s.append(&0_u8); + s.append(&0_u8); + } else { + s.begin_list(6); + s.append(nonce); + s.append(gas_limit); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + } + } + Transaction::EIP2930 { + chain_id, + nonce, + gas_price, + gas_limit, + action, + value, + input, + access_list, + } => { + s.append_raw(&[1u8], 0); + s.begin_list(8); + s.append(chain_id); + s.append(nonce); + s.append(gas_price); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append_list(access_list); + } + Transaction::EIP1559 { + chain_id, + nonce, + max_priority_fee_per_gas, + max_fee_per_gas, + gas_limit, + action, + value, + input, + access_list, + } => { + s.append_raw(&[2u8], 0); + s.begin_list(9); + s.append(chain_id); + s.append(nonce); + s.append(max_priority_fee_per_gas); + s.append(max_fee_per_gas); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append_list(access_list); + } + }; + + H256::from_slice(Keccak256::digest(s.as_raw()).as_slice()) + } + + pub fn nonce(&self) -> u64 { + *match self { + Transaction::Legacy { nonce, .. } => nonce, + Transaction::EIP2930 { nonce, .. } => nonce, + Transaction::EIP1559 { nonce, .. } => nonce, + } + } + + pub fn chain_id(&self) -> Option { + match self { + Transaction::Legacy { chain_id, .. } => *chain_id, + Transaction::EIP2930 { chain_id, .. } => Some(*chain_id), + Transaction::EIP1559 { chain_id, .. } => Some(*chain_id), + } + } + + pub fn gas_price(&self) -> U256 { + *match self { + Transaction::Legacy { gas_price, .. } => gas_price, + Transaction::EIP2930 { gas_price, .. } => gas_price, + Transaction::EIP1559 { max_fee_per_gas, .. } => max_fee_per_gas, + } + } + + pub fn gas_limit(&self) -> u64 { + *match self { + Transaction::Legacy { gas_limit, .. } => gas_limit, + Transaction::EIP2930 { gas_limit, .. } => gas_limit, + Transaction::EIP1559 { gas_limit, .. } => gas_limit, + } + } + + pub fn action(&self) -> TransactionAction { + match self { + Transaction::Legacy { action, .. } => action, + Transaction::EIP2930 { action, .. } => action, + Transaction::EIP1559 { action, .. } => action, + } + .clone() + } + + pub fn input(&self) -> Bytes { + match self { + Transaction::Legacy { input, .. } => input, + Transaction::EIP2930 { input, .. } => input, + Transaction::EIP1559 { input, .. } => input, + } + .clone() + } + + pub fn value(&self) -> U256 { + *match self { + Transaction::Legacy { value, .. } => value, + Transaction::EIP2930 { value, .. } => value, + Transaction::EIP1559 { value, .. } => value, + } + } +} + +impl Deref for TransactionRecoveryId { + type Target = u64; + + fn deref(&self) -> &u64 { + &self.0 + } +} + +impl TransactionRecoveryId { + pub fn odd_y_parity(&self) -> u8 { + if self.0 == 27 || self.0 == 28 || self.0 > 36 { + ((self.0 - 1) % 2) as u8 + } else { + 4 + } + } + + pub fn chain_id(&self) -> Option { + if self.0 > 36 { + Some((self.0 - 35) / 2) + } else { + None + } + } +} + +impl TryFrom<&[u8]> for SignedTransaction { + type Error = DecoderError; + + fn try_from(value: &[u8]) -> Result { + if value.is_empty() { + return Err(DecoderError::RlpIsTooShort); + } + + match value[0] { + 0x01 => parse_eip2930_transaction(value), + 0x02 => parse_eip1559_transaction(value), + _ => parse_legacy_transaction(value), + } + } +} + +impl Deref for SignedTransaction { + type Target = Transaction; + + fn deref(&self) -> &Self::Target { + &self.transaction + } +} + +impl SignedTransaction { + /// Creates RLP serialized representation of the transaction. + /// This value is the input to the hash function that is used + /// to calculate the final transaction hash as it appears on + /// blockchain explorers. This representation can be sent directly + /// to ETH nodes and to the FVM-EVM bridge + pub fn serialize(&self) -> Vec { + let mut s = RlpStream::new(); + match &self.transaction { + Transaction::Legacy { nonce, gas_price, gas_limit, action, value, input, .. } => { + s.begin_list(9); + s.append(nonce); + s.append(gas_price); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append(&self.signature.v.0); + s.append(&self.signature.r); + s.append(&self.signature.s); + } + Transaction::EIP2930 { + chain_id, + nonce, + gas_price, + gas_limit, + action, + value, + input, + access_list, + } => { + s.append_raw(&[1u8], 0); + s.begin_list(11); + s.append(chain_id); + s.append(nonce); + s.append(gas_price); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append_list(access_list); + s.append(&self.signature.v.0); + s.append(&self.signature.r); + s.append(&self.signature.s); + } + Transaction::EIP1559 { + chain_id, + nonce, + max_priority_fee_per_gas, + max_fee_per_gas, + gas_limit, + action, + value, + input, + access_list, + } => { + s.append_raw(&[2u8], 0); + s.begin_list(12); + s.append(chain_id); + s.append(nonce); + s.append(max_priority_fee_per_gas); + s.append(max_fee_per_gas); + s.append(gas_limit); + s.append(action); + s.append(value); + s.append(input); + s.append_list(access_list); + s.append(&self.signature.v.0); + s.append(&self.signature.r); + s.append(&self.signature.s); + } + }; + s.as_raw().to_vec() + } + + pub fn hash(&self) -> H256 { + H256::from_slice(Keccak256::digest(&self.serialize()).as_slice()) + } + + /// The secp256k1 public key of the transaction sender. + /// + /// This public key can used to derive the equivalent Filecoin account + pub fn sender_public_key(&self) -> Result<[u8; SECP_PUB_LEN], ActorError> { + todo!(); + // let mut sig = [0u8; 65]; + // sig[..32].copy_from_slice(self.signature.r.as_bytes()); + // sig[32..64].copy_from_slice(self.signature.s.as_bytes()); + + // if matches!(self.transaction, Transaction::Legacy { .. }) { + // sig[64] = self.signature.v.odd_y_parity(); + // } else { + // sig[64] = self.signature.v.0 as u8; + // } + + // #[cfg(not(test))] // use a syscall to fvm + // return fvm_sdk::crypto::recover_secp_public_key( + // &self.transaction.hash().to_fixed_bytes(), + // &sig, + // ) + // .map_err(|e| { + // ActorError::illegal_argument(format!("failed to recover public key: {e:?}")) + // }); + + // #[cfg(test)] + // // invoke the recovery impl directly as there is not FVM running this code + // return Ok( + // fvm_shared::crypto::signature::ops::recover_secp_public_key( + // &self.transaction.hash().to_fixed_bytes(), + // &sig, + // ) + // .unwrap() + // .serialize(), + // ); + } + + /// Ethereum sender address which is 20-bytes trimmed keccak256(pubkey) + pub fn sender_address(&self) -> Result { + let pubkey = self.sender_public_key()?; + let address_slice = &Keccak256::digest(&pubkey[1..])[12..]; + Ok(H160::from_slice(address_slice)) + } +} + +/// rlp([nonce, gasPrice, gasLimit, to, value, data, init, v, r, s]) +fn parse_legacy_transaction(bytes: &[u8]) -> Result { + let rlp = Rlp::new(bytes); + + if rlp.item_count()? != 9 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let signature = TransactionSignature { + v: TransactionRecoveryId(rlp.val_at(6)?), + r: rlp.val_at(7)?, + s: rlp.val_at(8)?, + }; + + Ok(SignedTransaction { + transaction: Transaction::Legacy { + chain_id: signature.v.chain_id(), + nonce: rlp.val_at(0)?, + gas_price: rlp.val_at(1)?, + gas_limit: rlp.val_at(2)?, + action: rlp.val_at(3)?, + value: rlp.val_at(4)?, + input: rlp.val_at(5)?, + }, + signature, + }) +} + +/// 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, +/// accessList, signatureYParity, signatureR, signatureS]) +fn parse_eip2930_transaction(bytes: &[u8]) -> Result { + let rlp = Rlp::new(&bytes[1..]); + + if rlp.item_count()? != 11 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let signature = TransactionSignature { + v: TransactionRecoveryId(rlp.val_at(8)?), + r: rlp.val_at(9)?, + s: rlp.val_at(10)?, + }; + + Ok(SignedTransaction { + transaction: Transaction::EIP2930 { + chain_id: rlp.val_at(0)?, + nonce: rlp.val_at(1)?, + gas_price: rlp.val_at(2)?, + gas_limit: rlp.val_at(3)?, + action: rlp.val_at(4)?, + value: rlp.val_at(5)?, + input: rlp.val_at(6)?, + access_list: rlp.list_at(7)?, + }, + signature, + }) +} + +/// 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, +/// gas_limit, destination, amount, data, access_list, signature_y_parity, +/// signature_r, signature_s]) +fn parse_eip1559_transaction(bytes: &[u8]) -> Result { + let rlp = Rlp::new(&bytes[1..]); + + if rlp.item_count()? != 12 { + return Err(DecoderError::RlpIncorrectListLen); + } + + Ok(SignedTransaction { + signature: TransactionSignature { + v: TransactionRecoveryId(rlp.val_at(9)?), + r: rlp.val_at(10)?, + s: rlp.val_at(11)?, + }, + transaction: Transaction::EIP1559 { + chain_id: rlp.val_at(0)?, + nonce: rlp.val_at(1)?, + max_priority_fee_per_gas: rlp.val_at(2)?, + max_fee_per_gas: rlp.val_at(3)?, + gas_limit: rlp.val_at(4)?, + action: rlp.val_at(5)?, + value: rlp.val_at(6)?, + input: rlp.val_at(7)?, + access_list: rlp.list_at(8)?, + }, + }) +} + +impl Debug for Transaction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } => f + .debug_struct("Legacy") + .field("chain_id", chain_id) + .field("nonce", nonce) + .field("gas_price", gas_price) + .field("gas_limit", gas_limit) + .field("action", action) + .field("value", value) + .field("input", &hex::encode(&input)) + .finish(), + Self::EIP2930 { + chain_id, + nonce, + gas_price, + gas_limit, + action, + value, + input, + access_list, + } => f + .debug_struct("EIP2930") + .field("chain_id", chain_id) + .field("nonce", nonce) + .field("gas_price", gas_price) + .field("gas_limit", gas_limit) + .field("action", action) + .field("value", value) + .field("input", &hex::encode(&input)) + .field("access_list", access_list) + .finish(), + Self::EIP1559 { + chain_id, + nonce, + max_priority_fee_per_gas, + max_fee_per_gas, + gas_limit, + action, + value, + input, + access_list, + } => f + .debug_struct("EIP1559") + .field("chain_id", chain_id) + .field("nonce", nonce) + .field("max_priority_fee_per_gas", max_priority_fee_per_gas) + .field("max_fee_per_gas", max_fee_per_gas) + .field("gas_limit", gas_limit) + .field("action", action) + .field("value", value) + .field("input", &hex::encode(&input)) + .field("access_list", access_list) + .finish(), + } + } +} + +#[cfg(test)] +mod tests { + use { + crate::interpreter::{ + transaction::{AccessListItem, Transaction, TransactionAction}, + SignedTransaction, H160, H256, U256, + }, + hex_literal::hex, + }; + + #[ignore] + #[test] + fn decode_legacy_transaction() { + // https://etherscan.io/tx/0x3741aea434dc6e9e740be0113af4bac372fcdd2fa2188409c93c9405cbdcaaf0 + let raw = hex!( + "f9016b0885113abe69b38302895c947a250d5630b4cf539739df2c5dacb4c659f2488d80b90 + 1044a25d94a00000000000000000000000000000000000000000000000022b1c8c1227a0000 + 000000000000000000000000000000000000000000000003f0a59430f92a924400000000000 + 000000000000000000000000000000000000000000000000000a00000000000000000000000 + 0012021043bbaab3b71b2217655787a13d24cf618b000000000000000000000000000000000 + 00000000000000000000000603c6a1e00000000000000000000000000000000000000000000 + 00000000000000000002000000000000000000000000fe9a29ab92522d14fc65880d8172142 + 61d8479ae000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc225 + a01df6c364ee7d2b684bbb6e3892fee69a1bc4fc487222b003ea57ec1596884916a01e1643f + de193fde5e6be4ae0b2d4c4669560132a6dc87b6404d5c0cdc743fee6 + " + ); + + let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); + + // test sender recovery + assert_eq!( + H160::from_slice(&hex!("12021043bbaab3b71b2217655787a13d24cf618b")), + transaction.sender_address().unwrap() + ); + + // test transaction hash computation: + assert_eq!( + H256::from_slice(&hex!( + "3741aea434dc6e9e740be0113af4bac372fcdd2fa2188409c93c9405cbdcaaf0" + )), + transaction.hash() + ); + + // test decoded fields + if let Transaction::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } = + transaction.transaction + { + assert_eq!(Some(1), chain_id); + assert_eq!(8, nonce); + assert_eq!(U256::from(74000001459u64), gas_price); + assert_eq!(166236, gas_limit); + assert_eq!(U256::zero(), value); + + assert_eq!( + TransactionAction::Call(H160::from_slice(&hex!( + "7a250d5630b4cf539739df2c5dacb4c659f2488d" + ))), + action + ); + + assert_eq!( + &hex!( + "4a25d94a00000000000000000000000000000000000000000000000022b1c8c1227a + 0000000000000000000000000000000000000000000000000003f0a59430f92a9244 + 00000000000000000000000000000000000000000000000000000000000000a00000 + 0000000000000000000012021043bbaab3b71b2217655787a13d24cf618b00000000 + 000000000000000000000000000000000000000000000000603c6a1e000000000000 + 00000000000000000000000000000000000000000000000000020000000000000000 + 00000000fe9a29ab92522d14fc65880d817214261d8479ae00000000000000000000 + 0000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ) as &[u8], + &input + ); + } else { + panic!("decoded into wrong transaction type"); + } + } + + #[ignore] + #[test] + fn decode_eip2930_transaction() { + // https://etherscan.io/tx/0xfbf20efe99271206c0f5b497a92bee2e66f8bf9991e07648935194f17610b36e + let raw = hex!( + "01f8bb01808522ecb25c008307a120942a48420d75777af4c99970c0ed3c25effd1c08b + e80843ccfd60bf84ff794fbfed54d426217bf75d2ce86622c1e5faf16b0a6e1a00000000 + 000000000000000000000000000000000000000000000000000000000d694d9db270c1b5 + e3bd161e8c8503c55ceabee709552c080a03057d1077af1fc48bdfe2a8eac03caf686145 + b52342e77ad6982566fe39e0691a00507044aa767a50dc926d0daa4dd616b1e5a8d2e578 + 1df5bc9feeee5a5139d61" + ); + + let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); + + // test if the right transaction type was detected + assert!(matches!(transaction.transaction, Transaction::EIP2930 { .. })); + + // test transaction hash computation: + assert_eq!( + H256::from_slice(&hex!( + "fbf20efe99271206c0f5b497a92bee2e66f8bf9991e07648935194f17610b36e" + )), + transaction.hash() + ); + + // test sender recovery + assert_eq!( + H160::from_slice(&hex!("4e2b6cc39e22026d8ce21214646a657ab7eb92b3")), + transaction.sender_address().unwrap() + ); + + if let Transaction::EIP2930 { + chain_id, + nonce, + gas_price, + gas_limit, + action, + value, + input, + access_list, + } = transaction.transaction + { + assert_eq!(1, chain_id); + assert_eq!(0, nonce); + assert_eq!(U256::from(150000000000u64), gas_price); + assert_eq!(500000, gas_limit); + assert_eq!(U256::zero(), value); + assert_eq!( + TransactionAction::Call(H160::from_slice(&hex!( + "2a48420d75777af4c99970c0ed3c25effd1c08be" + ))), + action + ); + assert_eq!(&hex!("3ccfd60b") as &[u8], &input); + assert_eq!( + vec![ + AccessListItem { + address: H160::from_slice(&hex!( + "fbfed54d426217bf75d2ce86622c1e5faf16b0a6" + )), + slots: vec![H256::from_slice(&hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + ))] + }, + AccessListItem { + address: H160::from_slice(&hex!( + "d9db270c1b5e3bd161e8c8503c55ceabee709552" + )), + slots: vec![] + } + ], + access_list + ) + } else { + panic!("decoded into wrong transaction type"); + } + } + + #[ignore] + #[test] + fn decode_eip1559_transaction() { + // https://etherscan.io/tx/0x734678f719001015c5b5f5cbac6a9210ede7ee6ce63e746ff2e9eecda3ab68c7 + let raw = hex!( + "02f8720104843b9aca008504eb6480bc82520894f76c5b19e86c256 + 482f4aad1dae620a0c3ac0cd68717699d954d540080c080a05a5206a8e0486b8e101bcf + 4ed5b290df24a4d54f1ca752c859fa19c291244b98a0177166d96fd69db70628d99855b + 400c8a149b2254c211a0a00645830f5338218" + ); + + let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); + + // test if the right transaction type was detected + assert!(matches!(transaction.transaction, Transaction::EIP1559 { .. })); + + // test transaction hash computation: + assert_eq!( + H256::from_slice(&hex!( + "734678f719001015c5b5f5cbac6a9210ede7ee6ce63e746ff2e9eecda3ab68c7" + )), + transaction.hash() + ); + + // test sender recovery + assert_eq!( + H160::from_slice(&hex!("d882fab949fe224befd0e85afcc5f13d67980102")), + transaction.sender_address().unwrap() + ); + + if let Transaction::EIP1559 { + chain_id, + nonce, + max_priority_fee_per_gas, + max_fee_per_gas, + gas_limit, + action, + value, + input, + access_list, + } = transaction.transaction + { + assert_eq!(1, chain_id); + assert_eq!(4, nonce); + assert_eq!(21000, gas_limit); + assert_eq!(U256::from(6590050000000000u64), value); + assert_eq!(U256::from(1000000000), max_priority_fee_per_gas); + assert_eq!(U256::from(21129101500u64), max_fee_per_gas); + assert_eq!(&[] as &[u8], &input); + assert_eq!(Vec::::new(), access_list); + assert_eq!( + TransactionAction::Call(H160::from_slice(&hex!( + "f76c5b19e86c256482f4aad1dae620a0c3ac0cd6" + ))), + action + ); + } else { + panic!("decoded into wrong transaction type"); + } + } +} diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs new file mode 100644 index 000000000..607df7a6e --- /dev/null +++ b/actors/evm/src/interpreter/uints.rs @@ -0,0 +1,295 @@ +#![allow(dead_code)] +// 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)] + +use { + fixed_hash::construct_fixed_hash, + fvm_shared::econ::TokenAmount, + impl_serde::{impl_fixed_hash_serde, impl_uint_serde}, + std::cmp::Ordering, + uint::construct_uint, +}; + +construct_uint! { pub struct U256(4); } // ethereum word size +construct_uint! { pub struct U512(8); } // used for addmod and mulmod opcodes + +construct_fixed_hash! { pub struct H160(20); } // ethereum address +construct_fixed_hash! { pub struct H256(32); } // Keccak256 + +impl From<&TokenAmount> for U256 { + fn from(amount: &TokenAmount) -> U256 { + let (_, bytes) = amount.to_bytes_be(); + U256::from(bytes.as_slice()) + } +} + +// make ETH uints serde serializable, +// so it can work with Hamt and other +// IPLD structures seamlessly +impl_uint_serde!(U256, 4); +impl_uint_serde!(U512, 8); +impl_fixed_hash_serde!(H160, 20); +impl_fixed_hash_serde!(H256, 32); + +macro_rules! impl_hamt_hash { + ($type:ident) => { + impl fvm_ipld_hamt::Hash for $type { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } + } + }; +} + +fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { + let v = v.as_ref(); + &v[v.iter().take_while(|&&b| b == 0).count()..] +} + +macro_rules! impl_rlp_codec_hash { + ($type:ident) => { + impl rlp::Encodable for $type { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + let bytes = self.as_fixed_bytes(); + s.encoder().encode_value(&bytes[..]); + } + } + impl rlp::Decodable for $type { + fn decode(rlp: &rlp::Rlp) -> Result { + rlp.decoder().decode_value(|bytes| Ok($type::from_slice(bytes))) + } + } + }; +} + +macro_rules! impl_rlp_codec_uint { + ($type:ident, $bytes_len: expr) => { + impl rlp::Encodable for $type { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + let mut bytes = [0u8; $bytes_len]; + self.to_big_endian(&mut bytes); + let zbytes = zeroless_view(&bytes); + s.encoder().encode_value(&zbytes); + } + } + impl rlp::Decodable for $type { + fn decode(rlp: &rlp::Rlp) -> Result { + rlp + .decoder() + .decode_value(|bytes| Ok($type::from_big_endian(bytes))) + } + } + }; +} + +// Hamt support +impl_hamt_hash!(H160); +impl_hamt_hash!(H256); + +impl_hamt_hash!(U256); +impl_hamt_hash!(U512); + +// RLP Support +impl_rlp_codec_hash!(H160); +impl_rlp_codec_hash!(H256); +impl_rlp_codec_uint!(U256, 32); +impl_rlp_codec_uint!(U512, 64); + +#[inline(always)] +pub fn u256_high(val: U256) -> u128 { + let mut bytes = [0u8; 32]; + val.to_big_endian(&mut bytes); + u128::from_be_bytes(bytes[0..16].try_into().unwrap()) +} + +#[inline(always)] +pub fn u256_low(val: U256) -> u128 { + let mut bytes = [0u8; 32]; + val.to_big_endian(&mut bytes); + u128::from_be_bytes(bytes[16..32].try_into().unwrap()) +} + +#[inline(always)] +pub fn u128_words_to_u256(high: u128, low: u128) -> U256 { + let high = high.to_be_bytes(); + let low = low.to_be_bytes(); + let bytes = high.into_iter().chain(low.into_iter()).collect::>(); + U256::from_big_endian(&bytes) +} + +#[inline] +fn _u256_to_address(v: U256) -> H160 { + let mut bytes = [0u8; 32]; + v.to_big_endian(&mut bytes); + H160::from_slice(&bytes) +} + +#[inline] +pub fn address_to_u256(v: H160) -> U256 { + U256::from_big_endian(v.as_bytes()) +} + +const SIGN_BITMASK_U128: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; +const FLIPH_BITMASK_U128: u128 = 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Sign { + Plus, + Minus, + Zero, +} + +#[inline] +pub fn log2floor(value: U256) -> u64 { + debug_assert!(value != U256::zero()); + let mut l: u64 = 256; + for v in [u256_high(value), u256_low(value)] { + if v == 0 { + l -= 128; + } else { + l -= v.leading_zeros() as u64; + if l == 0 { + return l; + } else { + return l - 1; + } + } + } + l +} + +#[inline(always)] +pub fn two_compl(op: U256) -> U256 { + !op + U256::from(1) +} + +#[inline(always)] +fn two_compl_mut(op: &mut U256) { + *op = two_compl(*op); +} + +#[inline(always)] +pub fn i256_sign(val: &mut U256) -> Sign { + if u256_high(*val) & SIGN_BITMASK_U128 == 0 { + if *val == U256::zero() { + Sign::Zero + } else { + Sign::Plus + } + } else { + if DO_TWO_COMPL { + two_compl_mut(val); + } + Sign::Minus + } +} + +#[inline(always)] +pub fn i256_div(mut first: U256, mut second: U256) -> U256 { + let min_negative_value: U256 = u128_words_to_u256(SIGN_BITMASK_U128, 0); + let second_sign = i256_sign::(&mut second); + if second_sign == Sign::Zero { + return U256::zero(); + } + let first_sign = i256_sign::(&mut first); + if first_sign == Sign::Minus && first == min_negative_value && second == U256::from(1) { + return two_compl(min_negative_value); + } + + let mut d = first / second; + + u256_remove_sign(&mut d); + + if d == U256::zero() { + return U256::zero(); + } + + match (first_sign, second_sign) { + (Sign::Zero, Sign::Plus) + | (Sign::Plus, Sign::Zero) + | (Sign::Zero, Sign::Zero) + | (Sign::Plus, Sign::Plus) + | (Sign::Minus, Sign::Minus) => d, + (Sign::Zero, Sign::Minus) + | (Sign::Plus, Sign::Minus) + | (Sign::Minus, Sign::Zero) + | (Sign::Minus, Sign::Plus) => two_compl(d), + } +} + +#[inline(always)] +pub fn i256_mod(mut first: U256, mut second: U256) -> U256 { + let first_sign = i256_sign::(&mut first); + if first_sign == Sign::Zero { + return U256::zero(); + } + + let _ = i256_sign::(&mut second); + let mut r = first % second; + u256_remove_sign(&mut r); + if r == U256::zero() { + return U256::zero(); + } + if first_sign == Sign::Minus { + two_compl(r) + } else { + r + } +} + +#[inline(always)] +pub fn i256_cmp(mut first: U256, mut second: U256) -> Ordering { + let first_sign = i256_sign::(&mut first); + let second_sign = i256_sign::(&mut second); + match (first_sign, second_sign) { + (Sign::Zero, Sign::Zero) => Ordering::Equal, + (Sign::Zero, Sign::Plus) => Ordering::Less, + (Sign::Zero, Sign::Minus) => Ordering::Greater, + (Sign::Minus, Sign::Zero) => Ordering::Less, + (Sign::Minus, Sign::Plus) => Ordering::Less, + (Sign::Minus, Sign::Minus) => first.cmp(&second), + (Sign::Plus, Sign::Minus) => Ordering::Greater, + (Sign::Plus, Sign::Zero) => Ordering::Greater, + (Sign::Plus, Sign::Plus) => first.cmp(&second), + } +} + +#[inline(always)] +fn u256_remove_sign(val: &mut U256) { + let low = u256_low(*val); + let mut high = u256_high(*val); + high &= FLIPH_BITMASK_U128; + *val = u128_words_to_u256(high, low) +} + +#[cfg(test)] +mod tests { + use {super::*, core::num::Wrapping}; + + #[test] + fn div_i256() { + let min_negative_value: U256 = u128_words_to_u256(SIGN_BITMASK_U128, 0); + + assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN)); + assert_eq!(i8::MAX / -1, -i8::MAX); + + let one = U256::from(1); + let one_hundred = U256::from(100); + let fifty = U256::from(50); + let _fifty_sign = Sign::Plus; + let two = U256::from(2); + let neg_one_hundred = U256::from(100); + let _neg_one_hundred_sign = Sign::Minus; + 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!(i256_div(min_negative_value, minus_one), min_negative_value); + assert_eq!(i256_div(min_negative_value, one), min_negative_value); + assert_eq!(i256_div(max_value, one), max_value); + assert_eq!(i256_div(max_value, minus_one), neg_max_value); + assert_eq!(i256_div(one_hundred, minus_one), neg_one_hundred); + assert_eq!(i256_div(one_hundred, two), fifty); + } +} diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs new file mode 100644 index 000000000..7d9b1a656 --- /dev/null +++ b/actors/evm/src/lib.rs @@ -0,0 +1,190 @@ +pub mod interpreter; +mod state; + +use { + crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, + crate::state::State, + bytes::Bytes, + fil_actors_runtime::{ + actor_error, cbor, + runtime::{ActorCode, Runtime}, + ActorDowncast, ActorError, + }, + fvm_ipld_blockstore::Blockstore, + fvm_ipld_encoding::tuple::*, + fvm_ipld_encoding::RawBytes, + fvm_ipld_hamt::Hamt, + fvm_shared::error::*, + fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}, + num_derive::FromPrimitive, + num_traits::FromPrimitive, +}; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(EvmContractActor); + +/// Maximum allowed EVM bytecode size. +/// The contract code size limit is 24kB. +const MAX_CODE_SIZE: usize = 24 << 10; + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + InvokeContract = 2, +} + +pub struct EvmContractActor; +impl EvmContractActor { + pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> + where + BS: Blockstore, + RT: Runtime, + { + rt.validate_immediate_caller_accept_any()?; + + if params.bytecode.len() > MAX_CODE_SIZE { + return Err(ActorError::illegal_argument(format!( + "EVM byte code length ({}) is exceeding the maximum allowed of {MAX_CODE_SIZE}", + params.bytecode.len() + ))); + } + + if params.bytecode.is_empty() { + return Err(ActorError::illegal_argument("no bytecode provided".into())); + } + + // initialize contract state + let init_contract_state_cid = + Hamt::<_, U256, U256>::new(rt.store()).flush().map_err(|e| { + ActorError::unspecified(format!("failed to flush contract state: {e:?}")) + })?; + // create an instance of the platform abstraction layer -- note: do we even need this? + let system = System::new(rt, init_contract_state_cid).map_err(|e| { + ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) + })?; + // create a new execution context + let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + // identify bytecode valid jump destinations + let bytecode = Bytecode::new(¶ms.bytecode) + .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; + // invoke the contract constructor + let exec_status = execute(&bytecode, &mut exec_state, &system) + .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; + + if !exec_status.reverted + && exec_status.status_code == StatusCode::Success + && !exec_status.output_data.is_empty() + { + // constructor ran to completion successfully and returned + // the resulting bytecode. + let contract_bytecode = exec_status.output_data; + + let contract_state_cid = system.flush_state()?; + + let state = State::new( + rt.store(), + RawBytes::new(contract_bytecode.to_vec()), + contract_state_cid, + ) + .map_err(|e| { + e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") + })?; + rt.create(&state)?; + + Ok(()) + } else { + Err(ActorError::unspecified("EVM constructor failed".to_string())) + } + } + + pub fn invoke_contract( + rt: &mut RT, + params: InvokeParams, + ) -> Result + where + BS: Blockstore, + RT: Runtime, + { + rt.validate_immediate_caller_accept_any()?; + + // TODO this is fine in a transaction for now, as we don't have yet cross-contact calls + // some refactoring will be needed when we start making cross contract calls. + rt.transaction(|state: &mut State, rt| { + let bytecode: Vec = + match rt.store().get(&state.bytecode).map_err(|e| { + ActorError::unspecified(format!("failed to parse bytecode: {e:?}")) + })? { + Some(bytes) => bytes, + None => return Err(ActorError::unspecified("missing bytecode".to_string())), + }; + + let bytecode = Bytecode::new(&bytecode) + .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; + + let system = System::new(rt, state.contract_state).map_err(|e| { + ActorError::unspecified(format!( + "failed to create execution abstraction layer: {e:?}" + )) + })?; + + let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + + let exec_status = execute(&bytecode, &mut exec_state, &system) + .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; + + // XXX is this correct handling of reverts? or should we fail execution? + if exec_status.status_code == StatusCode::Success { + let result = RawBytes::serialize(U256::from_big_endian(&exec_status.output_data)) + .map_err(|e| { + ActorError::unspecified(format!("failed to serialize return data: {e:?}")) + })?; + + if !exec_status.reverted { + state.contract_state = system.flush_state()?; + } + + Ok(result) + } else { + Err(ActorError::unspecified(format!( + "EVM contract invocation failed: status: {}", + exec_status.status_code + ))) + } + }) + } +} + +impl ActorCode for EvmContractActor { + fn invoke_method( + rt: &mut RT, + method: MethodNum, + params: &RawBytes, + ) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + match FromPrimitive::from_u64(method) { + Some(Method::Constructor) => { + Self::constructor(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + Some(Method::InvokeContract) => { + Self::invoke_contract(rt, cbor::deserialize_params(params)?) + } + None => Err(actor_error!(unhandled_message; "Invalid method")), + } + } +} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct ConstructorParams { + pub bytecode: RawBytes, + pub input_data: RawBytes, +} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct InvokeParams { + pub input_data: RawBytes, +} diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs new file mode 100644 index 000000000..992bc81ad --- /dev/null +++ b/actors/evm/src/state.rs @@ -0,0 +1,39 @@ +use { + cid::Cid, + fvm_ipld_blockstore::Block, + fvm_ipld_blockstore::Blockstore, + fvm_ipld_encoding::tuple::*, + fvm_ipld_encoding::{Cbor, RawBytes}, + multihash::Code, + serde_tuple::{Deserialize_tuple, Serialize_tuple}, +}; + +pub const RAW: u64 = 0x55; + +/// 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 state dictionary. + /// All eth contract state is a map of U256 -> U256 values. + /// + /// HAMT + pub contract_state: Cid, +} + +impl Cbor for State {} + +impl State { + pub fn new( + store: &BS, + bytecode: RawBytes, + contract_state: Cid, + ) -> anyhow::Result { + let bytecode_cid = store.put(Code::Blake2b256, &Block::new(RAW, bytecode.to_vec()))?; + Ok(Self { bytecode: bytecode_cid, contract_state }) + } +} diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs new file mode 100644 index 000000000..c79dd5494 --- /dev/null +++ b/actors/evm/tests/basic.rs @@ -0,0 +1,50 @@ +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; + +#[test] +fn basic_contract_construction_and_invocation() { + let mut rt = MockRuntime::default(); + + let contract = Address::new_id(100); + + let params = evm::ConstructorParams { + bytecode: hex::decode(include_str!("simplecoin.hex")).unwrap().into(), + input_data: RawBytes::default(), + }; + + // invoke constructor + rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); + rt.expect_validate_caller_any(); + + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // invoke contract -- getBalance + 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[31] = 100; + solidity_params.append(&mut arg0); + + let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + assert_eq!(RawBytes::deserialize::(&result).unwrap(), U256::from(10000)); +} diff --git a/actors/evm/tests/simplecoin.hex b/actors/evm/tests/simplecoin.hex new file mode 100644 index 000000000..7cdca3db7 --- /dev/null +++ b/actors/evm/tests/simplecoin.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220d6b7446ff7b783a98dd1d68f516949438f2d578a05c05722e283b81a7774246564736f6c634300080e0033 \ No newline at end of file diff --git a/actors/evm/tests/simplecoin.sol b/actors/evm/tests/simplecoin.sol new file mode 100644 index 000000000..6ea41c76d --- /dev/null +++ b/actors/evm/tests/simplecoin.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.25 <= 0.8.15; + +contract SimpleCoin { + mapping (address => uint) balances; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + + constructor() { + balances[tx.origin] = 10000; + } + + function sendCoin(address receiver, uint 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(uint){ + return getBalance(addr) * 2; + } + + function getBalance(address addr) public view returns(uint) { + return balances[addr]; + } +} diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 196bdf0ae..3a012c0c8 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index e97c9d0f4..86a9df263 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_bitfield = "0.5.2" num-traits = "0.2.14" num-derive = "0.3.3" @@ -40,4 +40,3 @@ itertools = "0.10" [features] fil-actor = [] - diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 7c6f32b8f..1c9be4ae6 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_hamt = "0.5.1" @@ -42,4 +42,3 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] multihash = { version = "0.16.1", default-features = false } [features] fil-actor = [] - diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 40b47e092..2a32d5e3e 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" @@ -31,4 +31,3 @@ fvm_ipld_encoding = "0.2.2" fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = [] - diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 3825737ce..db9cbcfb3 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -30,4 +30,3 @@ fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } derive_builder = "0.10.2" [features] fil-actor = [] - diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 3d806f417..cecf9506d 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" @@ -34,4 +34,3 @@ fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector fil_actor_reward = { path = "../reward" } [features] fil-actor = [] - diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index a432671fc..c90863c67 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 8eb1735ed..31b96c861 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" @@ -28,4 +28,3 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = [] - diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 26335cc60..020634e4b 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" @@ -30,4 +30,3 @@ fvm_ipld_encoding = "0.2.2" fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = [] - diff --git a/build.rs b/build.rs index 80dd9d872..1b02c1eb7 100644 --- a/build.rs +++ b/build.rs @@ -23,6 +23,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("paych", "paymentchannel"), ("reward", "reward"), ("verifreg", "verifiedregistry"), + ("evm", "evm"), ]; const WASM_FEATURES: &[&str] = &["+bulk-memory", "+crt-static"]; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index e14c3d0ec..6d5777985 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -18,7 +18,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" fvm_ipld_hamt = "0.5.1" fvm_sdk = { version = "2.0.0-alpha.1", optional = true } -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } getrandom = { version = "0.2.5", features = ["js"] } hex = { version = "0.4.3", optional = true } indexmap = { version = "1.8.0", features = ["serde-1"] } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index fc7654bc2..bb9f4ea50 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -52,6 +52,7 @@ lazy_static! { pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/multisig"); pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward"); pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry"); + pub static ref EVM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/evm"); pub static ref ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System); @@ -65,6 +66,7 @@ lazy_static! { map.insert(*MULTISIG_ACTOR_CODE_ID, Type::Multisig); map.insert(*REWARD_ACTOR_CODE_ID, Type::Reward); map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); + map.insert(*EVM_ACTOR_CODE_ID, Type::EVM); map }; pub static ref ACTOR_CODES: BTreeMap = [ diff --git a/state/Cargo.toml b/state/Cargo.toml index 60cc0621b..ca721b0aa 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" @@ -38,4 +38,3 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] [dev-dependencies] [features] fil-actor = [] - diff --git a/state/src/check.rs b/state/src/check.rs index 29b642ec8..411a71518 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -202,6 +202,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( acc.with_prefix("verifreg: ").add_all(&msgs); verifreg_summary = Some(summary); } + Some(Type::EVM) => {} None => { bail!("unexpected actor code CID {} for address {}", actor.code, key); } diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 97524ccb3..dfbb39a9a 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -24,8 +24,9 @@ fil_actor_power = { version = "10.0.0-alpha.1", path = "../actors/power" } fil_actor_market = { version = "10.0.0-alpha.1", path = "../actors/market" } 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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "0.8.0", default-features = false } +fvm_shared = { version = "0.9.0", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index c43325421..a6e21c639 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -3,6 +3,7 @@ use cid::multihash::Code; 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_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, State as MinerState}; @@ -659,6 +660,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { Type::Power => PowerActor::invoke_method(self, self.msg.method, ¶ms), Type::PaymentChannel => PaychActor::invoke_method(self, self.msg.method, ¶ms), Type::VerifiedRegistry => VerifregActor::invoke_method(self, self.msg.method, ¶ms), + Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, ¶ms), }; if res.is_err() { self.v.rollback(prior_root) From 0dc90b0a722f8cc6eaabe1fafdfde08ede4864f8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 19 Aug 2022 16:59:31 +0300 Subject: [PATCH 006/339] Integration origin from runtime context (#562) --- Cargo.lock | 45 ++++++++++++------- actors/evm/src/interpreter/execution.rs | 4 +- .../src/interpreter/instructions/context.rs | 18 ++++---- actors/evm/tests/basic.rs | 3 +- runtime/src/runtime/fvm.rs | 4 ++ runtime/src/runtime/mod.rs | 3 ++ runtime/src/test_utils.rs | 9 ++++ test_vm/src/lib.rs | 3 ++ 8 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bb3de25a..2508f2204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" +checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" [[package]] name = "arrayref" @@ -86,10 +86,11 @@ dependencies = [ [[package]] name = "async-io" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "0ab006897723d9352f63e2b13047177c3982d8d79709d713ce7747a8f19fd1b0" dependencies = [ + "autocfg", "concurrent-queue", "futures-lite", "libc", @@ -274,9 +275,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "byteorder" @@ -405,9 +406,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "1079fb8528d9f9c888b1e8aa651e6e079ade467323d58f75faf1d30b1808f540" dependencies = [ "libc", ] @@ -593,9 +594,9 @@ dependencies = [ [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "event-listener" @@ -1080,7 +1081,7 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "ahash", "anyhow", @@ -1096,7 +1097,7 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1108,7 +1109,7 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "anyhow", "cid", @@ -1133,7 +1134,7 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "anyhow", "cid", @@ -1150,7 +1151,7 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "anyhow", "byteorder", @@ -1170,7 +1171,7 @@ dependencies = [ [[package]] name = "fvm_sdk" version = "2.0.0-alpha.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1184,7 +1185,7 @@ dependencies = [ [[package]] name = "fvm_shared" version = "0.9.0" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#4033ef252296209269fa5478e28d934d98bd884a" +source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" dependencies = [ "anyhow", "bimap", @@ -1497,6 +1498,7 @@ dependencies = [ "core2", "digest 0.10.3", "multihash-derive", + "ripemd", "serde", "serde-big-array", "sha2 0.10.2", @@ -1770,6 +1772,15 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "ripemd" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1facec54cb5e0dc08553501fa740091086d0259ad0067e0d4103448e4cb22ed3" +dependencies = [ + "digest 0.10.3", +] + [[package]] name = "rlp" version = "0.5.1" diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index c19a23c2c..d86e0b4d2 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -79,9 +79,9 @@ pub fn execute<'r, BS: Blockstore, RT: Runtime>( OpCode::SHR => bitwise::shr(&mut runtime.stack), OpCode::SAR => bitwise::sar(&mut runtime.stack), OpCode::KECCAK256 => hash::keccak256(runtime)?, - OpCode::ADDRESS => context::address(runtime, system)?, + OpCode::ADDRESS => context::address(runtime, system), OpCode::BALANCE => storage::balance(runtime, system)?, - OpCode::CALLER => context::caller(runtime, system)?, + OpCode::CALLER => context::caller(runtime, system), OpCode::CALLVALUE => context::call_value(runtime, system), OpCode::CALLDATALOAD => call::calldataload(runtime), OpCode::CALLDATASIZE => call::calldatasize(runtime), diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index a0c5d91fd..8a7bb64f8 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -14,10 +14,10 @@ pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn caller<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.message().caller().id().unwrap())) } #[inline] @@ -30,10 +30,10 @@ pub fn call_value<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn address<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.message().receiver().id().unwrap())) } #[inline] @@ -41,7 +41,7 @@ pub fn origin<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.message().caller().id().unwrap())) + state.stack.push(U256::from(platform.rt.message().origin().id().unwrap())) } #[inline] diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index c79dd5494..b13cb8d79 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -18,6 +18,7 @@ fn basic_contract_construction_and_invocation() { // invoke constructor rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); rt.expect_validate_caller_any(); + rt.set_origin(contract); let result = rt .call::( @@ -33,7 +34,7 @@ fn basic_contract_construction_and_invocation() { solidity_params.append(&mut hex::decode("f8b2cb4f").unwrap()); // function selector // caller id address in U256 form let mut arg0 = vec![0u8; 32]; - //arg0[31] = 100; + arg0[31] = 100; // the contract address solidity_params.append(&mut arg0); let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 71c290b86..1c57f593e 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -85,6 +85,10 @@ impl MessageInfo for FvmMessage { Address::new_id(fvm::message::caller()) } + fn origin(&self) -> Address { + Address::new_id(fvm::message::origin()) + } + fn receiver(&self) -> Address { Address::new_id(fvm::message::receiver()) } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 6bc25d61d..f94edf5f2 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -169,6 +169,9 @@ pub trait MessageInfo { /// The address of the immediate calling actor. Always an ID-address. fn caller(&self) -> Address; + /// The address of the origin of the current invocation. Always an ID-address + fn origin(&self) -> Address; + /// The address of the actor receiving the message. Always an ID-address. fn receiver(&self) -> Address; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index bb9f4ea50..64a3d66c8 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -113,6 +113,7 @@ pub struct MockRuntime { pub receiver: Address, pub caller: Address, pub caller_type: Cid, + pub origin: Address, pub value_received: TokenAmount, pub hash_func: Box [u8; 32]>, pub network_version: NetworkVersion, @@ -258,6 +259,7 @@ impl Default for MockRuntime { receiver: Address::new_id(0), caller: Address::new_id(0), caller_type: Default::default(), + origin: Address::new_id(0), value_received: Default::default(), hash_func: Box::new(blake2b_256), network_version: NetworkVersion::V0, @@ -423,6 +425,10 @@ impl MockRuntime { self.actor_code_cids.insert(address, code_id); } + pub fn set_origin(&mut self, address: Address) { + self.origin = address; + } + pub fn set_address_actor_type(&mut self, address: Address, actor_type: Cid) { self.actor_code_cids.insert(address, actor_type); } @@ -658,6 +664,9 @@ impl MessageInfo for MockRuntime { fn caller(&self) -> Address { self.caller } + fn origin(&self) -> Address { + self.origin + } fn receiver(&self) -> Address { self.receiver } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index a6e21c639..c581e0464 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -520,6 +520,9 @@ impl MessageInfo for InvocationCtx<'_, '_> { fn caller(&self) -> Address { self.msg.from } + fn origin(&self) -> Address { + self.resolve_address(&self.top.originator_stable_addr).unwrap() + } fn receiver(&self) -> Address { self.to() } From 3f8e6f11bef000f334b47cc4ebe087a9f6a28b40 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 21 Aug 2022 18:35:29 +0300 Subject: [PATCH 007/339] EVM: don't encode contract invocation output data (#565) --- actors/evm/src/lib.rs | 5 +---- actors/evm/tests/basic.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 7d9b1a656..94f4f6a81 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -135,10 +135,7 @@ impl EvmContractActor { // XXX is this correct handling of reverts? or should we fail execution? if exec_status.status_code == StatusCode::Success { - let result = RawBytes::serialize(U256::from_big_endian(&exec_status.output_data)) - .map_err(|e| { - ActorError::unspecified(format!("failed to serialize return data: {e:?}")) - })?; + let result = RawBytes::from(exec_status.output_data.to_vec()); if !exec_status.reverted { state.contract_state = system.flush_state()?; diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index b13cb8d79..efe213b96 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -30,11 +30,13 @@ fn basic_contract_construction_and_invocation() { rt.verify(); // 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]; - arg0[31] = 100; // the contract address solidity_params.append(&mut arg0); let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; @@ -47,5 +49,25 @@ fn basic_contract_construction_and_invocation() { ) .unwrap(); - assert_eq!(RawBytes::deserialize::(&result).unwrap(), U256::from(10000)); + 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[31] = 100; // the owner address + solidity_params.append(&mut arg0); + + let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + assert_eq!(U256::from_big_endian(&result), U256::from(10000)); } From b1ca8a1570c5320130399344b9badc7e23f8a74e Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 1 Aug 2022 21:47:09 +0200 Subject: [PATCH 008/339] ci: change CI trigger to pull_request + push to master (#510) This change makes CI trigger whenever: - a pull request is opened/updated - a commit is pushed to `master` branch The main advantage of this setup is that it enables CI runs on PRs originating from forks. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fd84c2ac..1e30fc480 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ name: Continuous integration on: push: + branches: + - master + pull_request: env: RUSTFLAGS: -Dwarnings From fc1dfb90ff392995ec0d178f3bde709a442720e8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 22 Aug 2022 08:25:48 -0700 Subject: [PATCH 009/339] ci: run CI on next (#569) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e30fc480..8c80417ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - next pull_request: env: From 2d2122f1895f423517544694a46087afc1390dbe Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 23 Aug 2022 17:23:07 +0300 Subject: [PATCH 010/339] EVM: cleanup opcode definition and resolution (#572) cleanup opcode definition and resolution --- actors/evm/src/interpreter/opcode.rs | 1706 +++++++++----------------- 1 file changed, 546 insertions(+), 1160 deletions(-) diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs index 6427e89bb..724d2f312 100644 --- a/actors/evm/src/interpreter/opcode.rs +++ b/actors/evm/src/interpreter/opcode.rs @@ -11,10 +11,6 @@ pub struct OpCode { /// the byte representing the opcode in binary pub code: u8, - /// cost of executing the opcode, subtracted from the - /// total gas limit when running bytecode. - pub price: u16, - /// The number of stack items the instruction accesses during execution. pub stack_height_required: u8, @@ -24,6 +20,9 @@ pub struct OpCode { /// Human readable name of the opcode. pub name: &'static str, + + /// Reserved/Undefined opcode indicator + pub reserved: bool, } impl From for u8 { @@ -38,1014 +37,548 @@ impl PartialEq for OpCode { } } -const _COLD_SLOAD_COST: u16 = 2100; -const _COLD_ACCOUNT_ACCESS_COST: u16 = 2600; -const WARM_STORAGE_READ_COST: u16 = 100; - -impl OpCode { - pub const ADD: OpCode = OpCode { - code: 0x01, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "ADD", - }; - pub const ADDMOD: OpCode = OpCode { - code: 0x08, - price: 8, - stack_height_required: 3, - stack_height_change: -2, - name: "ADDMOD", - }; - pub const ADDRESS: OpCode = OpCode { - code: 0x30, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "ADDRESS", - }; - pub const AND: OpCode = OpCode { - code: 0x16, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "AND", - }; - pub const BALANCE: OpCode = OpCode { - code: 0x31, - price: WARM_STORAGE_READ_COST, - stack_height_required: 1, - stack_height_change: 0, - name: "BALANCE", - }; - pub const BASEFEE: OpCode = OpCode { - code: 0x48, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "BASEFEE", - }; - pub const BLOCKHASH: OpCode = OpCode { - code: 0x40, - price: 20, - stack_height_required: 1, - stack_height_change: 0, - name: "BLOCKHASH", - }; - pub const BYTE: OpCode = OpCode { - code: 0x1a, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "BYTE", - }; - pub const CALL: OpCode = OpCode { - code: 0xf1, - price: WARM_STORAGE_READ_COST, - stack_height_required: 7, - stack_height_change: -6, - name: "CALL", - }; - pub const CALLCODE: OpCode = OpCode { - code: 0xf2, - price: WARM_STORAGE_READ_COST, - stack_height_required: 7, - stack_height_change: -6, - name: "CALLCODE", - }; - pub const CALLDATACOPY: OpCode = OpCode { - code: 0x37, - price: 3, - stack_height_required: 3, - stack_height_change: -3, - name: "CALLDATACOPY", - }; - pub const CALLDATALOAD: OpCode = OpCode { - code: 0x35, - price: 3, - stack_height_required: 1, - stack_height_change: 0, - name: "CALLDATALOAD", - }; - pub const CALLDATASIZE: OpCode = OpCode { - code: 0x36, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "CALLDATASIZE", - }; - pub const CALLER: OpCode = OpCode { - code: 0x33, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "CALLER", - }; - pub const CALLVALUE: OpCode = OpCode { - code: 0x34, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "CALLVALUE", - }; - pub const CHAINID: OpCode = OpCode { - code: 0x46, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "CHAINID", - }; - pub const CODECOPY: OpCode = OpCode { - code: 0x39, - price: 3, - stack_height_required: 3, - stack_height_change: -3, - name: "CODECOPY", - }; - pub const CODESIZE: OpCode = OpCode { - code: 0x38, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "CODESIZE", - }; - pub const COINBASE: OpCode = OpCode { - code: 0x41, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "COINBASE", - }; - pub const CREATE: OpCode = OpCode { - code: 0xf0, - price: 32000, - stack_height_required: 3, - stack_height_change: -2, - name: "CREATE", - }; - pub const CREATE2: OpCode = OpCode { - code: 0xf5, - price: 32000, - stack_height_required: 4, - stack_height_change: -3, - name: "CREATE2", - }; - pub const DELEGATECALL: OpCode = OpCode { - code: 0xf4, - price: WARM_STORAGE_READ_COST, - stack_height_required: 6, - stack_height_change: -5, - name: "DELEGATECALL", - }; - pub const DIFFICULTY: OpCode = OpCode { - code: 0x44, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "DIFFICULTY", - }; - pub const DIV: OpCode = OpCode { - code: 0x04, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "DIV", - }; - pub const DUP1: OpCode = OpCode { - code: 0x80, - price: 3, - stack_height_required: 1, - stack_height_change: 1, - name: "DUP1", - }; - pub const DUP10: OpCode = OpCode { - code: 0x89, - price: 3, - stack_height_required: 10, - stack_height_change: 1, - name: "DUP10", - }; - pub const DUP11: OpCode = OpCode { - code: 0x8a, - price: 3, - stack_height_required: 11, - stack_height_change: 1, - name: "DUP11", - }; - pub const DUP12: OpCode = OpCode { - code: 0x8b, - price: 3, - stack_height_required: 12, - stack_height_change: 1, - name: "DUP12", - }; - pub const DUP13: OpCode = OpCode { - code: 0x8c, - price: 3, - stack_height_required: 13, - stack_height_change: 1, - name: "DUP13", - }; - pub const DUP14: OpCode = OpCode { - code: 0x8d, - price: 3, - stack_height_required: 14, - stack_height_change: 1, - name: "DUP14", - }; - pub const DUP15: OpCode = OpCode { - code: 0x8e, - price: 3, - stack_height_required: 15, - stack_height_change: 1, - name: "DUP15", - }; - pub const DUP16: OpCode = OpCode { - code: 0x8f, - price: 3, - stack_height_required: 16, - stack_height_change: 1, - name: "DUP16", - }; - pub const DUP2: OpCode = OpCode { - code: 0x81, - price: 3, - stack_height_required: 2, - stack_height_change: 1, - name: "DUP2", - }; - pub const DUP3: OpCode = OpCode { - code: 0x82, - price: 3, - stack_height_required: 3, - stack_height_change: 1, - name: "DUP3", - }; - pub const DUP4: OpCode = OpCode { - code: 0x83, - price: 3, - stack_height_required: 4, - stack_height_change: 1, - name: "DUP4", - }; - pub const DUP5: OpCode = OpCode { - code: 0x84, - price: 3, - stack_height_required: 5, - stack_height_change: 1, - name: "DUP5", - }; - pub const DUP6: OpCode = OpCode { - code: 0x85, - price: 3, - stack_height_required: 6, - stack_height_change: 1, - name: "DUP6", - }; - pub const DUP7: OpCode = OpCode { - code: 0x86, - price: 3, - stack_height_required: 7, - stack_height_change: 1, - name: "DUP7", - }; - pub const DUP8: OpCode = OpCode { - code: 0x87, - price: 3, - stack_height_required: 8, - stack_height_change: 1, - name: "DUP8", - }; - pub const DUP9: OpCode = OpCode { - code: 0x88, - price: 3, - stack_height_required: 9, - stack_height_change: 1, - name: "DUP9", - }; - pub const EQ: OpCode = OpCode { - code: 0x14, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "EQ", - }; - pub const EXP: OpCode = OpCode { - code: 0x0a, - price: 10, - stack_height_required: 2, - stack_height_change: -1, - name: "EXP", - }; - pub const EXTCODECOPY: OpCode = OpCode { - code: 0x3c, - price: WARM_STORAGE_READ_COST, - stack_height_required: 4, - stack_height_change: -4, - name: "EXTCODECOPY", - }; - pub const EXTCODEHASH: OpCode = OpCode { - code: 0x3f, - price: WARM_STORAGE_READ_COST, - stack_height_required: 1, - stack_height_change: 0, - name: "EXTCODEHASH", - }; - pub const EXTCODESIZE: OpCode = OpCode { - code: 0x3b, - price: WARM_STORAGE_READ_COST, - stack_height_required: 1, - stack_height_change: 0, - name: "EXTCODESIZE", - }; - pub const GAS: OpCode = OpCode { - code: 0x5a, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "GAS", - }; - pub const GASLIMIT: OpCode = OpCode { - code: 0x45, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "GASLIMIT", - }; - pub const GASPRICE: OpCode = OpCode { - code: 0x3a, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "GASPRICE", - }; - pub const GT: OpCode = OpCode { - code: 0x11, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "GT", - }; - pub const INVALID: OpCode = OpCode { - code: 0xfe, - price: 0, - stack_height_required: 0, - stack_height_change: 0, - name: "INVALID", - }; - pub const ISZERO: OpCode = OpCode { - code: 0x15, - price: 3, - stack_height_required: 1, - stack_height_change: 0, - name: "ISZERO", - }; - pub const JUMP: OpCode = OpCode { - code: 0x56, - price: 8, - stack_height_required: 1, - stack_height_change: -1, - name: "JUMP", - }; - pub const JUMPDEST: OpCode = OpCode { - code: 0x5b, - price: 1, - stack_height_required: 0, - stack_height_change: 0, - name: "JUMPDEST", - }; - pub const JUMPI: OpCode = OpCode { - code: 0x57, - price: 10, - stack_height_required: 2, - stack_height_change: -2, - name: "JUMPI", - }; - pub const KECCAK256: OpCode = OpCode { - code: 0x20, - price: 30, - stack_height_required: 2, - stack_height_change: -1, - name: "KECCAK256", - }; - pub const LOG0: OpCode = OpCode { - code: 0xa0, - price: 375, - stack_height_required: 2, - stack_height_change: -2, - name: "LOG0", - }; - pub const LOG1: OpCode = OpCode { - code: 0xa1, - price: 2 * 375, - stack_height_required: 3, - stack_height_change: -3, - name: "LOG1", - }; - pub const LOG2: OpCode = OpCode { - code: 0xa2, - price: 3 * 375, - stack_height_required: 4, - stack_height_change: -4, - name: "LOG2", - }; - pub const LOG3: OpCode = OpCode { - code: 0xa3, - price: 4 * 375, - stack_height_required: 5, - stack_height_change: -5, - name: "LOG3", - }; - pub const LOG4: OpCode = OpCode { - code: 0xa4, - price: 5 * 375, - stack_height_required: 6, - stack_height_change: -6, - name: "LOG4", - }; - pub const LT: OpCode = OpCode { - code: 0x10, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "LT", - }; - pub const MLOAD: OpCode = OpCode { - code: 0x51, - price: 3, - stack_height_required: 1, - stack_height_change: 0, - name: "MLOAD", - }; - pub const MOD: OpCode = OpCode { - code: 0x06, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "MOD", - }; - pub const MSIZE: OpCode = OpCode { - code: 0x59, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "MSIZE", - }; - pub const MSTORE: OpCode = OpCode { - code: 0x52, - price: 3, - stack_height_required: 2, - stack_height_change: -2, - name: "MSTORE", - }; - pub const MSTORE8: OpCode = OpCode { - code: 0x53, - price: 3, - stack_height_required: 2, - stack_height_change: -2, - name: "MSTORE8", - }; - pub const MUL: OpCode = OpCode { - code: 0x02, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "MUL", - }; - pub const MULMOD: OpCode = OpCode { - code: 0x09, - price: 8, - stack_height_required: 3, - stack_height_change: -2, - name: "MULMOD", - }; - pub const NOT: OpCode = OpCode { - code: 0x19, - price: 3, - stack_height_required: 1, - stack_height_change: 0, - name: "NOT", - }; - pub const NUMBER: OpCode = OpCode { - code: 0x43, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "NUMBER", - }; - pub const OR: OpCode = OpCode { - code: 0x17, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "OR", - }; - pub const ORIGIN: OpCode = OpCode { - code: 0x32, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "ORIGIN", - }; - pub const PC: OpCode = OpCode { - code: 0x58, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "PC", +macro_rules! def_opcode { + ($id:ident, $code:literal, $sk_required:literal, $sk_change:literal) => { + pub const $id: OpCode = OpCode { + code: $code, + stack_height_required: $sk_required, + stack_height_change: $sk_change, + name: stringify!($id), + reserved: false, + }; }; - pub const POP: OpCode = OpCode { - code: 0x50, - price: 2, - stack_height_required: 1, - stack_height_change: -1, - name: "POP", - }; - pub const PUSH1: OpCode = OpCode { - code: 0x60, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH1", - }; - pub const PUSH10: OpCode = OpCode { - code: 0x69, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH10", - }; - pub const PUSH11: OpCode = OpCode { - code: 0x6a, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH11", - }; - pub const PUSH12: OpCode = OpCode { - code: 0x6b, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH12", - }; - pub const PUSH13: OpCode = OpCode { - code: 0x6c, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH13", - }; - pub const PUSH14: OpCode = OpCode { - code: 0x6d, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH14", - }; - pub const PUSH15: OpCode = OpCode { - code: 0x6e, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH15", - }; - pub const PUSH16: OpCode = OpCode { - code: 0x6f, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH16", - }; - pub const PUSH17: OpCode = OpCode { - code: 0x70, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH17", - }; - pub const PUSH18: OpCode = OpCode { - code: 0x71, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH18", - }; - pub const PUSH19: OpCode = OpCode { - code: 0x72, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH19", - }; - pub const PUSH2: OpCode = OpCode { - code: 0x61, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH2", - }; - pub const PUSH20: OpCode = OpCode { - code: 0x73, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH20", - }; - pub const PUSH21: OpCode = OpCode { - code: 0x74, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH21", - }; - pub const PUSH22: OpCode = OpCode { - code: 0x75, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH22", - }; - pub const PUSH23: OpCode = OpCode { - code: 0x76, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH23", - }; - pub const PUSH24: OpCode = OpCode { - code: 0x77, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH24", - }; - pub const PUSH25: OpCode = OpCode { - code: 0x78, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH25", - }; - pub const PUSH26: OpCode = OpCode { - code: 0x79, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH26", - }; - pub const PUSH27: OpCode = OpCode { - code: 0x7a, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH27", - }; - pub const PUSH28: OpCode = OpCode { - code: 0x7b, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH28", - }; - pub const PUSH29: OpCode = OpCode { - code: 0x7c, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH29", - }; - pub const PUSH3: OpCode = OpCode { - code: 0x62, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH3", - }; - pub const PUSH30: OpCode = OpCode { - code: 0x7d, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH30", - }; - pub const PUSH31: OpCode = OpCode { - code: 0x7e, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH31", - }; - pub const PUSH32: OpCode = OpCode { - code: 0x7f, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH32", - }; - pub const PUSH4: OpCode = OpCode { - code: 0x63, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH4", - }; - pub const PUSH5: OpCode = OpCode { - code: 0x64, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH5", - }; - pub const PUSH6: OpCode = OpCode { - code: 0x65, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH6", - }; - pub const PUSH7: OpCode = OpCode { - code: 0x66, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH7", - }; - pub const PUSH8: OpCode = OpCode { - code: 0x67, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH8", - }; - pub const PUSH9: OpCode = OpCode { - code: 0x68, - price: 3, - stack_height_required: 0, - stack_height_change: 1, - name: "PUSH9", - }; - pub const RETURN: OpCode = OpCode { - code: 0xf3, - price: 0, - stack_height_required: 2, - stack_height_change: -2, - name: "RETURN", - }; - pub const RETURNDATACOPY: OpCode = OpCode { - code: 0x3e, - price: 3, - stack_height_required: 3, - stack_height_change: -3, - name: "RETURNDATACOPY", - }; - pub const RETURNDATASIZE: OpCode = OpCode { - code: 0x3d, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "RETURNDATASIZE", - }; - pub const REVERT: OpCode = OpCode { - code: 0xfd, - price: 0, - stack_height_required: 2, - stack_height_change: -2, - name: "REVERT", - }; - pub const SAR: OpCode = OpCode { - code: 0x1d, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SAR", - }; - pub const SDIV: OpCode = OpCode { - code: 0x05, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "SDIV", - }; - pub const SELFBALANCE: OpCode = OpCode { - code: 0x47, - price: 5, - stack_height_required: 0, - stack_height_change: 1, - name: "SELFBALANCE", - }; - pub const SELFDESTRUCT: OpCode = OpCode { - code: 0xff, - price: 5000, - stack_height_required: 1, - stack_height_change: -1, - name: "SELFDESTRUCT", - }; - pub const SGT: OpCode = OpCode { - code: 0x13, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SGT", - }; - pub const SHL: OpCode = OpCode { - code: 0x1b, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SHL", - }; - pub const SHR: OpCode = OpCode { - code: 0x1c, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SHR", - }; - pub const SIGNEXTEND: OpCode = OpCode { - code: 0x0b, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "SIGNEXTEND", - }; - pub const SLOAD: OpCode = OpCode { - code: 0x54, - price: WARM_STORAGE_READ_COST, - stack_height_required: 1, - stack_height_change: 0, - name: "SLOAD", - }; - pub const SLT: OpCode = OpCode { - code: 0x12, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SLT", - }; - pub const SMOD: OpCode = OpCode { - code: 0x07, - price: 5, - stack_height_required: 2, - stack_height_change: -1, - name: "SMOD", - }; - pub const SSTORE: OpCode = OpCode { - code: 0x55, - price: 0, - stack_height_required: 2, - stack_height_change: -2, - name: "SSTORE", - }; - pub const STATICCALL: OpCode = OpCode { - code: 0xfa, - price: WARM_STORAGE_READ_COST, - stack_height_required: 6, - stack_height_change: -5, - name: "STATICCALL", - }; - pub const STOP: OpCode = OpCode { - code: 0x00, - price: 0, - stack_height_required: 0, - stack_height_change: 0, - name: "STOP", - }; - pub const SUB: OpCode = OpCode { - code: 0x03, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "SUB", - }; - pub const SWAP1: OpCode = OpCode { - code: 0x90, - price: 3, - stack_height_required: 2, - stack_height_change: 0, - name: "SWAP1", - }; - pub const SWAP10: OpCode = OpCode { - code: 0x99, - price: 3, - stack_height_required: 11, - stack_height_change: 0, - name: "SWAP10", - }; - pub const SWAP11: OpCode = OpCode { - code: 0x9a, - price: 3, - stack_height_required: 12, - stack_height_change: 0, - name: "SWAP11", - }; - pub const SWAP12: OpCode = OpCode { - code: 0x9b, - price: 3, - stack_height_required: 13, - stack_height_change: 0, - name: "SWAP12", - }; - pub const SWAP13: OpCode = OpCode { - code: 0x9c, - price: 3, - stack_height_required: 14, - stack_height_change: 0, - name: "SWAP13", - }; - pub const SWAP14: OpCode = OpCode { - code: 0x9d, - price: 3, - stack_height_required: 15, - stack_height_change: 0, - name: "SWAP14", - }; - pub const SWAP15: OpCode = OpCode { - code: 0x9e, - price: 3, - stack_height_required: 16, - stack_height_change: 0, - name: "SWAP15", - }; - pub const SWAP16: OpCode = OpCode { - code: 0x9f, - price: 3, - stack_height_required: 17, - stack_height_change: 0, - name: "SWAP16", - }; - pub const SWAP2: OpCode = OpCode { - code: 0x91, - price: 3, - stack_height_required: 3, - stack_height_change: 0, - name: "SWAP2", - }; - pub const SWAP3: OpCode = OpCode { - code: 0x92, - price: 3, - stack_height_required: 4, - stack_height_change: 0, - name: "SWAP3", - }; - pub const SWAP4: OpCode = OpCode { - code: 0x93, - price: 3, - stack_height_required: 5, - stack_height_change: 0, - name: "SWAP4", - }; - pub const SWAP5: OpCode = OpCode { - code: 0x94, - price: 3, - stack_height_required: 6, - stack_height_change: 0, - name: "SWAP5", - }; - pub const SWAP6: OpCode = OpCode { - code: 0x95, - price: 3, - stack_height_required: 7, - stack_height_change: 0, - name: "SWAP6", - }; - pub const SWAP7: OpCode = OpCode { - code: 0x96, - price: 3, - stack_height_required: 8, - stack_height_change: 0, - name: "SWAP7", - }; - pub const SWAP8: OpCode = OpCode { - code: 0x97, - price: 3, - stack_height_required: 9, - stack_height_change: 0, - name: "SWAP8", - }; - pub const SWAP9: OpCode = OpCode { - code: 0x98, - price: 3, - stack_height_required: 10, - stack_height_change: 0, - name: "SWAP9", - }; - pub const TIMESTAMP: OpCode = OpCode { - code: 0x42, - price: 2, - stack_height_required: 0, - stack_height_change: 1, - name: "TIMESTAMP", - }; - pub const XOR: OpCode = OpCode { - code: 0x18, - price: 3, - stack_height_required: 2, - stack_height_change: -1, - name: "XOR", +} + +macro_rules! def_reserved { + ($id:ident, $code:literal) => { + pub const $id: OpCode = OpCode { + code: $code, + stack_height_required: 0, + stack_height_change: 0, + name: "RESERVED", + reserved: true, + }; }; } +impl OpCode { + def_opcode!(STOP, 0x00, 0, 0); + def_opcode!(ADD, 0x01, 2, -1); + def_opcode!(MUL, 0x02, 2, -1); + def_opcode!(SUB, 0x03, 2, -1); + def_opcode!(DIV, 0x04, 2, -1); + def_opcode!(SDIV, 0x05, 2, -1); + def_opcode!(MOD, 0x06, 2, -1); + def_opcode!(SMOD, 0x07, 2, -1); + def_opcode!(ADDMOD, 0x08, 3, -2); + def_opcode!(MULMOD, 0x09, 3, -2); + def_opcode!(EXP, 0x0a, 2, -1); + def_opcode!(SIGNEXTEND, 0x0b, 2, -1); + def_reserved!(RESERVED_0C, 0x0c); + def_reserved!(RESERVED_0D, 0x0d); + def_reserved!(RESERVED_0E, 0x0e); + def_reserved!(RESERVED_0F, 0x0f); + def_opcode!(LT, 0x10, 2, -1); + def_opcode!(GT, 0x11, 2, -1); + def_opcode!(SLT, 0x12, 2, -1); + def_opcode!(SGT, 0x13, 2, -1); + def_opcode!(EQ, 0x14, 2, -1); + def_opcode!(ISZERO, 0x15, 1, 0); + def_opcode!(AND, 0x16, 2, -1); + def_opcode!(OR, 0x17, 2, -1); + def_opcode!(XOR, 0x18, 2, -1); + def_opcode!(NOT, 0x19, 1, 0); + def_opcode!(BYTE, 0x1a, 2, -1); + def_opcode!(SHL, 0x1b, 2, -1); + def_opcode!(SHR, 0x1c, 2, -1); + def_opcode!(SAR, 0x1d, 2, -1); + def_reserved!(RESERVED_1E, 0x1e); + def_reserved!(RESERVED_1F, 0x1f); + def_opcode!(KECCAK256, 0x20, 2, -1); // SHA3 + def_reserved!(RESERVED_21, 0x21); + def_reserved!(RESERVED_22, 0x22); + def_reserved!(RESERVED_23, 0x23); + def_reserved!(RESERVED_24, 0x24); + def_reserved!(RESERVED_25, 0x25); + def_reserved!(RESERVED_26, 0x26); + def_reserved!(RESERVED_27, 0x27); + def_reserved!(RESERVED_28, 0x28); + def_reserved!(RESERVED_29, 0x29); + def_reserved!(RESERVED_2A, 0x2a); + def_reserved!(RESERVED_2B, 0x2b); + def_reserved!(RESERVED_2C, 0x2c); + def_reserved!(RESERVED_2D, 0x2d); + def_reserved!(RESERVED_2E, 0x2e); + def_reserved!(RESERVED_2F, 0x2f); + def_opcode!(ADDRESS, 0x30, 0, 1); + def_opcode!(BALANCE, 0x31, 1, 0); + def_opcode!(ORIGIN, 0x32, 0, 1); + def_opcode!(CALLER, 0x33, 0, 1); + def_opcode!(CALLVALUE, 0x34, 0, 1); + def_opcode!(CALLDATALOAD, 0x35, 1, 0); + def_opcode!(CALLDATASIZE, 0x36, 0, 1); + def_opcode!(CALLDATACOPY, 0x37, 3, -3); + def_opcode!(CODESIZE, 0x38, 0, 1); + def_opcode!(CODECOPY, 0x39, 3, -3); + def_opcode!(GASPRICE, 0x3a, 0, 1); + def_opcode!(EXTCODESIZE, 0x3b, 1, 0); + def_opcode!(EXTCODECOPY, 0x3c, 4, -4); + def_opcode!(RETURNDATASIZE, 0x3d, 0, 1); + def_opcode!(RETURNDATACOPY, 0x3e, 3, -3); + def_opcode!(EXTCODEHASH, 0x3f, 1, 0); + def_opcode!(BLOCKHASH, 0x40, 1, 0); + def_opcode!(COINBASE, 0x41, 0, 1); + def_opcode!(TIMESTAMP, 0x42, 0, 1); + def_opcode!(NUMBER, 0x43, 0, 1); + def_opcode!(DIFFICULTY, 0x44, 0, 1); + def_opcode!(GASLIMIT, 0x45, 0, 1); + def_opcode!(CHAINID, 0x46, 0, 1); + def_opcode!(SELFBALANCE, 0x47, 0, 1); + def_opcode!(BASEFEE, 0x48, 0, 1); + def_reserved!(RESERVED_49, 0x49); + def_reserved!(RESERVED_4A, 0x4a); + def_reserved!(RESERVED_4B, 0x4b); + def_reserved!(RESERVED_4C, 0x4c); + def_reserved!(RESERVED_4D, 0x4d); + def_reserved!(RESERVED_4E, 0x4e); + def_reserved!(RESERVED_4F, 0x4f); + def_opcode!(POP, 0x50, 1, -1); + def_opcode!(MLOAD, 0x51, 1, 0); + def_opcode!(MSTORE, 0x52, 2, -2); + def_opcode!(MSTORE8, 0x53, 2, -2); + def_opcode!(SLOAD, 0x54, 1, 0); + def_opcode!(SSTORE, 0x55, 2, -2); + def_opcode!(JUMP, 0x56, 1, -1); + def_opcode!(JUMPI, 0x57, 2, -2); + def_opcode!(PC, 0x58, 0, 1); + def_opcode!(MSIZE, 0x59, 0, 1); + def_opcode!(GAS, 0x5a, 0, 1); + def_opcode!(JUMPDEST, 0x5b, 0, 0); + def_reserved!(RESERVED_5C, 0x5c); + def_reserved!(RESERVED_5D, 0x5d); + def_reserved!(RESERVED_5E, 0x5e); + def_reserved!(RESERVED_5F, 0x5f); + def_opcode!(PUSH1, 0x60, 0, 1); + def_opcode!(PUSH2, 0x61, 0, 1); + def_opcode!(PUSH3, 0x62, 0, 1); + def_opcode!(PUSH4, 0x63, 0, 1); + def_opcode!(PUSH5, 0x64, 0, 1); + def_opcode!(PUSH6, 0x65, 0, 1); + def_opcode!(PUSH7, 0x66, 0, 1); + def_opcode!(PUSH8, 0x67, 0, 1); + def_opcode!(PUSH9, 0x68, 0, 1); + def_opcode!(PUSH10, 0x69, 0, 1); + def_opcode!(PUSH11, 0x6a, 0, 1); + def_opcode!(PUSH12, 0x6b, 0, 1); + def_opcode!(PUSH13, 0x6c, 0, 1); + def_opcode!(PUSH14, 0x6d, 0, 1); + def_opcode!(PUSH15, 0x6e, 0, 1); + def_opcode!(PUSH16, 0x6f, 0, 1); + def_opcode!(PUSH17, 0x70, 0, 1); + def_opcode!(PUSH18, 0x71, 0, 1); + def_opcode!(PUSH19, 0x72, 0, 1); + def_opcode!(PUSH20, 0x73, 0, 1); + def_opcode!(PUSH21, 0x74, 0, 1); + def_opcode!(PUSH22, 0x75, 0, 1); + def_opcode!(PUSH23, 0x76, 0, 1); + def_opcode!(PUSH24, 0x77, 0, 1); + def_opcode!(PUSH25, 0x78, 0, 1); + def_opcode!(PUSH26, 0x79, 0, 1); + def_opcode!(PUSH27, 0x7a, 0, 1); + def_opcode!(PUSH28, 0x7b, 0, 1); + def_opcode!(PUSH29, 0x7c, 0, 1); + def_opcode!(PUSH30, 0x7d, 0, 1); + def_opcode!(PUSH31, 0x7e, 0, 1); + def_opcode!(PUSH32, 0x7f, 0, 1); + def_opcode!(DUP1, 0x80, 1, 1); + def_opcode!(DUP2, 0x81, 2, 1); + def_opcode!(DUP3, 0x82, 3, 1); + def_opcode!(DUP4, 0x83, 4, 1); + def_opcode!(DUP5, 0x84, 5, 1); + def_opcode!(DUP6, 0x85, 6, 1); + def_opcode!(DUP7, 0x86, 7, 1); + def_opcode!(DUP8, 0x87, 8, 1); + def_opcode!(DUP9, 0x88, 9, 1); + def_opcode!(DUP10, 0x89, 10, 1); + def_opcode!(DUP11, 0x8a, 11, 1); + def_opcode!(DUP12, 0x8b, 12, 1); + def_opcode!(DUP13, 0x8c, 13, 1); + def_opcode!(DUP14, 0x8d, 14, 1); + def_opcode!(DUP15, 0x8e, 15, 1); + def_opcode!(DUP16, 0x8f, 16, 1); + def_opcode!(SWAP1, 0x90, 2, 0); + def_opcode!(SWAP2, 0x91, 3, 0); + def_opcode!(SWAP3, 0x92, 4, 0); + def_opcode!(SWAP4, 0x93, 5, 0); + def_opcode!(SWAP5, 0x94, 6, 0); + def_opcode!(SWAP6, 0x95, 7, 0); + def_opcode!(SWAP7, 0x96, 8, 0); + def_opcode!(SWAP8, 0x97, 9, 0); + def_opcode!(SWAP9, 0x98, 10, 0); + def_opcode!(SWAP10, 0x99, 11, 0); + def_opcode!(SWAP11, 0x9a, 12, 0); + def_opcode!(SWAP12, 0x9b, 13, 0); + def_opcode!(SWAP13, 0x9c, 14, 0); + def_opcode!(SWAP14, 0x9d, 15, 0); + def_opcode!(SWAP15, 0x9e, 16, 0); + def_opcode!(SWAP16, 0x9f, 17, 0); + def_opcode!(LOG0, 0xa0, 2, -2); + def_opcode!(LOG1, 0xa1, 3, -3); + def_opcode!(LOG2, 0xa2, 4, -4); + def_opcode!(LOG3, 0xa3, 5, -5); + def_opcode!(LOG4, 0xa4, 6, -6); + def_reserved!(RESERVED_A5, 0xa5); + def_reserved!(RESERVED_A6, 0xa6); + def_reserved!(RESERVED_A7, 0xa7); + def_reserved!(RESERVED_A8, 0xa8); + def_reserved!(RESERVED_A9, 0xa9); + def_reserved!(RESERVED_AA, 0xaa); + def_reserved!(RESERVED_AB, 0xab); + def_reserved!(RESERVED_AC, 0xac); + def_reserved!(RESERVED_AD, 0xad); + def_reserved!(RESERVED_AE, 0xae); + def_reserved!(RESERVED_AF, 0xaf); + def_reserved!(RESERVED_B0, 0xb0); + def_reserved!(RESERVED_B1, 0xb1); + def_reserved!(RESERVED_B2, 0xb2); + def_reserved!(RESERVED_B3, 0xb3); + def_reserved!(RESERVED_B4, 0xb4); + def_reserved!(RESERVED_B5, 0xb5); + def_reserved!(RESERVED_B6, 0xb6); + def_reserved!(RESERVED_B7, 0xb7); + def_reserved!(RESERVED_B8, 0xb8); + def_reserved!(RESERVED_B9, 0xb9); + def_reserved!(RESERVED_BA, 0xba); + def_reserved!(RESERVED_BB, 0xbb); + def_reserved!(RESERVED_BC, 0xbc); + def_reserved!(RESERVED_BD, 0xbd); + def_reserved!(RESERVED_BE, 0xbe); + def_reserved!(RESERVED_BF, 0xbf); + def_reserved!(RESERVED_C0, 0xc0); + def_reserved!(RESERVED_C1, 0xc1); + def_reserved!(RESERVED_C2, 0xc2); + def_reserved!(RESERVED_C3, 0xc3); + def_reserved!(RESERVED_C4, 0xc4); + def_reserved!(RESERVED_C5, 0xc5); + def_reserved!(RESERVED_C6, 0xc6); + def_reserved!(RESERVED_C7, 0xc7); + def_reserved!(RESERVED_C8, 0xc8); + def_reserved!(RESERVED_C9, 0xc9); + def_reserved!(RESERVED_CA, 0xca); + def_reserved!(RESERVED_CB, 0xcb); + def_reserved!(RESERVED_CC, 0xcc); + def_reserved!(RESERVED_CD, 0xcd); + def_reserved!(RESERVED_CE, 0xce); + def_reserved!(RESERVED_CF, 0xcf); + def_reserved!(RESERVED_D0, 0xd0); + def_reserved!(RESERVED_D1, 0xd1); + def_reserved!(RESERVED_D2, 0xd2); + def_reserved!(RESERVED_D3, 0xd3); + def_reserved!(RESERVED_D4, 0xd4); + def_reserved!(RESERVED_D5, 0xd5); + def_reserved!(RESERVED_D6, 0xd6); + def_reserved!(RESERVED_D7, 0xd7); + def_reserved!(RESERVED_D8, 0xd8); + def_reserved!(RESERVED_D9, 0xd9); + def_reserved!(RESERVED_DA, 0xda); + def_reserved!(RESERVED_DB, 0xdb); + def_reserved!(RESERVED_DC, 0xdc); + def_reserved!(RESERVED_DD, 0xdd); + def_reserved!(RESERVED_DE, 0xde); + def_reserved!(RESERVED_DF, 0xdf); + def_reserved!(RESERVED_E0, 0xe0); + def_reserved!(RESERVED_E1, 0xe1); + def_reserved!(RESERVED_E2, 0xe2); + def_reserved!(RESERVED_E3, 0xe3); + def_reserved!(RESERVED_E4, 0xe4); + def_reserved!(RESERVED_E5, 0xe5); + def_reserved!(RESERVED_E6, 0xe6); + def_reserved!(RESERVED_E7, 0xe7); + def_reserved!(RESERVED_E8, 0xe8); + def_reserved!(RESERVED_E9, 0xe9); + def_reserved!(RESERVED_EA, 0xea); + def_reserved!(RESERVED_EB, 0xeb); + def_reserved!(RESERVED_EC, 0xec); + def_reserved!(RESERVED_ED, 0xed); + def_reserved!(RESERVED_EE, 0xee); + def_reserved!(RESERVED_EF, 0xef); + def_opcode!(CREATE, 0xf0, 3, -2); + def_opcode!(CALL, 0xf1, 7, -6); + def_opcode!(CALLCODE, 0xf2, 7, -6); + def_opcode!(RETURN, 0xf3, 2, -2); + def_opcode!(DELEGATECALL, 0xf4, 6, -5); + def_opcode!(CREATE2, 0xf5, 4, -3); + def_reserved!(RESERVED_F6, 0xf6); + def_reserved!(RESERVED_F7, 0xf7); + def_reserved!(RESERVED_F8, 0xf8); + def_reserved!(RESERVED_F9, 0xf9); + def_opcode!(STATICCALL, 0xfa, 6, -5); + def_reserved!(RESERVED_FB, 0xfb); + def_reserved!(RESERVED_FC, 0xfc); + def_opcode!(REVERT, 0xfd, 2, -2); + def_opcode!(INVALID, 0xfe, 0, 0); + def_opcode!(SELFDESTRUCT, 0xff, 1, -1); + + const OPCODES: [OpCode; 256] = [ + OpCode::STOP, + OpCode::ADD, + OpCode::MUL, + OpCode::SUB, + OpCode::DIV, + OpCode::SDIV, + OpCode::MOD, + OpCode::SMOD, + OpCode::ADDMOD, + OpCode::MULMOD, + OpCode::EXP, + OpCode::SIGNEXTEND, + OpCode::RESERVED_0C, + OpCode::RESERVED_0D, + OpCode::RESERVED_0E, + OpCode::RESERVED_0F, + OpCode::LT, + OpCode::GT, + OpCode::SLT, + OpCode::SGT, + OpCode::EQ, + OpCode::ISZERO, + OpCode::AND, + OpCode::OR, + OpCode::XOR, + OpCode::NOT, + OpCode::BYTE, + OpCode::SHL, + OpCode::SHR, + OpCode::SAR, + OpCode::RESERVED_1E, + OpCode::RESERVED_1F, + OpCode::KECCAK256, + OpCode::RESERVED_21, + OpCode::RESERVED_22, + OpCode::RESERVED_23, + OpCode::RESERVED_24, + OpCode::RESERVED_25, + OpCode::RESERVED_26, + OpCode::RESERVED_27, + OpCode::RESERVED_28, + OpCode::RESERVED_29, + OpCode::RESERVED_2A, + OpCode::RESERVED_2B, + OpCode::RESERVED_2C, + OpCode::RESERVED_2D, + OpCode::RESERVED_2E, + OpCode::RESERVED_2F, + OpCode::ADDRESS, + OpCode::BALANCE, + OpCode::ORIGIN, + OpCode::CALLER, + OpCode::CALLVALUE, + OpCode::CALLDATALOAD, + OpCode::CALLDATASIZE, + OpCode::CALLDATACOPY, + OpCode::CODESIZE, + OpCode::CODECOPY, + OpCode::GASPRICE, + OpCode::EXTCODESIZE, + OpCode::EXTCODECOPY, + OpCode::RETURNDATASIZE, + OpCode::RETURNDATACOPY, + OpCode::EXTCODEHASH, + OpCode::BLOCKHASH, + OpCode::COINBASE, + OpCode::TIMESTAMP, + OpCode::NUMBER, + OpCode::DIFFICULTY, + OpCode::GASLIMIT, + OpCode::CHAINID, + OpCode::SELFBALANCE, + OpCode::BASEFEE, + OpCode::RESERVED_49, + OpCode::RESERVED_4A, + OpCode::RESERVED_4B, + OpCode::RESERVED_4C, + OpCode::RESERVED_4D, + OpCode::RESERVED_4E, + OpCode::RESERVED_4F, + OpCode::POP, + OpCode::MLOAD, + OpCode::MSTORE, + OpCode::MSTORE8, + OpCode::SLOAD, + OpCode::SSTORE, + OpCode::JUMP, + OpCode::JUMPI, + OpCode::PC, + OpCode::MSIZE, + OpCode::GAS, + OpCode::JUMPDEST, + OpCode::RESERVED_5C, + OpCode::RESERVED_5D, + OpCode::RESERVED_5E, + OpCode::RESERVED_5F, + OpCode::PUSH1, + OpCode::PUSH2, + OpCode::PUSH3, + OpCode::PUSH4, + OpCode::PUSH5, + OpCode::PUSH6, + OpCode::PUSH7, + OpCode::PUSH8, + OpCode::PUSH9, + OpCode::PUSH10, + OpCode::PUSH11, + OpCode::PUSH12, + OpCode::PUSH13, + OpCode::PUSH14, + OpCode::PUSH15, + OpCode::PUSH16, + OpCode::PUSH17, + OpCode::PUSH18, + OpCode::PUSH19, + OpCode::PUSH20, + OpCode::PUSH21, + OpCode::PUSH22, + OpCode::PUSH23, + OpCode::PUSH24, + OpCode::PUSH25, + OpCode::PUSH26, + OpCode::PUSH27, + OpCode::PUSH28, + OpCode::PUSH29, + OpCode::PUSH30, + OpCode::PUSH31, + OpCode::PUSH32, + OpCode::DUP1, + OpCode::DUP2, + OpCode::DUP3, + OpCode::DUP4, + OpCode::DUP5, + OpCode::DUP6, + OpCode::DUP7, + OpCode::DUP8, + OpCode::DUP9, + OpCode::DUP10, + OpCode::DUP11, + OpCode::DUP12, + OpCode::DUP13, + OpCode::DUP14, + OpCode::DUP15, + OpCode::DUP16, + OpCode::SWAP1, + OpCode::SWAP2, + OpCode::SWAP3, + OpCode::SWAP4, + OpCode::SWAP5, + OpCode::SWAP6, + OpCode::SWAP7, + OpCode::SWAP8, + OpCode::SWAP9, + OpCode::SWAP10, + OpCode::SWAP11, + OpCode::SWAP12, + OpCode::SWAP13, + OpCode::SWAP14, + OpCode::SWAP15, + OpCode::SWAP16, + OpCode::LOG0, + OpCode::LOG1, + OpCode::LOG2, + OpCode::LOG3, + OpCode::LOG4, + OpCode::RESERVED_A5, + OpCode::RESERVED_A6, + OpCode::RESERVED_A7, + OpCode::RESERVED_A8, + OpCode::RESERVED_A9, + OpCode::RESERVED_AA, + OpCode::RESERVED_AB, + OpCode::RESERVED_AC, + OpCode::RESERVED_AD, + OpCode::RESERVED_AE, + OpCode::RESERVED_AF, + OpCode::RESERVED_B0, + OpCode::RESERVED_B1, + OpCode::RESERVED_B2, + OpCode::RESERVED_B3, + OpCode::RESERVED_B4, + OpCode::RESERVED_B5, + OpCode::RESERVED_B6, + OpCode::RESERVED_B7, + OpCode::RESERVED_B8, + OpCode::RESERVED_B9, + OpCode::RESERVED_BA, + OpCode::RESERVED_BB, + OpCode::RESERVED_BC, + OpCode::RESERVED_BD, + OpCode::RESERVED_BE, + OpCode::RESERVED_BF, + OpCode::RESERVED_C0, + OpCode::RESERVED_C1, + OpCode::RESERVED_C2, + OpCode::RESERVED_C3, + OpCode::RESERVED_C4, + OpCode::RESERVED_C5, + OpCode::RESERVED_C6, + OpCode::RESERVED_C7, + OpCode::RESERVED_C8, + OpCode::RESERVED_C9, + OpCode::RESERVED_CA, + OpCode::RESERVED_CB, + OpCode::RESERVED_CC, + OpCode::RESERVED_CD, + OpCode::RESERVED_CE, + OpCode::RESERVED_CF, + OpCode::RESERVED_D0, + OpCode::RESERVED_D1, + OpCode::RESERVED_D2, + OpCode::RESERVED_D3, + OpCode::RESERVED_D4, + OpCode::RESERVED_D5, + OpCode::RESERVED_D6, + OpCode::RESERVED_D7, + OpCode::RESERVED_D8, + OpCode::RESERVED_D9, + OpCode::RESERVED_DA, + OpCode::RESERVED_DB, + OpCode::RESERVED_DC, + OpCode::RESERVED_DD, + OpCode::RESERVED_DE, + OpCode::RESERVED_DF, + OpCode::RESERVED_E0, + OpCode::RESERVED_E1, + OpCode::RESERVED_E2, + OpCode::RESERVED_E3, + OpCode::RESERVED_E4, + OpCode::RESERVED_E5, + OpCode::RESERVED_E6, + OpCode::RESERVED_E7, + OpCode::RESERVED_E8, + OpCode::RESERVED_E9, + OpCode::RESERVED_EA, + OpCode::RESERVED_EB, + OpCode::RESERVED_EC, + OpCode::RESERVED_ED, + OpCode::RESERVED_EE, + OpCode::RESERVED_EF, + OpCode::CREATE, + OpCode::CALL, + OpCode::CALLCODE, + OpCode::RETURN, + OpCode::DELEGATECALL, + OpCode::CREATE2, + OpCode::RESERVED_F6, + OpCode::RESERVED_F7, + OpCode::RESERVED_F8, + OpCode::RESERVED_F9, + OpCode::STATICCALL, + OpCode::RESERVED_FB, + OpCode::RESERVED_FC, + OpCode::REVERT, + OpCode::INVALID, + OpCode::SELFDESTRUCT, + ]; +} + impl std::fmt::Display for OpCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.name) @@ -1055,160 +588,13 @@ impl std::fmt::Display for OpCode { impl TryFrom for OpCode { type Error = StatusCode; - fn try_from(value: u8) -> Result { - // todo: optimize and turn it into a jump table - const OPCODES: [OpCode; 143] = [ - OpCode::STOP, - OpCode::ADD, - OpCode::MUL, - OpCode::SUB, - OpCode::DIV, - OpCode::SDIV, - OpCode::MOD, - OpCode::SMOD, - OpCode::ADDMOD, - OpCode::MULMOD, - OpCode::EXP, - OpCode::SIGNEXTEND, - OpCode::LT, - OpCode::GT, - OpCode::SLT, - OpCode::SGT, - OpCode::EQ, - OpCode::ISZERO, - OpCode::AND, - OpCode::OR, - OpCode::XOR, - OpCode::NOT, - OpCode::BYTE, - OpCode::SHL, - OpCode::SHR, - OpCode::SAR, - OpCode::KECCAK256, - OpCode::ADDRESS, - OpCode::BALANCE, - OpCode::ORIGIN, - OpCode::CALLER, - OpCode::CALLVALUE, - OpCode::CALLDATALOAD, - OpCode::CALLDATASIZE, - OpCode::CALLDATACOPY, - OpCode::CODESIZE, - OpCode::CODECOPY, - OpCode::GASPRICE, - OpCode::EXTCODESIZE, - OpCode::EXTCODECOPY, - OpCode::RETURNDATASIZE, - OpCode::RETURNDATACOPY, - OpCode::EXTCODEHASH, - OpCode::BLOCKHASH, - OpCode::COINBASE, - OpCode::TIMESTAMP, - OpCode::NUMBER, - OpCode::DIFFICULTY, - OpCode::GASLIMIT, - OpCode::CHAINID, - OpCode::SELFBALANCE, - OpCode::BASEFEE, - OpCode::POP, - OpCode::MLOAD, - OpCode::MSTORE, - OpCode::MSTORE8, - OpCode::SLOAD, - OpCode::SSTORE, - OpCode::JUMP, - OpCode::JUMPI, - OpCode::PC, - OpCode::MSIZE, - OpCode::GAS, - OpCode::JUMPDEST, - OpCode::PUSH1, - OpCode::PUSH2, - OpCode::PUSH3, - OpCode::PUSH4, - OpCode::PUSH5, - OpCode::PUSH6, - OpCode::PUSH7, - OpCode::PUSH8, - OpCode::PUSH9, - OpCode::PUSH10, - OpCode::PUSH11, - OpCode::PUSH12, - OpCode::PUSH13, - OpCode::PUSH14, - OpCode::PUSH15, - OpCode::PUSH16, - OpCode::PUSH17, - OpCode::PUSH18, - OpCode::PUSH19, - OpCode::PUSH20, - OpCode::PUSH21, - OpCode::PUSH22, - OpCode::PUSH23, - OpCode::PUSH24, - OpCode::PUSH25, - OpCode::PUSH26, - OpCode::PUSH27, - OpCode::PUSH28, - OpCode::PUSH29, - OpCode::PUSH30, - OpCode::PUSH31, - OpCode::PUSH32, - OpCode::DUP1, - OpCode::DUP2, - OpCode::DUP3, - OpCode::DUP4, - OpCode::DUP5, - OpCode::DUP6, - OpCode::DUP7, - OpCode::DUP8, - OpCode::DUP9, - OpCode::DUP10, - OpCode::DUP11, - OpCode::DUP12, - OpCode::DUP13, - OpCode::DUP14, - OpCode::DUP15, - OpCode::DUP16, - OpCode::SWAP1, - OpCode::SWAP2, - OpCode::SWAP3, - OpCode::SWAP4, - OpCode::SWAP5, - OpCode::SWAP6, - OpCode::SWAP7, - OpCode::SWAP8, - OpCode::SWAP9, - OpCode::SWAP10, - OpCode::SWAP11, - OpCode::SWAP12, - OpCode::SWAP13, - OpCode::SWAP14, - OpCode::SWAP15, - OpCode::SWAP16, - OpCode::LOG0, - OpCode::LOG1, - OpCode::LOG2, - OpCode::LOG3, - OpCode::LOG4, - OpCode::CREATE, - OpCode::CALL, - OpCode::CALLCODE, - OpCode::RETURN, - OpCode::DELEGATECALL, - OpCode::CREATE2, - OpCode::STATICCALL, - OpCode::REVERT, - OpCode::INVALID, - OpCode::SELFDESTRUCT, - ]; + fn try_from(op: u8) -> Result { + let opc = OpCode::OPCODES[op as usize]; - for op in OPCODES { - if op == value { - return Ok(op); - } + if opc.reserved { + return Err(StatusCode::UndefinedInstruction); } - Err(StatusCode::UndefinedInstruction) + Ok(opc) } } From 85338732d744dd398a92978da619c41d3f3fd826 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 23 Aug 2022 08:42:47 -0700 Subject: [PATCH 011/339] feat: enum-based opcodes (#573) - removes the explicit instruction array. - removes the undefined instructions. - removes the need for ordering instructions in a specific way. - ensures that we can't talk about invalid instructions. --- actors/evm/src/interpreter/bytecode.rs | 6 +- actors/evm/src/interpreter/execution.rs | 1 - actors/evm/src/interpreter/opcode.rs | 792 +++++++----------------- 3 files changed, 212 insertions(+), 587 deletions(-) diff --git a/actors/evm/src/interpreter/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs index 841a9e92a..8ca77e0f7 100644 --- a/actors/evm/src/interpreter/bytecode.rs +++ b/actors/evm/src/interpreter/bytecode.rs @@ -15,11 +15,11 @@ impl<'c> Bytecode<'c> { let mut jumpdest = vec![false; bytecode.len()]; let mut i = 0; while i < bytecode.len() { - if bytecode[i] == OpCode::JUMPDEST.code { + if bytecode[i] == OpCode::JUMPDEST as u8 { jumpdest[i] = true; i += 1; - } else if bytecode[i] >= OpCode::PUSH1.code && bytecode[i] <= OpCode::PUSH32.code { - i += (bytecode[i] - OpCode::PUSH1.code) as usize + 2; + } else if bytecode[i] >= OpCode::PUSH1 as u8 && bytecode[i] <= OpCode::PUSH32 as u8 { + i += (bytecode[i] - OpCode::PUSH1 as u8) as usize + 2; } else { i += 1; } diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index d86e0b4d2..2fafbcecd 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -207,7 +207,6 @@ pub fn execute<'r, BS: Blockstore, RT: Runtime>( } OpCode::INVALID => return Err(StatusCode::InvalidInstruction), OpCode::SELFDESTRUCT => storage::selfdestruct(runtime, system)?, - _ => return Err(StatusCode::UndefinedInstruction), } pc += 1; // advance diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs index 724d2f312..f99413bd6 100644 --- a/actors/evm/src/interpreter/opcode.rs +++ b/actors/evm/src/interpreter/opcode.rs @@ -6,595 +6,221 @@ use crate::interpreter::output::StatusCode; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct OpCode { - /// the byte representing the opcode in binary - pub code: u8, - - /// The number of stack items the instruction accesses during execution. - pub stack_height_required: u8, - - /// The stack height change caused by the instruction execution. Can be - /// negative. - pub stack_height_change: i8, - - /// Human readable name of the opcode. - pub name: &'static str, - - /// Reserved/Undefined opcode indicator - pub reserved: bool, -} - -impl From for u8 { - fn from(op: OpCode) -> Self { - op.code - } -} - -impl PartialEq for OpCode { - fn eq(&self, other: &u8) -> bool { - self.code == *other - } -} +macro_rules! def_opcodes { + ($($code:literal: $name:ident($stack:literal, $change:literal),)*) => { + #[repr(u8)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum OpCode { + $($name = $code,)* + } + #[derive(Copy, Clone, Debug)] + pub struct StackSpec { + pub required: u8, + pub changed: i8, + } -macro_rules! def_opcode { - ($id:ident, $code:literal, $sk_required:literal, $sk_change:literal) => { - pub const $id: OpCode = OpCode { - code: $code, - stack_height_required: $sk_required, - stack_height_change: $sk_change, - name: stringify!($id), - reserved: false, - }; - }; -} + impl std::fmt::Display for OpCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name()) + } + } -macro_rules! def_reserved { - ($id:ident, $code:literal) => { - pub const $id: OpCode = OpCode { - code: $code, - stack_height_required: 0, - stack_height_change: 0, - name: "RESERVED", - reserved: true, - }; - }; -} + impl From for u8 { + #[inline(always)] + fn from(op: OpCode) -> Self { + op as u8 + } + } -impl OpCode { - def_opcode!(STOP, 0x00, 0, 0); - def_opcode!(ADD, 0x01, 2, -1); - def_opcode!(MUL, 0x02, 2, -1); - def_opcode!(SUB, 0x03, 2, -1); - def_opcode!(DIV, 0x04, 2, -1); - def_opcode!(SDIV, 0x05, 2, -1); - def_opcode!(MOD, 0x06, 2, -1); - def_opcode!(SMOD, 0x07, 2, -1); - def_opcode!(ADDMOD, 0x08, 3, -2); - def_opcode!(MULMOD, 0x09, 3, -2); - def_opcode!(EXP, 0x0a, 2, -1); - def_opcode!(SIGNEXTEND, 0x0b, 2, -1); - def_reserved!(RESERVED_0C, 0x0c); - def_reserved!(RESERVED_0D, 0x0d); - def_reserved!(RESERVED_0E, 0x0e); - def_reserved!(RESERVED_0F, 0x0f); - def_opcode!(LT, 0x10, 2, -1); - def_opcode!(GT, 0x11, 2, -1); - def_opcode!(SLT, 0x12, 2, -1); - def_opcode!(SGT, 0x13, 2, -1); - def_opcode!(EQ, 0x14, 2, -1); - def_opcode!(ISZERO, 0x15, 1, 0); - def_opcode!(AND, 0x16, 2, -1); - def_opcode!(OR, 0x17, 2, -1); - def_opcode!(XOR, 0x18, 2, -1); - def_opcode!(NOT, 0x19, 1, 0); - def_opcode!(BYTE, 0x1a, 2, -1); - def_opcode!(SHL, 0x1b, 2, -1); - def_opcode!(SHR, 0x1c, 2, -1); - def_opcode!(SAR, 0x1d, 2, -1); - def_reserved!(RESERVED_1E, 0x1e); - def_reserved!(RESERVED_1F, 0x1f); - def_opcode!(KECCAK256, 0x20, 2, -1); // SHA3 - def_reserved!(RESERVED_21, 0x21); - def_reserved!(RESERVED_22, 0x22); - def_reserved!(RESERVED_23, 0x23); - def_reserved!(RESERVED_24, 0x24); - def_reserved!(RESERVED_25, 0x25); - def_reserved!(RESERVED_26, 0x26); - def_reserved!(RESERVED_27, 0x27); - def_reserved!(RESERVED_28, 0x28); - def_reserved!(RESERVED_29, 0x29); - def_reserved!(RESERVED_2A, 0x2a); - def_reserved!(RESERVED_2B, 0x2b); - def_reserved!(RESERVED_2C, 0x2c); - def_reserved!(RESERVED_2D, 0x2d); - def_reserved!(RESERVED_2E, 0x2e); - def_reserved!(RESERVED_2F, 0x2f); - def_opcode!(ADDRESS, 0x30, 0, 1); - def_opcode!(BALANCE, 0x31, 1, 0); - def_opcode!(ORIGIN, 0x32, 0, 1); - def_opcode!(CALLER, 0x33, 0, 1); - def_opcode!(CALLVALUE, 0x34, 0, 1); - def_opcode!(CALLDATALOAD, 0x35, 1, 0); - def_opcode!(CALLDATASIZE, 0x36, 0, 1); - def_opcode!(CALLDATACOPY, 0x37, 3, -3); - def_opcode!(CODESIZE, 0x38, 0, 1); - def_opcode!(CODECOPY, 0x39, 3, -3); - def_opcode!(GASPRICE, 0x3a, 0, 1); - def_opcode!(EXTCODESIZE, 0x3b, 1, 0); - def_opcode!(EXTCODECOPY, 0x3c, 4, -4); - def_opcode!(RETURNDATASIZE, 0x3d, 0, 1); - def_opcode!(RETURNDATACOPY, 0x3e, 3, -3); - def_opcode!(EXTCODEHASH, 0x3f, 1, 0); - def_opcode!(BLOCKHASH, 0x40, 1, 0); - def_opcode!(COINBASE, 0x41, 0, 1); - def_opcode!(TIMESTAMP, 0x42, 0, 1); - def_opcode!(NUMBER, 0x43, 0, 1); - def_opcode!(DIFFICULTY, 0x44, 0, 1); - def_opcode!(GASLIMIT, 0x45, 0, 1); - def_opcode!(CHAINID, 0x46, 0, 1); - def_opcode!(SELFBALANCE, 0x47, 0, 1); - def_opcode!(BASEFEE, 0x48, 0, 1); - def_reserved!(RESERVED_49, 0x49); - def_reserved!(RESERVED_4A, 0x4a); - def_reserved!(RESERVED_4B, 0x4b); - def_reserved!(RESERVED_4C, 0x4c); - def_reserved!(RESERVED_4D, 0x4d); - def_reserved!(RESERVED_4E, 0x4e); - def_reserved!(RESERVED_4F, 0x4f); - def_opcode!(POP, 0x50, 1, -1); - def_opcode!(MLOAD, 0x51, 1, 0); - def_opcode!(MSTORE, 0x52, 2, -2); - def_opcode!(MSTORE8, 0x53, 2, -2); - def_opcode!(SLOAD, 0x54, 1, 0); - def_opcode!(SSTORE, 0x55, 2, -2); - def_opcode!(JUMP, 0x56, 1, -1); - def_opcode!(JUMPI, 0x57, 2, -2); - def_opcode!(PC, 0x58, 0, 1); - def_opcode!(MSIZE, 0x59, 0, 1); - def_opcode!(GAS, 0x5a, 0, 1); - def_opcode!(JUMPDEST, 0x5b, 0, 0); - def_reserved!(RESERVED_5C, 0x5c); - def_reserved!(RESERVED_5D, 0x5d); - def_reserved!(RESERVED_5E, 0x5e); - def_reserved!(RESERVED_5F, 0x5f); - def_opcode!(PUSH1, 0x60, 0, 1); - def_opcode!(PUSH2, 0x61, 0, 1); - def_opcode!(PUSH3, 0x62, 0, 1); - def_opcode!(PUSH4, 0x63, 0, 1); - def_opcode!(PUSH5, 0x64, 0, 1); - def_opcode!(PUSH6, 0x65, 0, 1); - def_opcode!(PUSH7, 0x66, 0, 1); - def_opcode!(PUSH8, 0x67, 0, 1); - def_opcode!(PUSH9, 0x68, 0, 1); - def_opcode!(PUSH10, 0x69, 0, 1); - def_opcode!(PUSH11, 0x6a, 0, 1); - def_opcode!(PUSH12, 0x6b, 0, 1); - def_opcode!(PUSH13, 0x6c, 0, 1); - def_opcode!(PUSH14, 0x6d, 0, 1); - def_opcode!(PUSH15, 0x6e, 0, 1); - def_opcode!(PUSH16, 0x6f, 0, 1); - def_opcode!(PUSH17, 0x70, 0, 1); - def_opcode!(PUSH18, 0x71, 0, 1); - def_opcode!(PUSH19, 0x72, 0, 1); - def_opcode!(PUSH20, 0x73, 0, 1); - def_opcode!(PUSH21, 0x74, 0, 1); - def_opcode!(PUSH22, 0x75, 0, 1); - def_opcode!(PUSH23, 0x76, 0, 1); - def_opcode!(PUSH24, 0x77, 0, 1); - def_opcode!(PUSH25, 0x78, 0, 1); - def_opcode!(PUSH26, 0x79, 0, 1); - def_opcode!(PUSH27, 0x7a, 0, 1); - def_opcode!(PUSH28, 0x7b, 0, 1); - def_opcode!(PUSH29, 0x7c, 0, 1); - def_opcode!(PUSH30, 0x7d, 0, 1); - def_opcode!(PUSH31, 0x7e, 0, 1); - def_opcode!(PUSH32, 0x7f, 0, 1); - def_opcode!(DUP1, 0x80, 1, 1); - def_opcode!(DUP2, 0x81, 2, 1); - def_opcode!(DUP3, 0x82, 3, 1); - def_opcode!(DUP4, 0x83, 4, 1); - def_opcode!(DUP5, 0x84, 5, 1); - def_opcode!(DUP6, 0x85, 6, 1); - def_opcode!(DUP7, 0x86, 7, 1); - def_opcode!(DUP8, 0x87, 8, 1); - def_opcode!(DUP9, 0x88, 9, 1); - def_opcode!(DUP10, 0x89, 10, 1); - def_opcode!(DUP11, 0x8a, 11, 1); - def_opcode!(DUP12, 0x8b, 12, 1); - def_opcode!(DUP13, 0x8c, 13, 1); - def_opcode!(DUP14, 0x8d, 14, 1); - def_opcode!(DUP15, 0x8e, 15, 1); - def_opcode!(DUP16, 0x8f, 16, 1); - def_opcode!(SWAP1, 0x90, 2, 0); - def_opcode!(SWAP2, 0x91, 3, 0); - def_opcode!(SWAP3, 0x92, 4, 0); - def_opcode!(SWAP4, 0x93, 5, 0); - def_opcode!(SWAP5, 0x94, 6, 0); - def_opcode!(SWAP6, 0x95, 7, 0); - def_opcode!(SWAP7, 0x96, 8, 0); - def_opcode!(SWAP8, 0x97, 9, 0); - def_opcode!(SWAP9, 0x98, 10, 0); - def_opcode!(SWAP10, 0x99, 11, 0); - def_opcode!(SWAP11, 0x9a, 12, 0); - def_opcode!(SWAP12, 0x9b, 13, 0); - def_opcode!(SWAP13, 0x9c, 14, 0); - def_opcode!(SWAP14, 0x9d, 15, 0); - def_opcode!(SWAP15, 0x9e, 16, 0); - def_opcode!(SWAP16, 0x9f, 17, 0); - def_opcode!(LOG0, 0xa0, 2, -2); - def_opcode!(LOG1, 0xa1, 3, -3); - def_opcode!(LOG2, 0xa2, 4, -4); - def_opcode!(LOG3, 0xa3, 5, -5); - def_opcode!(LOG4, 0xa4, 6, -6); - def_reserved!(RESERVED_A5, 0xa5); - def_reserved!(RESERVED_A6, 0xa6); - def_reserved!(RESERVED_A7, 0xa7); - def_reserved!(RESERVED_A8, 0xa8); - def_reserved!(RESERVED_A9, 0xa9); - def_reserved!(RESERVED_AA, 0xaa); - def_reserved!(RESERVED_AB, 0xab); - def_reserved!(RESERVED_AC, 0xac); - def_reserved!(RESERVED_AD, 0xad); - def_reserved!(RESERVED_AE, 0xae); - def_reserved!(RESERVED_AF, 0xaf); - def_reserved!(RESERVED_B0, 0xb0); - def_reserved!(RESERVED_B1, 0xb1); - def_reserved!(RESERVED_B2, 0xb2); - def_reserved!(RESERVED_B3, 0xb3); - def_reserved!(RESERVED_B4, 0xb4); - def_reserved!(RESERVED_B5, 0xb5); - def_reserved!(RESERVED_B6, 0xb6); - def_reserved!(RESERVED_B7, 0xb7); - def_reserved!(RESERVED_B8, 0xb8); - def_reserved!(RESERVED_B9, 0xb9); - def_reserved!(RESERVED_BA, 0xba); - def_reserved!(RESERVED_BB, 0xbb); - def_reserved!(RESERVED_BC, 0xbc); - def_reserved!(RESERVED_BD, 0xbd); - def_reserved!(RESERVED_BE, 0xbe); - def_reserved!(RESERVED_BF, 0xbf); - def_reserved!(RESERVED_C0, 0xc0); - def_reserved!(RESERVED_C1, 0xc1); - def_reserved!(RESERVED_C2, 0xc2); - def_reserved!(RESERVED_C3, 0xc3); - def_reserved!(RESERVED_C4, 0xc4); - def_reserved!(RESERVED_C5, 0xc5); - def_reserved!(RESERVED_C6, 0xc6); - def_reserved!(RESERVED_C7, 0xc7); - def_reserved!(RESERVED_C8, 0xc8); - def_reserved!(RESERVED_C9, 0xc9); - def_reserved!(RESERVED_CA, 0xca); - def_reserved!(RESERVED_CB, 0xcb); - def_reserved!(RESERVED_CC, 0xcc); - def_reserved!(RESERVED_CD, 0xcd); - def_reserved!(RESERVED_CE, 0xce); - def_reserved!(RESERVED_CF, 0xcf); - def_reserved!(RESERVED_D0, 0xd0); - def_reserved!(RESERVED_D1, 0xd1); - def_reserved!(RESERVED_D2, 0xd2); - def_reserved!(RESERVED_D3, 0xd3); - def_reserved!(RESERVED_D4, 0xd4); - def_reserved!(RESERVED_D5, 0xd5); - def_reserved!(RESERVED_D6, 0xd6); - def_reserved!(RESERVED_D7, 0xd7); - def_reserved!(RESERVED_D8, 0xd8); - def_reserved!(RESERVED_D9, 0xd9); - def_reserved!(RESERVED_DA, 0xda); - def_reserved!(RESERVED_DB, 0xdb); - def_reserved!(RESERVED_DC, 0xdc); - def_reserved!(RESERVED_DD, 0xdd); - def_reserved!(RESERVED_DE, 0xde); - def_reserved!(RESERVED_DF, 0xdf); - def_reserved!(RESERVED_E0, 0xe0); - def_reserved!(RESERVED_E1, 0xe1); - def_reserved!(RESERVED_E2, 0xe2); - def_reserved!(RESERVED_E3, 0xe3); - def_reserved!(RESERVED_E4, 0xe4); - def_reserved!(RESERVED_E5, 0xe5); - def_reserved!(RESERVED_E6, 0xe6); - def_reserved!(RESERVED_E7, 0xe7); - def_reserved!(RESERVED_E8, 0xe8); - def_reserved!(RESERVED_E9, 0xe9); - def_reserved!(RESERVED_EA, 0xea); - def_reserved!(RESERVED_EB, 0xeb); - def_reserved!(RESERVED_EC, 0xec); - def_reserved!(RESERVED_ED, 0xed); - def_reserved!(RESERVED_EE, 0xee); - def_reserved!(RESERVED_EF, 0xef); - def_opcode!(CREATE, 0xf0, 3, -2); - def_opcode!(CALL, 0xf1, 7, -6); - def_opcode!(CALLCODE, 0xf2, 7, -6); - def_opcode!(RETURN, 0xf3, 2, -2); - def_opcode!(DELEGATECALL, 0xf4, 6, -5); - def_opcode!(CREATE2, 0xf5, 4, -3); - def_reserved!(RESERVED_F6, 0xf6); - def_reserved!(RESERVED_F7, 0xf7); - def_reserved!(RESERVED_F8, 0xf8); - def_reserved!(RESERVED_F9, 0xf9); - def_opcode!(STATICCALL, 0xfa, 6, -5); - def_reserved!(RESERVED_FB, 0xfb); - def_reserved!(RESERVED_FC, 0xfc); - def_opcode!(REVERT, 0xfd, 2, -2); - def_opcode!(INVALID, 0xfe, 0, 0); - def_opcode!(SELFDESTRUCT, 0xff, 1, -1); + impl TryFrom for OpCode { + type Error = StatusCode; + + fn try_from(op: u8) -> Result { + const fn codes() -> [bool; 256] { + let mut table = [false; 256]; + $(table[$code] = true;)* + table + } + const CODES: [bool; 256] = codes(); + if !CODES[op as usize] { + return Err(StatusCode::UndefinedInstruction); + } + + Ok(unsafe { std::mem::transmute(op) }) + } + } - const OPCODES: [OpCode; 256] = [ - OpCode::STOP, - OpCode::ADD, - OpCode::MUL, - OpCode::SUB, - OpCode::DIV, - OpCode::SDIV, - OpCode::MOD, - OpCode::SMOD, - OpCode::ADDMOD, - OpCode::MULMOD, - OpCode::EXP, - OpCode::SIGNEXTEND, - OpCode::RESERVED_0C, - OpCode::RESERVED_0D, - OpCode::RESERVED_0E, - OpCode::RESERVED_0F, - OpCode::LT, - OpCode::GT, - OpCode::SLT, - OpCode::SGT, - OpCode::EQ, - OpCode::ISZERO, - OpCode::AND, - OpCode::OR, - OpCode::XOR, - OpCode::NOT, - OpCode::BYTE, - OpCode::SHL, - OpCode::SHR, - OpCode::SAR, - OpCode::RESERVED_1E, - OpCode::RESERVED_1F, - OpCode::KECCAK256, - OpCode::RESERVED_21, - OpCode::RESERVED_22, - OpCode::RESERVED_23, - OpCode::RESERVED_24, - OpCode::RESERVED_25, - OpCode::RESERVED_26, - OpCode::RESERVED_27, - OpCode::RESERVED_28, - OpCode::RESERVED_29, - OpCode::RESERVED_2A, - OpCode::RESERVED_2B, - OpCode::RESERVED_2C, - OpCode::RESERVED_2D, - OpCode::RESERVED_2E, - OpCode::RESERVED_2F, - OpCode::ADDRESS, - OpCode::BALANCE, - OpCode::ORIGIN, - OpCode::CALLER, - OpCode::CALLVALUE, - OpCode::CALLDATALOAD, - OpCode::CALLDATASIZE, - OpCode::CALLDATACOPY, - OpCode::CODESIZE, - OpCode::CODECOPY, - OpCode::GASPRICE, - OpCode::EXTCODESIZE, - OpCode::EXTCODECOPY, - OpCode::RETURNDATASIZE, - OpCode::RETURNDATACOPY, - OpCode::EXTCODEHASH, - OpCode::BLOCKHASH, - OpCode::COINBASE, - OpCode::TIMESTAMP, - OpCode::NUMBER, - OpCode::DIFFICULTY, - OpCode::GASLIMIT, - OpCode::CHAINID, - OpCode::SELFBALANCE, - OpCode::BASEFEE, - OpCode::RESERVED_49, - OpCode::RESERVED_4A, - OpCode::RESERVED_4B, - OpCode::RESERVED_4C, - OpCode::RESERVED_4D, - OpCode::RESERVED_4E, - OpCode::RESERVED_4F, - OpCode::POP, - OpCode::MLOAD, - OpCode::MSTORE, - OpCode::MSTORE8, - OpCode::SLOAD, - OpCode::SSTORE, - OpCode::JUMP, - OpCode::JUMPI, - OpCode::PC, - OpCode::MSIZE, - OpCode::GAS, - OpCode::JUMPDEST, - OpCode::RESERVED_5C, - OpCode::RESERVED_5D, - OpCode::RESERVED_5E, - OpCode::RESERVED_5F, - OpCode::PUSH1, - OpCode::PUSH2, - OpCode::PUSH3, - OpCode::PUSH4, - OpCode::PUSH5, - OpCode::PUSH6, - OpCode::PUSH7, - OpCode::PUSH8, - OpCode::PUSH9, - OpCode::PUSH10, - OpCode::PUSH11, - OpCode::PUSH12, - OpCode::PUSH13, - OpCode::PUSH14, - OpCode::PUSH15, - OpCode::PUSH16, - OpCode::PUSH17, - OpCode::PUSH18, - OpCode::PUSH19, - OpCode::PUSH20, - OpCode::PUSH21, - OpCode::PUSH22, - OpCode::PUSH23, - OpCode::PUSH24, - OpCode::PUSH25, - OpCode::PUSH26, - OpCode::PUSH27, - OpCode::PUSH28, - OpCode::PUSH29, - OpCode::PUSH30, - OpCode::PUSH31, - OpCode::PUSH32, - OpCode::DUP1, - OpCode::DUP2, - OpCode::DUP3, - OpCode::DUP4, - OpCode::DUP5, - OpCode::DUP6, - OpCode::DUP7, - OpCode::DUP8, - OpCode::DUP9, - OpCode::DUP10, - OpCode::DUP11, - OpCode::DUP12, - OpCode::DUP13, - OpCode::DUP14, - OpCode::DUP15, - OpCode::DUP16, - OpCode::SWAP1, - OpCode::SWAP2, - OpCode::SWAP3, - OpCode::SWAP4, - OpCode::SWAP5, - OpCode::SWAP6, - OpCode::SWAP7, - OpCode::SWAP8, - OpCode::SWAP9, - OpCode::SWAP10, - OpCode::SWAP11, - OpCode::SWAP12, - OpCode::SWAP13, - OpCode::SWAP14, - OpCode::SWAP15, - OpCode::SWAP16, - OpCode::LOG0, - OpCode::LOG1, - OpCode::LOG2, - OpCode::LOG3, - OpCode::LOG4, - OpCode::RESERVED_A5, - OpCode::RESERVED_A6, - OpCode::RESERVED_A7, - OpCode::RESERVED_A8, - OpCode::RESERVED_A9, - OpCode::RESERVED_AA, - OpCode::RESERVED_AB, - OpCode::RESERVED_AC, - OpCode::RESERVED_AD, - OpCode::RESERVED_AE, - OpCode::RESERVED_AF, - OpCode::RESERVED_B0, - OpCode::RESERVED_B1, - OpCode::RESERVED_B2, - OpCode::RESERVED_B3, - OpCode::RESERVED_B4, - OpCode::RESERVED_B5, - OpCode::RESERVED_B6, - OpCode::RESERVED_B7, - OpCode::RESERVED_B8, - OpCode::RESERVED_B9, - OpCode::RESERVED_BA, - OpCode::RESERVED_BB, - OpCode::RESERVED_BC, - OpCode::RESERVED_BD, - OpCode::RESERVED_BE, - OpCode::RESERVED_BF, - OpCode::RESERVED_C0, - OpCode::RESERVED_C1, - OpCode::RESERVED_C2, - OpCode::RESERVED_C3, - OpCode::RESERVED_C4, - OpCode::RESERVED_C5, - OpCode::RESERVED_C6, - OpCode::RESERVED_C7, - OpCode::RESERVED_C8, - OpCode::RESERVED_C9, - OpCode::RESERVED_CA, - OpCode::RESERVED_CB, - OpCode::RESERVED_CC, - OpCode::RESERVED_CD, - OpCode::RESERVED_CE, - OpCode::RESERVED_CF, - OpCode::RESERVED_D0, - OpCode::RESERVED_D1, - OpCode::RESERVED_D2, - OpCode::RESERVED_D3, - OpCode::RESERVED_D4, - OpCode::RESERVED_D5, - OpCode::RESERVED_D6, - OpCode::RESERVED_D7, - OpCode::RESERVED_D8, - OpCode::RESERVED_D9, - OpCode::RESERVED_DA, - OpCode::RESERVED_DB, - OpCode::RESERVED_DC, - OpCode::RESERVED_DD, - OpCode::RESERVED_DE, - OpCode::RESERVED_DF, - OpCode::RESERVED_E0, - OpCode::RESERVED_E1, - OpCode::RESERVED_E2, - OpCode::RESERVED_E3, - OpCode::RESERVED_E4, - OpCode::RESERVED_E5, - OpCode::RESERVED_E6, - OpCode::RESERVED_E7, - OpCode::RESERVED_E8, - OpCode::RESERVED_E9, - OpCode::RESERVED_EA, - OpCode::RESERVED_EB, - OpCode::RESERVED_EC, - OpCode::RESERVED_ED, - OpCode::RESERVED_EE, - OpCode::RESERVED_EF, - OpCode::CREATE, - OpCode::CALL, - OpCode::CALLCODE, - OpCode::RETURN, - OpCode::DELEGATECALL, - OpCode::CREATE2, - OpCode::RESERVED_F6, - OpCode::RESERVED_F7, - OpCode::RESERVED_F8, - OpCode::RESERVED_F9, - OpCode::STATICCALL, - OpCode::RESERVED_FB, - OpCode::RESERVED_FC, - OpCode::REVERT, - OpCode::INVALID, - OpCode::SELFDESTRUCT, - ]; -} + impl PartialEq for OpCode { + fn eq(&self, other: &u8) -> bool { + (*self as u8) == *other + } + } -impl std::fmt::Display for OpCode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) + impl OpCode { + pub const fn spec(self) -> StackSpec { + const fn specs() -> [StackSpec; 256] { + let mut table = [StackSpec{required: 0, changed: 0}; 256]; + $(table[$code] = StackSpec{required: $stack, changed: $change};)* + table + } + const SPECS: [StackSpec; 256] = specs(); + SPECS[self as usize] + } + pub const fn name(self) -> &'static str { + const fn names() -> [&'static str; 256] { + let mut table = ["RESERVED"; 256]; + $(table[$code] = stringify!($name);)* + table + } + const NAMES: [&'static str; 256] = names(); + NAMES[self as usize] + } + } } } -impl TryFrom for OpCode { - type Error = StatusCode; - - fn try_from(op: u8) -> Result { - let opc = OpCode::OPCODES[op as usize]; - - if opc.reserved { - return Err(StatusCode::UndefinedInstruction); - } - - Ok(opc) - } +def_opcodes! { + 0x00: STOP(0, 0), + 0x01: ADD(2, -1), + 0x02: MUL(2, -1), + 0x03: SUB(2, -1), + 0x04: DIV(2, -1), + 0x05: SDIV(2, -1), + 0x06: MOD(2, -1), + 0x07: SMOD(2, -1), + 0x08: ADDMOD(3, -2), + 0x09: MULMOD(3, -2), + 0x0a: EXP(2, -1), + 0x0b: SIGNEXTEND(2, -1), + 0x10: LT(2, -1), + 0x11: GT(2, -1), + 0x12: SLT(2, -1), + 0x13: SGT(2, -1), + 0x14: EQ(2, -1), + 0x15: ISZERO(1, 0), + 0x16: AND(2, -1), + 0x17: OR(2, -1), + 0x18: XOR(2, -1), + 0x19: NOT(1, 0), + 0x1a: BYTE(2, -1), + 0x1b: SHL(2, -1), + 0x1c: SHR(2, -1), + 0x1d: SAR(2, -1), + 0x20: KECCAK256(2, -1), // SHA3 + 0x30: ADDRESS(0, 1), + 0x31: BALANCE(1, 0), + 0x32: ORIGIN(0, 1), + 0x33: CALLER(0, 1), + 0x34: CALLVALUE(0, 1), + 0x35: CALLDATALOAD(1, 0), + 0x36: CALLDATASIZE(0, 1), + 0x37: CALLDATACOPY(3, -3), + 0x38: CODESIZE(0, 1), + 0x39: CODECOPY(3, -3), + 0x3a: GASPRICE(0, 1), + 0x3b: EXTCODESIZE(1, 0), + 0x3c: EXTCODECOPY(4, -4), + 0x3d: RETURNDATASIZE(0, 1), + 0x3e: RETURNDATACOPY(3, -3), + 0x3f: EXTCODEHASH(1, 0), + 0x40: BLOCKHASH(1, 0), + 0x41: COINBASE(0, 1), + 0x42: TIMESTAMP(0, 1), + 0x43: NUMBER(0, 1), + 0x44: DIFFICULTY(0, 1), + 0x45: GASLIMIT(0, 1), + 0x46: CHAINID(0, 1), + 0x47: SELFBALANCE(0, 1), + 0x48: BASEFEE(0, 1), + 0x50: POP(1, -1), + 0x51: MLOAD(1, 0), + 0x52: MSTORE(2, -2), + 0x53: MSTORE8(2, -2), + 0x54: SLOAD(1, 0), + 0x55: SSTORE(2, -2), + 0x56: JUMP(1, -1), + 0x57: JUMPI(2, -2), + 0x58: PC(0, 1), + 0x59: MSIZE(0, 1), + 0x5a: GAS(0, 1), + 0x5b: JUMPDEST(0, 0), + 0x60: PUSH1(0, 1), + 0x61: PUSH2(0, 1), + 0x62: PUSH3(0, 1), + 0x63: PUSH4(0, 1), + 0x64: PUSH5(0, 1), + 0x65: PUSH6(0, 1), + 0x66: PUSH7(0, 1), + 0x67: PUSH8(0, 1), + 0x68: PUSH9(0, 1), + 0x69: PUSH10(0, 1), + 0x6a: PUSH11(0, 1), + 0x6b: PUSH12(0, 1), + 0x6c: PUSH13(0, 1), + 0x6d: PUSH14(0, 1), + 0x6e: PUSH15(0, 1), + 0x6f: PUSH16(0, 1), + 0x70: PUSH17(0, 1), + 0x71: PUSH18(0, 1), + 0x72: PUSH19(0, 1), + 0x73: PUSH20(0, 1), + 0x74: PUSH21(0, 1), + 0x75: PUSH22(0, 1), + 0x76: PUSH23(0, 1), + 0x77: PUSH24(0, 1), + 0x78: PUSH25(0, 1), + 0x79: PUSH26(0, 1), + 0x7a: PUSH27(0, 1), + 0x7b: PUSH28(0, 1), + 0x7c: PUSH29(0, 1), + 0x7d: PUSH30(0, 1), + 0x7e: PUSH31(0, 1), + 0x7f: PUSH32(0, 1), + 0x80: DUP1(1, 1), + 0x81: DUP2(2, 1), + 0x82: DUP3(3, 1), + 0x83: DUP4(4, 1), + 0x84: DUP5(5, 1), + 0x85: DUP6(6, 1), + 0x86: DUP7(7, 1), + 0x87: DUP8(8, 1), + 0x88: DUP9(9, 1), + 0x89: DUP10(10, 1), + 0x8a: DUP11(11, 1), + 0x8b: DUP12(12, 1), + 0x8c: DUP13(13, 1), + 0x8d: DUP14(14, 1), + 0x8e: DUP15(15, 1), + 0x8f: DUP16(16, 1), + 0x90: SWAP1(2, 0), + 0x91: SWAP2(3, 0), + 0x92: SWAP3(4, 0), + 0x93: SWAP4(5, 0), + 0x94: SWAP5(6, 0), + 0x95: SWAP6(7, 0), + 0x96: SWAP7(8, 0), + 0x97: SWAP8(9, 0), + 0x98: SWAP9(10, 0), + 0x99: SWAP10(11, 0), + 0x9a: SWAP11(12, 0), + 0x9b: SWAP12(13, 0), + 0x9c: SWAP13(14, 0), + 0x9d: SWAP14(15, 0), + 0x9e: SWAP15(16, 0), + 0x9f: SWAP16(17, 0), + 0xa0: LOG0(2, -2), + 0xa1: LOG1(3, -3), + 0xa2: LOG2(4, -4), + 0xa3: LOG3(5, -5), + 0xa4: LOG4(6, -6), + 0xf0: CREATE(3, -2), + 0xf1: CALL(7, -6), + 0xf2: CALLCODE(7, -6), + 0xf3: RETURN(2, -2), + 0xf4: DELEGATECALL(6, -5), + 0xf5: CREATE2(4, -3), + 0xfa: STATICCALL(6, -5), + 0xfd: REVERT(2, -2), + 0xfe: INVALID(0, 0), + 0xff: SELFDESTRUCT(1, -1), } From b0c86719eed77a3286fefddd727c8d7947d46c06 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 18:22:50 +0300 Subject: [PATCH 012/339] bump rust-toolchain to 1.63 (#576) * bump rust-toolchain to 1.63 [needed for const fn magic] * fix new clippy warnings, most changes is just adding `Eq` to derive, only others was avoiding reborrows and allowing a 'complex' type * change .err.expect to .expect_err, and dont bind to a unit value Co-authored-by: mriise --- actors/cron/src/state.rs | 2 +- actors/evm/src/interpreter/message.rs | 2 +- actors/evm/src/interpreter/output.rs | 4 ++-- actors/evm/src/interpreter/system.rs | 2 +- actors/market/src/deal.rs | 8 ++++---- actors/market/src/ext.rs | 2 +- actors/market/src/state.rs | 2 +- actors/miner/src/deadline_info.rs | 2 +- actors/miner/src/partition_state.rs | 4 ++-- actors/miner/src/state.rs | 2 +- actors/miner/src/types.rs | 16 ++++++++-------- actors/miner/tests/deadline_state_test.rs | 17 ++++++----------- actors/miner/tests/expiration_queue.rs | 2 +- actors/multisig/src/types.rs | 4 ++-- actors/paych/src/state.rs | 4 ++-- actors/paych/src/types.rs | 4 ++-- actors/power/src/state.rs | 2 +- actors/reward/src/state.rs | 4 ++-- actors/reward/src/types.rs | 2 +- actors/verifreg/src/types.rs | 2 +- runtime/src/actor_error.rs | 2 +- runtime/src/test_utils.rs | 3 ++- rust-toolchain | 2 +- state/src/check.rs | 2 +- test_vm/src/lib.rs | 4 ++-- 25 files changed, 48 insertions(+), 52 deletions(-) diff --git a/actors/cron/src/state.rs b/actors/cron/src/state.rs index 09646e061..9fbc6ce83 100644 --- a/actors/cron/src/state.rs +++ b/actors/cron/src/state.rs @@ -13,7 +13,7 @@ pub struct State { pub entries: Vec, } -#[derive(Clone, PartialEq, Debug, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct Entry { /// The actor to call (ID address) pub receiver: Address, diff --git a/actors/evm/src/interpreter/message.rs b/actors/evm/src/interpreter/message.rs index 66bf402db..200cdc84e 100644 --- a/actors/evm/src/interpreter/message.rs +++ b/actors/evm/src/interpreter/message.rs @@ -21,7 +21,7 @@ pub enum CallKind { /// The message describing an EVM call, /// including a zero-depth call from transaction origin. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Message { /// The kind of the call. For zero-depth calls `CallKind::Call` SHOULD be /// used. diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index eef10235d..a5634280d 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -7,7 +7,7 @@ use { }; /// Output of EVM execution. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] pub struct Output { /// EVM exited with this status code. pub status_code: StatusCode, @@ -29,7 +29,7 @@ impl Debug for Output { /// Message status code. #[must_use] -#[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)] pub enum StatusCode { /// Execution finished with success. #[strum(serialize = "success")] diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 7b0128e53..9bce708fa 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -23,7 +23,7 @@ pub enum StorageStatus { Deleted, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Call<'a> { Call(&'a Message), Create(&'a Message), diff --git a/actors/market/src/deal.rs b/actors/market/src/deal.rs index 99e165ead..8d9a86a68 100644 --- a/actors/market/src/deal.rs +++ b/actors/market/src/deal.rs @@ -25,7 +25,7 @@ pub fn is_piece_cid(c: &Cid) -> bool { && c.hash().size() == 32 } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Label { String(String), Bytes(Vec), @@ -90,7 +90,7 @@ impl Label { /// minimal deals that last for a long time. /// Note: ClientCollateralPerEpoch may not be needed and removed pending future confirmation. /// There will be a Minimum value for both client and provider deal collateral. -#[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct DealProposal { pub piece_cid: Cid, pub piece_size: PaddedPieceSize, @@ -139,7 +139,7 @@ impl DealProposal { } /// ClientDealProposal is a DealProposal signed by a client -#[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct ClientDealProposal { pub proposal: DealProposal, pub client_signature: Signature, @@ -147,7 +147,7 @@ pub struct ClientDealProposal { impl Cbor for ClientDealProposal {} -#[derive(Clone, Debug, PartialEq, Copy, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Copy, Serialize_tuple, Deserialize_tuple)] pub struct DealState { // -1 if not yet included in proven sector pub sector_start_epoch: ChainEpoch, diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index 911c34478..efddd9af1 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -28,7 +28,7 @@ pub mod verifreg { pub type UseBytesParams = BytesParams; pub type RestoreBytesParams = BytesParams; - #[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] + #[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct BytesParams { /// Address of verified client. pub address: Address, diff --git a/actors/market/src/state.rs b/actors/market/src/state.rs index 08e73e9ff..0c2b2a614 100644 --- a/actors/market/src/state.rs +++ b/actors/market/src/state.rs @@ -148,7 +148,7 @@ fn deal_get_payment_remaining( impl Cbor for State {} -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub(super) enum Permission { Invalid, ReadOnly, diff --git a/actors/miner/src/deadline_info.rs b/actors/miner/src/deadline_info.rs index c6afeeef5..c9b9e8040 100644 --- a/actors/miner/src/deadline_info.rs +++ b/actors/miner/src/deadline_info.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; /// "Deadline" refers to the window during which proofs may be submitted. /// Windows are non-overlapping ranges [Open, Close), but the challenge epoch for a window occurs /// before the window opens. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Copy, Clone)] +#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq, Copy, Clone)] #[serde(rename_all = "PascalCase")] pub struct DeadlineInfo { /// Epoch at which this info was calculated. diff --git a/actors/miner/src/partition_state.rs b/actors/miner/src/partition_state.rs index db85c651c..404edc1e4 100644 --- a/actors/miner/src/partition_state.rs +++ b/actors/miner/src/partition_state.rs @@ -391,7 +391,7 @@ impl Partition { })?; // Ensure these sectors actually belong to this partition. - let present = &*sector_numbers & &self.sectors; + let present = sector_numbers & &self.sectors; // Filter out terminated sectors. let live = &present - &self.terminated; @@ -756,7 +756,7 @@ impl Partition { let retracted_recovery_power = power_for_sectors(sector_size, &retracted_recovery_sectors); // Ignore skipped faults that are already faults or terminated. - let new_faults = &(&*skipped - &self.terminated) - &self.faults; + let new_faults = &(skipped - &self.terminated) - &self.faults; let new_fault_sectors = sectors.load_sector(&new_faults).map_err(|e| e.wrap("failed to load sectors"))?; diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 03124e277..ce91e4110 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -112,7 +112,7 @@ pub struct State { pub deadline_cron_active: bool, } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum CollisionPolicy { AllowCollisions, DenyCollisions, diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 03bad108d..8e6758918 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -238,7 +238,7 @@ pub struct WithdrawBalanceReturn { pub amount_withdrawn: TokenAmount, } -#[derive(Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct WorkerKeyChange { /// Must be an ID address pub new_worker: Address, @@ -249,14 +249,14 @@ pub type PreCommitSectorParams = SectorPreCommitInfo; impl Cbor for PreCommitSectorParams {} -#[derive(Debug, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct PreCommitSectorBatchParams { pub sectors: Vec, } impl Cbor for PreCommitSectorBatchParams {} -#[derive(Debug, Default, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct SectorPreCommitInfo { pub seal_proof: RegisteredSealProof, pub sector_number: SectorNumber, @@ -274,7 +274,7 @@ pub struct SectorPreCommitInfo { } /// Information stored on-chain for a pre-committed sector. -#[derive(Debug, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct SectorPreCommitOnChainInfo { pub info: SectorPreCommitInfo, #[serde(with = "bigint_ser")] @@ -289,7 +289,7 @@ pub struct SectorPreCommitOnChainInfo { } /// Information stored on-chain for a proven sector. -#[derive(Debug, Default, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct SectorOnChainInfo { pub sector_number: SectorNumber, /// The seal proof type implies the PoSt proofs @@ -325,7 +325,7 @@ pub struct SectorOnChainInfo { pub sector_key_cid: Option, } -#[derive(Debug, PartialEq, Copy, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize_tuple, Deserialize_tuple)] pub struct Fault { pub miner: Address, pub fault: ChainEpoch, @@ -342,7 +342,7 @@ pub struct ApplyRewardParams { impl Cbor for DisputeWindowedPoStParams {} -#[derive(Debug, PartialEq, Clone, Copy, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize_tuple, Deserialize_tuple)] pub struct DisputeWindowedPoStParams { pub deadline: u64, pub post_index: u64, // only one is allowed at a time to avoid loading too many sector infos. @@ -357,7 +357,7 @@ pub struct ProveCommitAggregateParams { pub aggregate_proof: Vec, } -#[derive(Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct ReplicaUpdate { pub sector_number: SectorNumber, pub deadline: u64, diff --git a/actors/miner/tests/deadline_state_test.rs b/actors/miner/tests/deadline_state_test.rs index 484562a22..0e2e609fc 100644 --- a/actors/miner/tests/deadline_state_test.rs +++ b/actors/miner/tests/deadline_state_test.rs @@ -546,8 +546,7 @@ fn fails_to_terminate_missing_sector() { assert!(ret.is_err()); let err = ret - .err() - .expect("can only terminate live sectors") + .expect_err("can only terminate live sectors") .downcast::() .expect("Invalid error"); assert_eq!(err.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); @@ -569,8 +568,7 @@ fn fails_to_terminate_missing_partition() { assert!(ret.is_err()); let err = ret - .err() - .expect("can only terminate existing partitions") + .expect_err("can only terminate existing partitions") .downcast::() .expect("Invalid error"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); @@ -592,8 +590,7 @@ fn fails_to_terminate_already_terminated_sector() { assert!(ret.is_err()); let err = ret - .err() - .expect("cannot terminate already terminated sector") + .expect_err("cannot terminate already terminated sector") .downcast::() .expect("Invalid error"); assert_eq!(err.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); @@ -658,7 +655,7 @@ fn cannot_pop_expired_sectors_before_proving() { // try to pop some expirations let ret = deadline.pop_expired_sectors(rt.store(), 9, QUANT_SPEC); assert!(ret.is_err()); - let err = ret.err().expect("cannot pop expired sectors from a partition with unproven sectors"); + let err = ret.expect_err("cannot pop expired sectors from a partition with unproven sectors"); assert!(err .to_string() @@ -1172,8 +1169,7 @@ fn cannot_declare_faults_in_missing_partitions() { ); let err = result - .err() - .expect("missing partition, should have failed") + .expect_err("missing partition, should have failed") .downcast::() .expect("Invalid error"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); @@ -1200,8 +1196,7 @@ fn cannot_declare_faults_recovered_in_missing_partitions() { ); let err = result - .err() - .expect("missing partition, should have failed") + .expect_err("missing partition, should have failed") .downcast::() .expect("Invalid error"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); diff --git a/actors/miner/tests/expiration_queue.rs b/actors/miner/tests/expiration_queue.rs index 38434cd9c..b4d58c233 100644 --- a/actors/miner/tests/expiration_queue.rs +++ b/actors/miner/tests/expiration_queue.rs @@ -677,7 +677,7 @@ fn rescheduling_all_expirations_as_faults_leaves_the_queue_empty_if_it_was_empty // all sectors already expire before epoch 15, nothing should change. let length = queue.amt.count(); - let _ = queue.reschedule_all_as_faults(15).unwrap(); + queue.reschedule_all_as_faults(15).unwrap(); assert_eq!(queue.amt.count(), length); } diff --git a/actors/multisig/src/types.rs b/actors/multisig/src/types.rs index 9b0f5e778..b4ba99669 100644 --- a/actors/multisig/src/types.rs +++ b/actors/multisig/src/types.rs @@ -37,7 +37,7 @@ impl Display for TxnID { } /// Transaction type used in multisig actor -#[derive(Clone, PartialEq, Debug, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct Transaction { pub to: Address, #[serde(with = "bigint_ser")] @@ -103,7 +103,7 @@ impl Cbor for ProposeParams {} impl Cbor for ProposeReturn {} /// Parameters for approve and cancel multisig functions. -#[derive(Clone, PartialEq, Debug, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct TxnIDParams { pub id: TxnID, /// Optional hash of proposal to ensure an operation can only apply to a diff --git a/actors/paych/src/state.rs b/actors/paych/src/state.rs index 93f1c679c..aeeb10dda 100644 --- a/actors/paych/src/state.rs +++ b/actors/paych/src/state.rs @@ -44,7 +44,7 @@ impl State { /// The Lane state tracks the latest (highest) voucher nonce used to merge the lane /// as well as the amount it has already redeemed. -#[derive(Default, Clone, PartialEq, Debug, Serialize_tuple, Deserialize_tuple)] +#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct LaneState { #[serde(with = "bigint_ser")] pub redeemed: BigInt, @@ -52,7 +52,7 @@ pub struct LaneState { } /// Specifies which `lane`s to be merged with what `nonce` on `channel_update` -#[derive(Default, Clone, Copy, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct Merge { pub lane: u64, pub nonce: u64, diff --git a/actors/paych/src/types.rs b/actors/paych/src/types.rs index ef6a37216..d5c79ecb7 100644 --- a/actors/paych/src/types.rs +++ b/actors/paych/src/types.rs @@ -30,7 +30,7 @@ pub struct ConstructorParams { /// A voucher is sent by `from` to `to` off-chain in order to enable /// `to` to redeem payments on-chain in the future -#[derive(Debug, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct SignedVoucher { /// ChannelAddr is the address of the payment channel this signed voucher is valid for pub channel_addr: Address, @@ -99,7 +99,7 @@ impl SignedVoucher { } /// Modular Verification method -#[derive(Debug, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct ModVerifyParams { pub actor: Address, pub method: MethodNum, diff --git a/actors/power/src/state.rs b/actors/power/src/state.rs index 5f5e31540..1203dfacc 100644 --- a/actors/power/src/state.rs +++ b/actors/power/src/state.rs @@ -385,7 +385,7 @@ pub fn epoch_key(e: ChainEpoch) -> BytesKey { impl Cbor for State {} -#[derive(Debug, Serialize_tuple, Deserialize_tuple, Clone, PartialEq)] +#[derive(Debug, Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Eq)] pub struct Claim { /// Miner's proof type used to determine minimum miner size pub window_post_proof_type: RegisteredPoStProof, diff --git a/actors/reward/src/state.rs b/actors/reward/src/state.rs index 489d42995..2ed75ea47 100644 --- a/actors/reward/src/state.rs +++ b/actors/reward/src/state.rs @@ -152,14 +152,14 @@ impl State { impl Cbor for State {} /// Defines vestion function type for reward actor. -#[derive(Clone, Debug, PartialEq, Copy, FromPrimitive, Serialize_repr, Deserialize_repr)] +#[derive(Clone, Debug, PartialEq, Eq, Copy, FromPrimitive, Serialize_repr, Deserialize_repr)] #[repr(u8)] pub enum VestingFunction { None = 0, Linear = 1, } -#[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct Reward { pub vesting_function: VestingFunction, pub start_epoch: ChainEpoch, diff --git a/actors/reward/src/types.rs b/actors/reward/src/types.rs index f36095b9e..42b733a77 100644 --- a/actors/reward/src/types.rs +++ b/actors/reward/src/types.rs @@ -6,7 +6,7 @@ use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser; use fvm_shared::econ::TokenAmount; -#[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct AwardBlockRewardParams { pub miner: Address, #[serde(with = "bigint_ser")] diff --git a/actors/verifreg/src/types.rs b/actors/verifreg/src/types.rs index e5b60093d..cca316c23 100644 --- a/actors/verifreg/src/types.rs +++ b/actors/verifreg/src/types.rs @@ -26,7 +26,7 @@ pub type AddVerifierClientParams = VerifierParams; /// We can introduce policy changes and replace this in the future. pub type DataCap = StoragePower; -#[derive(Clone, Debug, PartialEq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct BytesParams { /// Address of verified client. pub address: Address, diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index b3e3a93f4..899d63956 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -2,7 +2,7 @@ use fvm_shared::error::ExitCode; use thiserror::Error; /// The error type returned by actor method calls. -#[derive(Error, Debug, Clone, PartialEq)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] #[error("ActorError(exit_code: {exit_code:?}, msg: {msg})")] pub struct ActorError { /// The exit code for this invocation. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 64a3d66c8..6fbb54b64 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -115,6 +115,7 @@ pub struct MockRuntime { pub caller_type: Cid, pub origin: Address, pub value_received: TokenAmount, + #[allow(clippy::type_complexity)] pub hash_func: Box [u8; 32]>, pub network_version: NetworkVersion, @@ -1239,7 +1240,7 @@ pub fn blake2b_256(data: &[u8]) -> [u8; 32] { } // multihash library doesn't support poseidon hashing, so we fake it -#[derive(Clone, Copy, Debug, Eq, Multihash, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Multihash)] #[mh(alloc_size = 64)] enum MhCode { #[mh(code = 0xb401, hasher = multihash::Sha2_256)] diff --git a/rust-toolchain b/rust-toolchain index 43eb9bc68..7cc6ef413 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.59.0 \ No newline at end of file +1.63.0 \ No newline at end of file diff --git a/state/src/check.rs b/state/src/check.rs index 411a71518..f3469d02b 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -51,7 +51,7 @@ use fil_actor_verifreg::testing as verifreg; /// Value type of the top level of the state tree. /// Represents the on-chain state of a single actor. -#[derive(Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Debug)] +#[derive(Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Eq, Debug)] pub struct Actor { /// CID representing the code associated with the actor pub code: Cid, diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index c581e0464..c80741628 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1018,13 +1018,13 @@ impl RuntimePolicy for InvocationCtx<'_, '_> { } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct MessageResult { pub code: ExitCode, pub ret: RawBytes, } -#[derive(Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Debug)] +#[derive(Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Eq, Debug)] pub struct Actor { pub code: Cid, pub head: Cid, From c49f4e14653ea46224f9b37b565e4ed664f67df2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 12:12:55 +0300 Subject: [PATCH 013/339] wip jmptable based interpreter --- actors/evm/src/interpreter/execution.rs | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 2fafbcecd..5afc1cf3f 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -37,6 +37,62 @@ impl ExecutionState { } } +struct Machine<'r, BS: Blockstore, RT: Runtime> { + system: &'r System<'r, BS, RT>, + runtime: &'r mut ExecutionState, + bytecode: &'r Bytecode<'r>, + pc: usize, + reverted: bool, +} + +enum ControlFlow { + Advance, + Continue, + Break, +} + +type Instruction = fn(&mut M) -> Result; + +impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { + fn new(system: &'r System<'r, BS, RT>, + runtime: &'r mut ExecutionState, + bytecode: &'r Bytecode, + ) -> Self { + Machine { + system: system, + runtime: runtime, + bytecode: bytecode, + pc: 0, + reverted: false, + } + } + + fn step(&mut self) -> Result { + let op = OpCode::try_from(self.bytecode[self.pc])?; + Machine::<'r, BS, RT>::dispatch(op)(self) + } + + const fn jmptable() -> [Instruction>; 256] { + let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; + table[OpCode::STOP as usize] = Machine::<'r, BS, RT>::ins_stop; + table + } + + const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); + + const fn dispatch(op: OpCode) -> Instruction> { + Self::JMPTABLE[op as usize] + } + + fn ins_undefined(&mut self) -> Result { + todo!(); + } + + fn ins_stop(&mut self) -> Result { + todo!(); + } +} + pub fn execute<'r, BS: Blockstore, RT: Runtime>( bytecode: &Bytecode, runtime: &mut ExecutionState, From 4b01f17d954e89ef82a19134b1c1b12c946b2380 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 13:02:27 +0300 Subject: [PATCH 014/339] flesh out machine scaffolding --- actors/evm/src/interpreter/execution.rs | 760 +++++++++++++++++++++++- 1 file changed, 748 insertions(+), 12 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 5afc1cf3f..44d44730e 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -51,7 +51,30 @@ enum ControlFlow { Break, } -type Instruction = fn(&mut M) -> Result; +type Instruction = fn(*mut M) -> Result; + +macro_rules! def_jmptable { + ($($op:ident: $ins:ident),*) => { + let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; + $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$ins;)* + table + } +} + +macro_rules! def_ins1 { + ($ins:ident ($arg:ident) $body:block) => { + fn $ins(p: *mut Self) -> Result { + let $arg :&mut Self = unsafe { p.as_mut().unwrap() }; + $body + } + } +} + +macro_rules! def_ins { + ($($ins:ident ($arg:ident) $body:block)*) => { + $(def_ins1! { $ins($arg) $body })* + } +} impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { fn new(system: &'r System<'r, BS, RT>, @@ -72,24 +95,737 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { Machine::<'r, BS, RT>::dispatch(op)(self) } + // Beware, dragons! + fn dispatch(op: OpCode) -> Instruction> { + Self::JMPTABLE[op as usize] + } + const fn jmptable() -> [Instruction>; 256] { - let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; - table[OpCode::STOP as usize] = Machine::<'r, BS, RT>::ins_stop; - table + def_jmptable! { + STOP: ins_stop, + ADD: ins_add, + MUL: ins_mul, + SUB: ins_sub, + DIV: ins_div, + SDIV: ins_sdiv, + MOD: ins_mod, + SMOD: ins_smod, + ADDMOD: ins_addmod, + MULMOD: ins_mulmod, + EXP: ins_exp, + SIGNEXTEND: ins_signextend, + LT: ins_lt, + GT: ins_gt, + SLT: ins_slt, + SGT: ins_sgt, + EQ: ins_eq, + ISZERO: ins_iszero, + AND: ins_and, + OR: ins_or, + XOR: ins_xor, + NOT: ins_not, + BYTE: ins_byte, + SHL: ins_shl, + SHR: ins_shr, + SAR: ins_sar, + KECCAK256: ins_keccak256, + ADDRESS: ins_address, + BALANCE: ins_balance, + ORIGIN: ins_origin, + CALLER: ins_caller, + CALLVALUE: ins_callvalue, + CALLDATALOAD: ins_calldataload, + CALLDATASIZE: ins_calldatasize, + CALLDATACOPY: ins_calldatacopy, + CODESIZE: ins_codesize, + CODECOPY: ins_codecopy, + GASPRICE: ins_gasprice, + EXTCODESIZE: ins_extcodesize, + EXTCODECOPY: ins_extcodecopy, + RETURNDATASIZE: ins_returndatasize, + RETURNDATACOPY: ins_returndatacopy, + EXTCODEHASH: ins_extcodehash, + BLOCKHASH: ins_blockhash, + COINBASE: ins_coinbase, + TIMESTAMP: ins_timestamp, + NUMBER: ins_number, + DIFFICULTY: ins_difficulty, + GASLIMIT: ins_gaslimit, + CHAINID: ins_chainid, + SELFBALANCE: ins_selfbalance, + BASEFEE: ins_basefee, + POP: ins_pop, + MLOAD: ins_mload, + MSTORE: ins_mstore, + MSTORE8: ins_mstore8, + SLOAD: ins_sload, + SSTORE: ins_sstore, + JUMP: ins_jump, + JUMPI: ins_jumpi, + PC: ins_pc, + MSIZE: ins_msize, + GAS: ins_gas, + JUMPDEST: ins_jumpdest, + PUSH1: ins_push1, + PUSH2: ins_push2, + PUSH3: ins_push3, + PUSH4: ins_push4, + PUSH5: ins_push5, + PUSH6: ins_push6, + PUSH7: ins_push7, + PUSH8: ins_push8, + PUSH9: ins_push9, + PUSH10: ins_push10, + PUSH11: ins_push11, + PUSH12: ins_push12, + PUSH13: ins_push13, + PUSH14: ins_push14, + PUSH15: ins_push15, + PUSH16: ins_push16, + PUSH17: ins_push17, + PUSH18: ins_push18, + PUSH19: ins_push19, + PUSH20: ins_push20, + PUSH21: ins_push21, + PUSH22: ins_push22, + PUSH23: ins_push23, + PUSH24: ins_push24, + PUSH25: ins_push25, + PUSH26: ins_push26, + PUSH27: ins_push27, + PUSH28: ins_push28, + PUSH29: ins_push29, + PUSH30: ins_push30, + PUSH31: ins_push31, + PUSH32: ins_push32, + DUP1: ins_dup1, + DUP2: ins_dup2, + DUP3: ins_dup3, + DUP4: ins_dup4, + DUP5: ins_dup5, + DUP6: ins_dup6, + DUP7: ins_dup7, + DUP8: ins_dup8, + DUP9: ins_dup9, + DUP10: ins_dup10, + DUP11: ins_dup11, + DUP12: ins_dup12, + DUP13: ins_dup13, + DUP14: ins_dup14, + DUP15: ins_dup15, + DUP16: ins_dup16, + SWAP1: ins_swap1, + SWAP2: ins_swap2, + SWAP3: ins_swap3, + SWAP4: ins_swap4, + SWAP5: ins_swap5, + SWAP6: ins_swap6, + SWAP7: ins_swap7, + SWAP8: ins_swap8, + SWAP9: ins_swap9, + SWAP10: ins_swap10, + SWAP11: ins_swap11, + SWAP12: ins_swap12, + SWAP13: ins_swap13, + SWAP14: ins_swap14, + SWAP15: ins_swap15, + SWAP16: ins_swap16, + LOG0: ins_log0, + LOG1: ins_log1, + LOG2: ins_log2, + LOG3: ins_log3, + LOG4: ins_log4, + CREATE: ins_create, + CALL: ins_call, + CALLCODE: ins_callcode, + RETURN: ins_return, + DELEGATECALL: ins_delegatecall, + CREATE2: ins_create2, + STATICCALL: ins_staticcall, + REVERT: ins_revert, + INVALID: ins_invalid, + SELFDESTRUCT: ins_selfdestruct + } } const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); - const fn dispatch(op: OpCode) -> Instruction> { - Self::JMPTABLE[op as usize] - } + def_ins! { + ins_undefined(_m) { + todo!() + } - fn ins_undefined(&mut self) -> Result { - todo!(); - } + ins_stop(_m) { + todo!() + } + + ins_add(_m) { + todo!() + } + + ins_mul(_m) { + todo!() + } + + ins_sub(_m) { + todo!() + } + + ins_div(_m) { + todo!() + } + + ins_sdiv(_m) { + todo!() + } + + ins_mod(_m) { + todo!() + } + + ins_smod(_m) { + todo!() + } + + ins_addmod(_m) { + todo!() + } + + ins_mulmod(_m) { + todo!() + } + + ins_exp(_m) { + todo!() + } + + ins_signextend(_m) { + todo!() + } + + ins_lt(_m) { + todo!() + } + + ins_gt(_m) { + todo!() + } + + ins_slt(_m) { + todo!() + } + + ins_sgt(_m) { + todo!() + } + + ins_eq(_m) { + todo!() + } + + ins_iszero(_m) { + todo!() + } + + ins_and(_m) { + todo!() + } + + ins_or(_m) { + todo!() + } + + ins_xor(_m) { + todo!() + } + + ins_not(_m) { + todo!() + } + + ins_byte(_m) { + todo!() + } + + ins_shl(_m) { + todo!() + } + + ins_shr(_m) { + todo!() + } + + ins_sar(_m) { + todo!() + } + + ins_keccak256(_m) { + todo!() + } + + ins_address(_m) { + todo!() + } + + ins_balance(_m) { + todo!() + } + + ins_origin(_m) { + todo!() + } + + ins_caller(_m) { + todo!() + } + + ins_callvalue(_m) { + todo!() + } + + ins_calldataload(_m) { + todo!() + } + + ins_calldatasize(_m) { + todo!() + } + + ins_calldatacopy(_m) { + todo!() + } + + ins_codesize(_m) { + todo!() + } + + ins_codecopy(_m) { + todo!() + } + + ins_gasprice(_m) { + todo!() + } + + ins_extcodesize(_m) { + todo!() + } + + ins_extcodecopy(_m) { + todo!() + } + + ins_returndatasize(_m) { + todo!() + } + + ins_returndatacopy(_m) { + todo!() + } + + ins_extcodehash(_m) { + todo!() + } + + ins_blockhash(_m) { + todo!() + } + + ins_coinbase(_m) { + todo!() + } + + ins_timestamp(_m) { + todo!() + } + + ins_number(_m) { + todo!() + } + + ins_difficulty(_m) { + todo!() + } + + ins_gaslimit(_m) { + todo!() + } + + ins_chainid(_m) { + todo!() + } + + ins_selfbalance(_m) { + todo!() + } + + ins_basefee(_m) { + todo!() + } + + ins_pop(_m) { + todo!() + } + + ins_mload(_m) { + todo!() + } + + ins_mstore(_m) { + todo!() + } + + ins_mstore8(_m) { + todo!() + } + + ins_sload(_m) { + todo!() + } + + ins_sstore(_m) { + todo!() + } + + ins_jump(_m) { + todo!() + } + + ins_jumpi(_m) { + todo!() + } + + ins_pc(_m) { + todo!() + } + + ins_msize(_m) { + todo!() + } + + ins_gas(_m) { + todo!() + } + + ins_jumpdest(_m) { + todo!() + } + + ins_push1(_m) { + todo!() + } + + ins_push2(_m) { + todo!() + } - fn ins_stop(&mut self) -> Result { - todo!(); + ins_push3(_m) { + todo!() + } + + ins_push4(_m) { + todo!() + } + + ins_push5(_m) { + todo!() + } + + ins_push6(_m) { + todo!() + } + + ins_push7(_m) { + todo!() + } + + ins_push8(_m) { + todo!() + } + + ins_push9(_m) { + todo!() + } + + ins_push10(_m) { + todo!() + } + + ins_push11(_m) { + todo!() + } + + ins_push12(_m) { + todo!() + } + + ins_push13(_m) { + todo!() + } + + ins_push14(_m) { + todo!() + } + + ins_push15(_m) { + todo!() + } + + ins_push16(_m) { + todo!() + } + + ins_push17(_m) { + todo!() + } + + ins_push18(_m) { + todo!() + } + + ins_push19(_m) { + todo!() + } + + ins_push20(_m) { + todo!() + } + + ins_push21(_m) { + todo!() + } + + ins_push22(_m) { + todo!() + } + + ins_push23(_m) { + todo!() + } + + ins_push24(_m) { + todo!() + } + + ins_push25(_m) { + todo!() + } + + ins_push26(_m) { + todo!() + } + + ins_push27(_m) { + todo!() + } + + ins_push28(_m) { + todo!() + } + + ins_push29(_m) { + todo!() + } + + ins_push30(_m) { + todo!() + } + + ins_push31(_m) { + todo!() + } + + ins_push32(_m) { + todo!() + } + + ins_dup1(_m) { + todo!() + } + + ins_dup2(_m) { + todo!() + } + + ins_dup3(_m) { + todo!() + } + + ins_dup4(_m) { + todo!() + } + + ins_dup5(_m) { + todo!() + } + + ins_dup6(_m) { + todo!() + } + + ins_dup7(_m) { + todo!() + } + + ins_dup8(_m) { + todo!() + } + + ins_dup9(_m) { + todo!() + } + + ins_dup10(_m) { + todo!() + } + + ins_dup11(_m) { + todo!() + } + + ins_dup12(_m) { + todo!() + } + + ins_dup13(_m) { + todo!() + } + + ins_dup14(_m) { + todo!() + } + + ins_dup15(_m) { + todo!() + } + + ins_dup16(_m) { + todo!() + } + + ins_swap1(_m) { + todo!() + } + + ins_swap2(_m) { + todo!() + } + + ins_swap3(_m) { + todo!() + } + + ins_swap4(_m) { + todo!() + } + + ins_swap5(_m) { + todo!() + } + + ins_swap6(_m) { + todo!() + } + + ins_swap7(_m) { + todo!() + } + + ins_swap8(_m) { + todo!() + } + + ins_swap9(_m) { + todo!() + } + + ins_swap10(_m) { + todo!() + } + + ins_swap11(_m) { + todo!() + } + + ins_swap12(_m) { + todo!() + } + + ins_swap13(_m) { + todo!() + } + + ins_swap14(_m) { + todo!() + } + + ins_swap15(_m) { + todo!() + } + + ins_swap16(_m) { + todo!() + } + + ins_log0(_m) { + todo!() + } + + ins_log1(_m) { + todo!() + } + + ins_log2(_m) { + todo!() + } + + ins_log3(_m) { + todo!() + } + + ins_log4(_m) { + todo!() + } + + ins_create(_m) { + todo!() + } + + ins_call(_m) { + todo!() + } + + ins_callcode(_m) { + todo!() + } + + ins_return(_m) { + todo!() + } + + ins_delegatecall(_m) { + todo!() + } + + ins_create2(_m) { + todo!() + } + + ins_staticcall(_m) { + todo!() + } + + ins_revert(_m) { + todo!() + } + + ins_invalid(_m) { + todo!() + } + + ins_selfdestruct(_m) { + todo!() + } } } From 5b3db72be9e01f9607d34b13e3fc5d77056bb2d0 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 14:19:00 +0300 Subject: [PATCH 015/339] implement interpreter guts --- actors/evm/src/interpreter/execution.rs | 926 +++++++++--------- .../interpreter/instructions/arithmetic.rs | 14 +- .../evm/src/interpreter/instructions/call.rs | 4 +- .../src/interpreter/instructions/control.rs | 4 +- .../evm/src/interpreter/instructions/hash.rs | 2 +- .../src/interpreter/instructions/memory.rs | 6 +- actors/evm/src/interpreter/output.rs | 4 - 7 files changed, 476 insertions(+), 484 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 44d44730e..fc658d6c5 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -46,9 +46,9 @@ struct Machine<'r, BS: Blockstore, RT: Runtime> { } enum ControlFlow { - Advance, Continue, - Break, + Jump, + Exit, } type Instruction = fn(*mut M) -> Result; @@ -64,7 +64,7 @@ macro_rules! def_jmptable { macro_rules! def_ins1 { ($ins:ident ($arg:ident) $body:block) => { fn $ins(p: *mut Self) -> Result { - let $arg :&mut Self = unsafe { p.as_mut().unwrap() }; + let $arg: &mut Self = unsafe { p.as_mut().unwrap() }; $body } } @@ -77,9 +77,9 @@ macro_rules! def_ins { } impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { - fn new(system: &'r System<'r, BS, RT>, - runtime: &'r mut ExecutionState, - bytecode: &'r Bytecode, + pub fn new(system: &'r System<'r, BS, RT>, + runtime: &'r mut ExecutionState, + bytecode: &'r Bytecode, ) -> Self { Machine { system: system, @@ -90,6 +90,26 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } } + pub fn execute(&mut self) -> Result<(), StatusCode> { + loop { + if self.pc >= self.bytecode.len() { + break; + } + + match self.step()? { + ControlFlow::Continue => { + self.pc += 1; + } + ControlFlow::Jump => {} + ControlFlow::Exit => { + break; + } + }; + } + + Ok(()) + } + fn step(&mut self) -> Result { let op = OpCode::try_from(self.bytecode[self.pc])?; Machine::<'r, BS, RT>::dispatch(op)(self) @@ -252,761 +272,739 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { def_ins! { ins_undefined(_m) { - todo!() + Err(StatusCode::UndefinedInstruction) } ins_stop(_m) { - todo!() + Ok(ControlFlow::Exit) } - ins_add(_m) { - todo!() + ins_add(m) { + arithmetic::add(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_mul(_m) { - todo!() + ins_mul(m) { + arithmetic::mul(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_sub(_m) { - todo!() + ins_sub(m) { + arithmetic::sub(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_div(_m) { - todo!() + ins_div(m) { + arithmetic::div(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_sdiv(_m) { - todo!() + ins_sdiv(m) { + arithmetic::sdiv(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_mod(_m) { - todo!() + ins_mod(m) { + arithmetic::modulo(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_smod(_m) { - todo!() + ins_smod(m) { + arithmetic::smod(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_addmod(_m) { - todo!() + ins_addmod(m) { + arithmetic::addmod(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_mulmod(_m) { - todo!() + ins_mulmod(m) { + arithmetic::mulmod(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_exp(_m) { - todo!() + ins_exp(m) { + arithmetic::exp(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_signextend(_m) { - todo!() + ins_signextend(m) { + arithmetic::signextend(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_lt(_m) { - todo!() + ins_lt(m) { + boolean::lt(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_gt(_m) { - todo!() + ins_gt(m) { + boolean::gt(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_slt(_m) { - todo!() + ins_slt(m) { + boolean::slt(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_sgt(_m) { - todo!() + ins_sgt(m) { + boolean::sgt(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_eq(_m) { - todo!() + ins_eq(m) { + boolean::eq(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_iszero(_m) { - todo!() + ins_iszero(m) { + boolean::iszero(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_and(_m) { - todo!() + ins_and(m) { + boolean::and(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_or(_m) { - todo!() + ins_or(m) { + boolean::or(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_xor(_m) { - todo!() + ins_xor(m) { + boolean::xor(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_not(_m) { - todo!() + ins_not(m) { + boolean::not(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_byte(_m) { - todo!() + ins_byte(m) { + bitwise::byte(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_shl(_m) { - todo!() + ins_shl(m) { + bitwise::shl(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_shr(_m) { - todo!() + ins_shr(m) { + bitwise::shr(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_sar(_m) { - todo!() + ins_sar(m) { + bitwise::sar(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_keccak256(_m) { - todo!() + ins_keccak256(m) { + hash::keccak256(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_address(_m) { - todo!() + ins_address(m) { + context::address(m.runtime, m.system); + Ok(ControlFlow::Continue) } - ins_balance(_m) { - todo!() + ins_balance(m) { + storage::balance(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_origin(_m) { - todo!() + ins_origin(m) { + context::origin(m.runtime, m.system); + Ok(ControlFlow::Continue) } - ins_caller(_m) { - todo!() + ins_caller(m) { + context::caller(m.runtime, m.system); + Ok(ControlFlow::Continue) } - ins_callvalue(_m) { - todo!() + ins_callvalue(m) { + context::call_value(m.runtime, m.system); + Ok(ControlFlow::Continue) } - ins_calldataload(_m) { - todo!() + ins_calldataload(m) { + call::calldataload(m.runtime); + Ok(ControlFlow::Continue) } - ins_calldatasize(_m) { - todo!() + ins_calldatasize(m) { + call::calldatasize(m.runtime); + Ok(ControlFlow::Continue) } - ins_calldatacopy(_m) { - todo!() + ins_calldatacopy(m) { + call::calldatacopy(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_codesize(_m) { - todo!() + ins_codesize(m) { + call::codesize(&mut m.runtime.stack, m.bytecode.as_ref()); + Ok(ControlFlow::Continue) } - ins_codecopy(_m) { - todo!() + ins_codecopy(m) { + call::codecopy(m.runtime, m.bytecode.as_ref())?; + Ok(ControlFlow::Continue) } - ins_gasprice(_m) { - todo!() + ins_gasprice(m) { + context::gas_price(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_extcodesize(_m) { - todo!() + ins_extcodesize(m) { + storage::extcodesize(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_extcodecopy(_m) { - todo!() + ins_extcodecopy(m) { + memory::extcodecopy(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_returndatasize(_m) { - todo!() + ins_returndatasize(m) { + control::returndatasize(m.runtime); + Ok(ControlFlow::Continue) } - ins_returndatacopy(_m) { - todo!() + ins_returndatacopy(m) { + control::returndatacopy(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_extcodehash(_m) { - todo!() + ins_extcodehash(m) { + storage::extcodehash(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_blockhash(_m) { - todo!() + ins_blockhash(m) { + context::blockhash(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_coinbase(_m) { - todo!() + ins_coinbase(m) { + context::coinbase(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_timestamp(_m) { - todo!() + ins_timestamp(m) { + context::timestamp(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_number(_m) { - todo!() + ins_number(m) { + context::block_number(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_difficulty(_m) { - todo!() + ins_difficulty(m) { + context::difficulty(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_gaslimit(_m) { - todo!() + ins_gaslimit(m) { + context::gas_limit(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_chainid(_m) { - todo!() + ins_chainid(m) { + context::chain_id(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_selfbalance(_m) { - todo!() + ins_selfbalance(m) { + storage::selfbalance(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_basefee(_m) { - todo!() + ins_basefee(m) { + context::base_fee(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_pop(_m) { - todo!() + ins_pop(m) { + stack::pop(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_mload(_m) { - todo!() + ins_mload(m) { + memory::mload(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_mstore(_m) { - todo!() + ins_mstore(m) { + memory::mstore(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_mstore8(_m) { - todo!() + ins_mstore8(m) { + memory::mstore8(m.runtime)?; + Ok(ControlFlow::Continue) } - ins_sload(_m) { - todo!() + ins_sload(m) { + storage::sload(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_sstore(_m) { - todo!() + ins_sstore(m) { + storage::sstore(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } - ins_jump(_m) { - todo!() + ins_jump(m) { + m.pc = control::jump(&mut m.runtime.stack, m.bytecode)?; + Ok(ControlFlow::Jump) } - ins_jumpi(_m) { - todo!() + ins_jumpi(m) { + if let Some(dest) = control::jumpi(&mut m.runtime.stack, m.bytecode)? { + m.pc = dest; + Ok(ControlFlow::Jump) + } else { + Ok(ControlFlow::Continue) + } } - ins_pc(_m) { - todo!() + ins_pc(m) { + control::pc(&mut m.runtime.stack, m.pc); + Ok(ControlFlow::Continue) } - ins_msize(_m) { - todo!() + ins_msize(m) { + memory::msize(m.runtime); + Ok(ControlFlow::Continue) } - ins_gas(_m) { - todo!() + ins_gas(m) { + control::gas(m.runtime); + Ok(ControlFlow::Continue) } ins_jumpdest(_m) { - todo!() + // marker opcode for valid jumps addresses + Ok(ControlFlow::Continue) } - ins_push1(_m) { - todo!() + ins_push1(m) { + m.pc += push::<1>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push2(_m) { - todo!() + ins_push2(m) { + m.pc += push::<2>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push3(_m) { - todo!() + ins_push3(m) { + m.pc += push::<3>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push4(_m) { - todo!() + ins_push4(m) { + m.pc += push::<4>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push5(_m) { - todo!() + ins_push5(m) { + m.pc += push::<5>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push6(_m) { - todo!() + ins_push6(m) { + m.pc += push::<6>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push7(_m) { - todo!() + ins_push7(m) { + m.pc += push::<7>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push8(_m) { - todo!() + ins_push8(m) { + m.pc += push::<8>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push9(_m) { - todo!() + ins_push9(m) { + m.pc += push::<9>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push10(_m) { - todo!() + ins_push10(m) { + m.pc += push::<10>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push11(_m) { - todo!() + ins_push11(m) { + m.pc += push::<11>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push12(_m) { - todo!() + ins_push12(m) { + m.pc += push::<12>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push13(_m) { - todo!() + ins_push13(m) { + m.pc += push::<13>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push14(_m) { - todo!() + ins_push14(m) { + m.pc += push::<14>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push15(_m) { - todo!() + ins_push15(m) { + m.pc += push::<15>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push16(_m) { - todo!() + ins_push16(m) { + m.pc += push::<16>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push17(_m) { - todo!() + ins_push17(m) { + m.pc += push::<17>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push18(_m) { - todo!() + ins_push18(m) { + m.pc += push::<18>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push19(_m) { - todo!() + ins_push19(m) { + m.pc += push::<19>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push20(_m) { - todo!() + ins_push20(m) { + m.pc += push::<20>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push21(_m) { - todo!() + ins_push21(m) { + m.pc += push::<21>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push22(_m) { - todo!() + ins_push22(m) { + m.pc += push::<22>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push23(_m) { - todo!() + ins_push23(m) { + m.pc += push::<23>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push24(_m) { - todo!() + ins_push24(m) { + m.pc += push::<24>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push25(_m) { - todo!() + ins_push25(m) { + m.pc += push::<25>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push26(_m) { - todo!() + ins_push26(m) { + m.pc += push::<26>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push27(_m) { - todo!() + ins_push27(m) { + m.pc += push::<27>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push28(_m) { - todo!() + ins_push28(m) { + m.pc += push::<28>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push29(_m) { - todo!() + ins_push29(m) { + m.pc += push::<29>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push30(_m) { - todo!() + ins_push30(m) { + m.pc += push::<30>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push31(_m) { - todo!() + ins_push31(m) { + m.pc += push::<31>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_push32(_m) { - todo!() + ins_push32(m) { + m.pc += push::<32>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); + Ok(ControlFlow::Continue) } - ins_dup1(_m) { - todo!() + ins_dup1(m) { + dup::<1>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup2(_m) { - todo!() + ins_dup2(m) { + dup::<2>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup3(_m) { - todo!() + ins_dup3(m) { + dup::<3>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup4(_m) { - todo!() + ins_dup4(m) { + dup::<4>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup5(_m) { - todo!() + ins_dup5(m) { + dup::<5>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup6(_m) { - todo!() + ins_dup6(m) { + dup::<6>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup7(_m) { - todo!() + ins_dup7(m) { + dup::<7>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup8(_m) { - todo!() + ins_dup8(m) { + dup::<8>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup9(_m) { - todo!() + ins_dup9(m) { + dup::<9>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup10(_m) { - todo!() + ins_dup10(m) { + dup::<10>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup11(_m) { - todo!() + ins_dup11(m) { + dup::<11>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup12(_m) { - todo!() + ins_dup12(m) { + dup::<12>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup13(_m) { - todo!() + ins_dup13(m) { + dup::<13>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup14(_m) { - todo!() + ins_dup14(m) { + dup::<14>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup15(_m) { - todo!() + ins_dup15(m) { + dup::<15>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_dup16(_m) { - todo!() + ins_dup16(m) { + dup::<16>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap1(_m) { - todo!() + ins_swap1(m) { + swap::<1>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap2(_m) { - todo!() + ins_swap2(m) { + swap::<2>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap3(_m) { - todo!() + ins_swap3(m) { + swap::<3>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap4(_m) { - todo!() + ins_swap4(m) { + swap::<4>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap5(_m) { - todo!() + ins_swap5(m) { + swap::<5>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap6(_m) { - todo!() + ins_swap6(m) { + swap::<6>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap7(_m) { - todo!() + ins_swap7(m) { + swap::<7>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap8(_m) { - todo!() + ins_swap8(m) { + swap::<8>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap9(_m) { - todo!() + ins_swap9(m) { + swap::<9>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap10(_m) { - todo!() + ins_swap10(m) { + swap::<10>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap11(_m) { - todo!() + ins_swap11(m) { + swap::<11>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap12(_m) { - todo!() + ins_swap12(m) { + swap::<12>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap13(_m) { - todo!() + ins_swap13(m) { + swap::<13>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap14(_m) { - todo!() + ins_swap14(m) { + swap::<14>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap15(_m) { - todo!() + ins_swap15(m) { + swap::<15>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_swap16(_m) { - todo!() + ins_swap16(m) { + swap::<16>(&mut m.runtime.stack); + Ok(ControlFlow::Continue) } - ins_log0(_m) { - todo!() + ins_log0(m) { + log(m.runtime, m.system, 0)?; + Ok(ControlFlow::Continue) } - ins_log1(_m) { - todo!() + ins_log1(m) { + log(m.runtime, m.system, 1)?; + Ok(ControlFlow::Continue) } - ins_log2(_m) { - todo!() + ins_log2(m) { + log(m.runtime, m.system, 2)?; + Ok(ControlFlow::Continue) } - ins_log3(_m) { - todo!() + ins_log3(m) { + log(m.runtime, m.system, 3)?; + Ok(ControlFlow::Continue) } - ins_log4(_m) { - todo!() + ins_log4(m) { + log(m.runtime, m.system, 4)?; + Ok(ControlFlow::Continue) } - ins_create(_m) { - todo!() + ins_create(m) { + storage::create(m.runtime, m.system, false)?; + Ok(ControlFlow::Continue) } - ins_call(_m) { - todo!() + ins_call(m) { + call::call(m.runtime, m.system, CallKind::Call, false)?; + Ok(ControlFlow::Continue) } - ins_callcode(_m) { - todo!() + ins_callcode(m) { + call::call(m.runtime, m.system, CallKind::CallCode, false)?; + Ok(ControlFlow::Continue) } - ins_return(_m) { - todo!() + ins_return(m) { + control::ret(m.runtime)?; + Ok(ControlFlow::Exit) } - ins_delegatecall(_m) { - todo!() + ins_delegatecall(m) { + call::call(m.runtime, m.system, CallKind::DelegateCall, false)?; + Ok(ControlFlow::Continue) } - ins_create2(_m) { - todo!() + ins_create2(m) { + storage::create(m.runtime, m.system, true)?; + Ok(ControlFlow::Continue) } - ins_staticcall(_m) { - todo!() + ins_staticcall(m) { + call::call(m.runtime, m.system, CallKind::Call, true)?; + Ok(ControlFlow::Continue) } - ins_revert(_m) { - todo!() + ins_revert(m) { + control::ret(m.runtime)?; + m.reverted = true; + Ok(ControlFlow::Exit) } ins_invalid(_m) { - todo!() + Err(StatusCode::InvalidInstruction) } - ins_selfdestruct(_m) { - todo!() + ins_selfdestruct(m) { + storage::selfdestruct(m.runtime, m.system)?; + Ok(ControlFlow::Continue) } } } pub fn execute<'r, BS: Blockstore, RT: Runtime>( - bytecode: &Bytecode, - runtime: &mut ExecutionState, + bytecode: &'r Bytecode, + runtime: &'r mut ExecutionState, system: &'r System<'r, BS, RT>, ) -> Result { - let mut pc = 0; // program counter - let mut reverted = false; - - loop { - if pc >= bytecode.len() { - break; - } - - let op = OpCode::try_from(bytecode[pc])?; - match op { - OpCode::STOP => break, - OpCode::ADD => arithmetic::add(&mut runtime.stack), - OpCode::MUL => arithmetic::mul(&mut runtime.stack), - OpCode::SUB => arithmetic::sub(&mut runtime.stack), - OpCode::DIV => arithmetic::div(&mut runtime.stack), - OpCode::SDIV => arithmetic::sdiv(&mut runtime.stack), - OpCode::MOD => arithmetic::modulo(&mut runtime.stack), - OpCode::SMOD => arithmetic::smod(&mut runtime.stack), - OpCode::ADDMOD => arithmetic::addmod(&mut runtime.stack), - OpCode::MULMOD => arithmetic::mulmod(&mut runtime.stack), - OpCode::EXP => arithmetic::exp(runtime)?, - OpCode::SIGNEXTEND => arithmetic::signextend(&mut runtime.stack), - OpCode::LT => boolean::lt(&mut runtime.stack), - OpCode::GT => boolean::gt(&mut runtime.stack), - OpCode::SLT => boolean::slt(&mut runtime.stack), - OpCode::SGT => boolean::sgt(&mut runtime.stack), - OpCode::EQ => boolean::eq(&mut runtime.stack), - OpCode::ISZERO => boolean::iszero(&mut runtime.stack), - OpCode::AND => boolean::and(&mut runtime.stack), - OpCode::OR => boolean::or(&mut runtime.stack), - OpCode::XOR => boolean::xor(&mut runtime.stack), - OpCode::NOT => boolean::not(&mut runtime.stack), - OpCode::BYTE => bitwise::byte(&mut runtime.stack), - OpCode::SHL => bitwise::shl(&mut runtime.stack), - OpCode::SHR => bitwise::shr(&mut runtime.stack), - OpCode::SAR => bitwise::sar(&mut runtime.stack), - OpCode::KECCAK256 => hash::keccak256(runtime)?, - OpCode::ADDRESS => context::address(runtime, system), - OpCode::BALANCE => storage::balance(runtime, system)?, - OpCode::CALLER => context::caller(runtime, system), - OpCode::CALLVALUE => context::call_value(runtime, system), - OpCode::CALLDATALOAD => call::calldataload(runtime), - OpCode::CALLDATASIZE => call::calldatasize(runtime), - OpCode::CALLDATACOPY => call::calldatacopy(runtime)?, - OpCode::CODESIZE => call::codesize(&mut runtime.stack, bytecode.as_ref()), - OpCode::CODECOPY => call::codecopy(runtime, bytecode.as_ref())?, - OpCode::EXTCODESIZE => storage::extcodesize(runtime, system)?, - OpCode::EXTCODECOPY => memory::extcodecopy(runtime, system)?, - OpCode::RETURNDATASIZE => control::returndatasize(runtime), - OpCode::RETURNDATACOPY => control::returndatacopy(runtime)?, - OpCode::EXTCODEHASH => storage::extcodehash(runtime, system)?, - OpCode::BLOCKHASH => context::blockhash(runtime, system)?, - OpCode::ORIGIN => context::origin(runtime, system), - OpCode::COINBASE => context::coinbase(runtime, system)?, - OpCode::GASPRICE => context::gas_price(runtime, system)?, - OpCode::TIMESTAMP => context::timestamp(runtime, system)?, - OpCode::NUMBER => context::block_number(runtime, system)?, - OpCode::DIFFICULTY => context::difficulty(runtime, system)?, - OpCode::GASLIMIT => context::gas_limit(runtime, system)?, - OpCode::CHAINID => context::chain_id(runtime, system)?, - OpCode::BASEFEE => context::base_fee(runtime, system)?, - OpCode::SELFBALANCE => storage::selfbalance(runtime, system)?, - OpCode::POP => stack::pop(&mut runtime.stack), - OpCode::MLOAD => memory::mload(runtime)?, - OpCode::MSTORE => memory::mstore(runtime)?, - OpCode::MSTORE8 => memory::mstore8(runtime)?, - OpCode::JUMP => { - pc = control::jump(&mut runtime.stack, bytecode)?; - continue; // don't increment PC after the jump - } - OpCode::JUMPI => { - // conditional jump - if let Some(dest) = control::jumpi(&mut runtime.stack, bytecode)? { - pc = dest; // condition met, set program counter - continue; // don't increment PC after jump - } - } - OpCode::PC => control::pc(&mut runtime.stack, pc), - OpCode::MSIZE => memory::msize(runtime), - OpCode::SLOAD => storage::sload(runtime, system)?, - OpCode::SSTORE => storage::sstore(runtime, system)?, - OpCode::GAS => control::gas(runtime), - OpCode::JUMPDEST => {} // marker opcode for valid jumps addresses - OpCode::PUSH1 => pc += push::<1>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH2 => pc += push::<2>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH3 => pc += push::<3>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH4 => pc += push::<4>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH5 => pc += push::<5>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH6 => pc += push::<6>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH7 => pc += push::<7>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH8 => pc += push::<8>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH9 => pc += push::<9>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH10 => pc += push::<10>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH11 => pc += push::<11>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH12 => pc += push::<12>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH13 => pc += push::<13>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH14 => pc += push::<14>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH15 => pc += push::<15>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH16 => pc += push::<16>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH17 => pc += push::<17>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH18 => pc += push::<18>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH19 => pc += push::<19>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH20 => pc += push::<20>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH21 => pc += push::<21>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH22 => pc += push::<22>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH23 => pc += push::<23>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH24 => pc += push::<24>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH25 => pc += push::<25>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH26 => pc += push::<26>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH27 => pc += push::<27>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH28 => pc += push::<28>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH29 => pc += push::<29>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH30 => pc += push::<30>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH31 => pc += push::<31>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::PUSH32 => pc += push::<32>(&mut runtime.stack, &bytecode[pc + 1..]), - OpCode::DUP1 => dup::<1>(&mut runtime.stack), - OpCode::DUP2 => dup::<2>(&mut runtime.stack), - OpCode::DUP3 => dup::<3>(&mut runtime.stack), - OpCode::DUP4 => dup::<4>(&mut runtime.stack), - OpCode::DUP5 => dup::<5>(&mut runtime.stack), - OpCode::DUP6 => dup::<6>(&mut runtime.stack), - OpCode::DUP7 => dup::<7>(&mut runtime.stack), - OpCode::DUP8 => dup::<8>(&mut runtime.stack), - OpCode::DUP9 => dup::<9>(&mut runtime.stack), - OpCode::DUP10 => dup::<10>(&mut runtime.stack), - OpCode::DUP11 => dup::<11>(&mut runtime.stack), - OpCode::DUP12 => dup::<12>(&mut runtime.stack), - OpCode::DUP13 => dup::<13>(&mut runtime.stack), - OpCode::DUP14 => dup::<14>(&mut runtime.stack), - OpCode::DUP15 => dup::<15>(&mut runtime.stack), - OpCode::DUP16 => dup::<16>(&mut runtime.stack), - OpCode::SWAP1 => swap::<1>(&mut runtime.stack), - OpCode::SWAP2 => swap::<2>(&mut runtime.stack), - OpCode::SWAP3 => swap::<3>(&mut runtime.stack), - OpCode::SWAP4 => swap::<4>(&mut runtime.stack), - OpCode::SWAP5 => swap::<5>(&mut runtime.stack), - OpCode::SWAP6 => swap::<6>(&mut runtime.stack), - OpCode::SWAP7 => swap::<7>(&mut runtime.stack), - OpCode::SWAP8 => swap::<8>(&mut runtime.stack), - OpCode::SWAP9 => swap::<9>(&mut runtime.stack), - OpCode::SWAP10 => swap::<10>(&mut runtime.stack), - OpCode::SWAP11 => swap::<11>(&mut runtime.stack), - OpCode::SWAP12 => swap::<12>(&mut runtime.stack), - OpCode::SWAP13 => swap::<13>(&mut runtime.stack), - OpCode::SWAP14 => swap::<14>(&mut runtime.stack), - OpCode::SWAP15 => swap::<15>(&mut runtime.stack), - OpCode::SWAP16 => swap::<16>(&mut runtime.stack), - OpCode::LOG0 => log(runtime, system, 0)?, - OpCode::LOG1 => log(runtime, system, 1)?, - OpCode::LOG2 => log(runtime, system, 2)?, - OpCode::LOG3 => log(runtime, system, 3)?, - OpCode::LOG4 => log(runtime, system, 4)?, - OpCode::CREATE => storage::create(runtime, system, false)?, - OpCode::CREATE2 => storage::create(runtime, system, true)?, - OpCode::CALL => call::call(runtime, system, CallKind::Call, false)?, - OpCode::CALLCODE => call::call(runtime, system, CallKind::CallCode, false)?, - OpCode::DELEGATECALL => call::call(runtime, system, CallKind::DelegateCall, false)?, - OpCode::STATICCALL => call::call(runtime, system, CallKind::Call, true)?, - OpCode::RETURN | OpCode::REVERT => { - control::ret(runtime)?; - reverted = op == OpCode::REVERT; - break; - } - OpCode::INVALID => return Err(StatusCode::InvalidInstruction), - OpCode::SELFDESTRUCT => storage::selfdestruct(runtime, system)?, - } - - pc += 1; // advance - } - + let mut m = Machine::new(system, runtime, bytecode); + m.execute()?; Ok(Output { - reverted, + reverted: m.reverted, status_code: StatusCode::Success, - output_data: runtime.output_data.clone(), + output_data: m.runtime.output_data.clone(), }) } diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index d9d47ab90..eadf6c3ba 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -1,6 +1,6 @@ use { - crate::interpreter::output::StatusCode, crate::interpreter::stack::Stack, - crate::interpreter::uints::*, crate::interpreter::ExecutionState, crate::interpreter::U256, + crate::interpreter::stack::Stack, + crate::interpreter::uints::*, crate::interpreter::U256, }; #[inline] @@ -138,9 +138,9 @@ pub fn signextend(stack: &mut Stack) { } #[inline] -pub fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> { - let mut base = state.stack.pop(); - let mut power = state.stack.pop(); +pub fn exp(stack: &mut Stack) { + let mut base = stack.pop(); + let mut power = stack.pop(); let mut v = U256::from(1); @@ -152,7 +152,5 @@ pub fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> { base = base.overflowing_mul(base).0; } - state.stack.push(v); - - Ok(()) + stack.push(v); } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 4d03d7195..47532cf9e 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -36,7 +36,7 @@ pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = ®ion { let input_len = U256::from(state.input_data.len()); @@ -68,7 +68,7 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = region { let src = core::cmp::min(U256::from(code.len()), input_index).as_usize(); diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index c13cde067..9c659aea8 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -10,7 +10,7 @@ pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { let size = *state.stack.get(1); if let Some(region) = - super::memory::get_memory_region(state, offset, size).map_err(|_| StatusCode::OutOfGas)? + super::memory::get_memory_region(state, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)? { state.output_data = state.memory[region.offset..region.offset + region.size.get()].to_vec().into(); @@ -30,7 +30,7 @@ pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?; + let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if input_index > U256::from(state.return_data.len()) { return Err(StatusCode::InvalidMemoryAccess); diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 21cd1cdf9..25a6ab5f0 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -11,7 +11,7 @@ pub fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> { let size = state.stack.pop(); let region = get_memory_region(state, index, size) // - .map_err(|_| StatusCode::OutOfGas)?; + .map_err(|_| StatusCode::InvalidMemoryAccess)?; state.stack.push(U256::from_big_endian(&*Keccak256::digest(if let Some(region) = region { &state.memory[region.offset..region.offset + region.size.get()] diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 68475c648..9aee8bd1a 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -71,7 +71,7 @@ pub fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::OutOfGas)?; + .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); @@ -86,7 +86,7 @@ pub fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> { let value = state.stack.pop(); let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::OutOfGas)?; + .map_err(|_| StatusCode::InvalidMemoryAccess)?; let mut bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut bytes); @@ -101,7 +101,7 @@ pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { let value = state.stack.pop(); let region = get_memory_region_u64(state, index, NonZeroUsize::new(1).unwrap()) - .map_err(|_| StatusCode::OutOfGas)?; + .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = (value.low_u32() & 0xff) as u8; diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index a5634280d..c628c076e 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -43,10 +43,6 @@ pub enum StatusCode { #[strum(serialize = "revert")] Revert, - /// The execution has run out of gas. - #[strum(serialize = "out of gas")] - OutOfGas, - /// The designated INVALID instruction has been hit during execution. /// /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) From 645bd71eaaa90eeb4df588874cb2de61530e128f Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 15:10:44 +0300 Subject: [PATCH 016/339] reduce duplication, make everything run by a single macro --- actors/evm/src/interpreter/execution.rs | 462 ++++++++---------------- 1 file changed, 160 insertions(+), 302 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index fc658d6c5..e66c19494 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -54,10 +54,12 @@ enum ControlFlow { type Instruction = fn(*mut M) -> Result; macro_rules! def_jmptable { - ($($op:ident: $ins:ident),*) => { - let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; - $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$ins;)* - table + ($($op:ident: $ins:ident)*) => { + const fn jmptable() -> [Instruction>; 256] { + let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; + $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$ins;)* + table + } } } @@ -71,8 +73,16 @@ macro_rules! def_ins1 { } macro_rules! def_ins { - ($($ins:ident ($arg:ident) $body:block)*) => { + ($($op:ident: $ins:ident ($arg:ident) $body:block)*) => { + def_ins1! { + ins_undefined(_m) { + Err(StatusCode::UndefinedInstruction) + } + } $(def_ins1! { $ins($arg) $body })* + def_jmptable! { + $($op: $ins)* + } } } @@ -120,456 +130,302 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { Self::JMPTABLE[op as usize] } - const fn jmptable() -> [Instruction>; 256] { - def_jmptable! { - STOP: ins_stop, - ADD: ins_add, - MUL: ins_mul, - SUB: ins_sub, - DIV: ins_div, - SDIV: ins_sdiv, - MOD: ins_mod, - SMOD: ins_smod, - ADDMOD: ins_addmod, - MULMOD: ins_mulmod, - EXP: ins_exp, - SIGNEXTEND: ins_signextend, - LT: ins_lt, - GT: ins_gt, - SLT: ins_slt, - SGT: ins_sgt, - EQ: ins_eq, - ISZERO: ins_iszero, - AND: ins_and, - OR: ins_or, - XOR: ins_xor, - NOT: ins_not, - BYTE: ins_byte, - SHL: ins_shl, - SHR: ins_shr, - SAR: ins_sar, - KECCAK256: ins_keccak256, - ADDRESS: ins_address, - BALANCE: ins_balance, - ORIGIN: ins_origin, - CALLER: ins_caller, - CALLVALUE: ins_callvalue, - CALLDATALOAD: ins_calldataload, - CALLDATASIZE: ins_calldatasize, - CALLDATACOPY: ins_calldatacopy, - CODESIZE: ins_codesize, - CODECOPY: ins_codecopy, - GASPRICE: ins_gasprice, - EXTCODESIZE: ins_extcodesize, - EXTCODECOPY: ins_extcodecopy, - RETURNDATASIZE: ins_returndatasize, - RETURNDATACOPY: ins_returndatacopy, - EXTCODEHASH: ins_extcodehash, - BLOCKHASH: ins_blockhash, - COINBASE: ins_coinbase, - TIMESTAMP: ins_timestamp, - NUMBER: ins_number, - DIFFICULTY: ins_difficulty, - GASLIMIT: ins_gaslimit, - CHAINID: ins_chainid, - SELFBALANCE: ins_selfbalance, - BASEFEE: ins_basefee, - POP: ins_pop, - MLOAD: ins_mload, - MSTORE: ins_mstore, - MSTORE8: ins_mstore8, - SLOAD: ins_sload, - SSTORE: ins_sstore, - JUMP: ins_jump, - JUMPI: ins_jumpi, - PC: ins_pc, - MSIZE: ins_msize, - GAS: ins_gas, - JUMPDEST: ins_jumpdest, - PUSH1: ins_push1, - PUSH2: ins_push2, - PUSH3: ins_push3, - PUSH4: ins_push4, - PUSH5: ins_push5, - PUSH6: ins_push6, - PUSH7: ins_push7, - PUSH8: ins_push8, - PUSH9: ins_push9, - PUSH10: ins_push10, - PUSH11: ins_push11, - PUSH12: ins_push12, - PUSH13: ins_push13, - PUSH14: ins_push14, - PUSH15: ins_push15, - PUSH16: ins_push16, - PUSH17: ins_push17, - PUSH18: ins_push18, - PUSH19: ins_push19, - PUSH20: ins_push20, - PUSH21: ins_push21, - PUSH22: ins_push22, - PUSH23: ins_push23, - PUSH24: ins_push24, - PUSH25: ins_push25, - PUSH26: ins_push26, - PUSH27: ins_push27, - PUSH28: ins_push28, - PUSH29: ins_push29, - PUSH30: ins_push30, - PUSH31: ins_push31, - PUSH32: ins_push32, - DUP1: ins_dup1, - DUP2: ins_dup2, - DUP3: ins_dup3, - DUP4: ins_dup4, - DUP5: ins_dup5, - DUP6: ins_dup6, - DUP7: ins_dup7, - DUP8: ins_dup8, - DUP9: ins_dup9, - DUP10: ins_dup10, - DUP11: ins_dup11, - DUP12: ins_dup12, - DUP13: ins_dup13, - DUP14: ins_dup14, - DUP15: ins_dup15, - DUP16: ins_dup16, - SWAP1: ins_swap1, - SWAP2: ins_swap2, - SWAP3: ins_swap3, - SWAP4: ins_swap4, - SWAP5: ins_swap5, - SWAP6: ins_swap6, - SWAP7: ins_swap7, - SWAP8: ins_swap8, - SWAP9: ins_swap9, - SWAP10: ins_swap10, - SWAP11: ins_swap11, - SWAP12: ins_swap12, - SWAP13: ins_swap13, - SWAP14: ins_swap14, - SWAP15: ins_swap15, - SWAP16: ins_swap16, - LOG0: ins_log0, - LOG1: ins_log1, - LOG2: ins_log2, - LOG3: ins_log3, - LOG4: ins_log4, - CREATE: ins_create, - CALL: ins_call, - CALLCODE: ins_callcode, - RETURN: ins_return, - DELEGATECALL: ins_delegatecall, - CREATE2: ins_create2, - STATICCALL: ins_staticcall, - REVERT: ins_revert, - INVALID: ins_invalid, - SELFDESTRUCT: ins_selfdestruct - } - } - - const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); - def_ins! { - ins_undefined(_m) { - Err(StatusCode::UndefinedInstruction) - } - - ins_stop(_m) { + STOP: ins_stop(_m) { Ok(ControlFlow::Exit) } - ins_add(m) { + ADD: ins_add(m) { arithmetic::add(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_mul(m) { + MUL: ins_mul(m) { arithmetic::mul(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_sub(m) { + SUB: ins_sub(m) { arithmetic::sub(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_div(m) { + DIV: ins_div(m) { arithmetic::div(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_sdiv(m) { + SDIV: ins_sdiv(m) { arithmetic::sdiv(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_mod(m) { + MOD: ins_mod(m) { arithmetic::modulo(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_smod(m) { + SMOD: ins_smod(m) { arithmetic::smod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_addmod(m) { + ADDMOD: ins_addmod(m) { arithmetic::addmod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_mulmod(m) { + MULMOD: ins_mulmod(m) { arithmetic::mulmod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_exp(m) { + EXP: ins_exp(m) { arithmetic::exp(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_signextend(m) { + SIGNEXTEND: ins_signextend(m) { arithmetic::signextend(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_lt(m) { + LT: ins_lt(m) { boolean::lt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_gt(m) { + GT: ins_gt(m) { boolean::gt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_slt(m) { + SLT: ins_slt(m) { boolean::slt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_sgt(m) { + SGT: ins_sgt(m) { boolean::sgt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_eq(m) { + EQ: ins_eq(m) { boolean::eq(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_iszero(m) { + ISZERO: ins_iszero(m) { boolean::iszero(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_and(m) { + AND: ins_and(m) { boolean::and(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_or(m) { + OR: ins_or(m) { boolean::or(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_xor(m) { + XOR: ins_xor(m) { boolean::xor(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_not(m) { + NOT: ins_not(m) { boolean::not(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_byte(m) { + BYTE: ins_byte(m) { bitwise::byte(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_shl(m) { + SHL: ins_shl(m) { bitwise::shl(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_shr(m) { + SHR: ins_shr(m) { bitwise::shr(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_sar(m) { + SAR: ins_sar(m) { bitwise::sar(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_keccak256(m) { + KECCAK256: ins_keccak256(m) { hash::keccak256(m.runtime)?; Ok(ControlFlow::Continue) } - ins_address(m) { + ADDRESS: ins_address(m) { context::address(m.runtime, m.system); Ok(ControlFlow::Continue) } - ins_balance(m) { + BALANCE: ins_balance(m) { storage::balance(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_origin(m) { + ORIGIN: ins_origin(m) { context::origin(m.runtime, m.system); Ok(ControlFlow::Continue) } - ins_caller(m) { + CALLER: ins_caller(m) { context::caller(m.runtime, m.system); Ok(ControlFlow::Continue) } - ins_callvalue(m) { + CALLVALUE: ins_callvalue(m) { context::call_value(m.runtime, m.system); Ok(ControlFlow::Continue) } - ins_calldataload(m) { + CALLDATALOAD: ins_calldataload(m) { call::calldataload(m.runtime); Ok(ControlFlow::Continue) } - ins_calldatasize(m) { + CALLDATASIZE: ins_calldatasize(m) { call::calldatasize(m.runtime); Ok(ControlFlow::Continue) } - ins_calldatacopy(m) { + CALLDATACOPY: ins_calldatacopy(m) { call::calldatacopy(m.runtime)?; Ok(ControlFlow::Continue) } - ins_codesize(m) { + CODESIZE: ins_codesize(m) { call::codesize(&mut m.runtime.stack, m.bytecode.as_ref()); Ok(ControlFlow::Continue) } - ins_codecopy(m) { + CODECOPY: ins_codecopy(m) { call::codecopy(m.runtime, m.bytecode.as_ref())?; Ok(ControlFlow::Continue) } - ins_gasprice(m) { + GASPRICE: ins_gasprice(m) { context::gas_price(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_extcodesize(m) { + EXTCODESIZE: ins_extcodesize(m) { storage::extcodesize(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_extcodecopy(m) { + EXTCODECOPY: ins_extcodecopy(m) { memory::extcodecopy(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_returndatasize(m) { + RETURNDATASIZE: ins_returndatasize(m) { control::returndatasize(m.runtime); Ok(ControlFlow::Continue) } - ins_returndatacopy(m) { + RETURNDATACOPY: ins_returndatacopy(m) { control::returndatacopy(m.runtime)?; Ok(ControlFlow::Continue) } - ins_extcodehash(m) { + EXTCODEHASH: ins_extcodehash(m) { storage::extcodehash(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_blockhash(m) { + BLOCKHASH: ins_blockhash(m) { context::blockhash(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_coinbase(m) { + COINBASE: ins_coinbase(m) { context::coinbase(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_timestamp(m) { + TIMESTAMP: ins_timestamp(m) { context::timestamp(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_number(m) { + NUMBER: ins_number(m) { context::block_number(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_difficulty(m) { + DIFFICULTY: ins_difficulty(m) { context::difficulty(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_gaslimit(m) { + GASLIMIT: ins_gaslimit(m) { context::gas_limit(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_chainid(m) { + CHAINID: ins_chainid(m) { context::chain_id(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_selfbalance(m) { + SELFBALANCE: ins_selfbalance(m) { storage::selfbalance(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_basefee(m) { + BASEFEE: ins_basefee(m) { context::base_fee(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_pop(m) { + POP: ins_pop(m) { stack::pop(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_mload(m) { + MLOAD: ins_mload(m) { memory::mload(m.runtime)?; Ok(ControlFlow::Continue) } - ins_mstore(m) { + MSTORE: ins_mstore(m) { memory::mstore(m.runtime)?; Ok(ControlFlow::Continue) } - ins_mstore8(m) { + MSTORE8: ins_mstore8(m) { memory::mstore8(m.runtime)?; Ok(ControlFlow::Continue) } - ins_sload(m) { + SLOAD: ins_sload(m) { storage::sload(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_sstore(m) { + SSTORE: ins_sstore(m) { storage::sstore(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ins_jump(m) { + JUMP: ins_jump(m) { m.pc = control::jump(&mut m.runtime.stack, m.bytecode)?; Ok(ControlFlow::Jump) } - ins_jumpi(m) { + JUMPI: ins_jumpi(m) { if let Some(dest) = control::jumpi(&mut m.runtime.stack, m.bytecode)? { m.pc = dest; Ok(ControlFlow::Jump) @@ -578,421 +434,423 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } } - ins_pc(m) { + PC: ins_pc(m) { control::pc(&mut m.runtime.stack, m.pc); Ok(ControlFlow::Continue) } - ins_msize(m) { + MSIZE: ins_msize(m) { memory::msize(m.runtime); Ok(ControlFlow::Continue) } - ins_gas(m) { + GAS: ins_gas(m) { control::gas(m.runtime); Ok(ControlFlow::Continue) } - ins_jumpdest(_m) { + JUMPDEST: ins_jumpdest(_m) { // marker opcode for valid jumps addresses Ok(ControlFlow::Continue) } - ins_push1(m) { + PUSH1: ins_push1(m) { m.pc += push::<1>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push2(m) { + PUSH2: ins_push2(m) { m.pc += push::<2>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push3(m) { + PUSH3: ins_push3(m) { m.pc += push::<3>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push4(m) { + PUSH4: ins_push4(m) { m.pc += push::<4>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push5(m) { + PUSH5: ins_push5(m) { m.pc += push::<5>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push6(m) { + PUSH6: ins_push6(m) { m.pc += push::<6>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push7(m) { + PUSH7: ins_push7(m) { m.pc += push::<7>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push8(m) { + PUSH8: ins_push8(m) { m.pc += push::<8>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push9(m) { + PUSH9: ins_push9(m) { m.pc += push::<9>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push10(m) { + PUSH10: ins_push10(m) { m.pc += push::<10>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push11(m) { + PUSH11: ins_push11(m) { m.pc += push::<11>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push12(m) { + PUSH12: ins_push12(m) { m.pc += push::<12>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push13(m) { + PUSH13: ins_push13(m) { m.pc += push::<13>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push14(m) { + PUSH14: ins_push14(m) { m.pc += push::<14>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push15(m) { + PUSH15: ins_push15(m) { m.pc += push::<15>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push16(m) { + PUSH16: ins_push16(m) { m.pc += push::<16>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push17(m) { + PUSH17: ins_push17(m) { m.pc += push::<17>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push18(m) { + PUSH18: ins_push18(m) { m.pc += push::<18>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push19(m) { + PUSH19: ins_push19(m) { m.pc += push::<19>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push20(m) { + PUSH20: ins_push20(m) { m.pc += push::<20>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push21(m) { + PUSH21: ins_push21(m) { m.pc += push::<21>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push22(m) { + PUSH22: ins_push22(m) { m.pc += push::<22>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push23(m) { + PUSH23: ins_push23(m) { m.pc += push::<23>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push24(m) { + PUSH24: ins_push24(m) { m.pc += push::<24>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push25(m) { + PUSH25: ins_push25(m) { m.pc += push::<25>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push26(m) { + PUSH26: ins_push26(m) { m.pc += push::<26>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push27(m) { + PUSH27: ins_push27(m) { m.pc += push::<27>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push28(m) { + PUSH28: ins_push28(m) { m.pc += push::<28>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push29(m) { + PUSH29: ins_push29(m) { m.pc += push::<29>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push30(m) { + PUSH30: ins_push30(m) { m.pc += push::<30>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push31(m) { + PUSH31: ins_push31(m) { m.pc += push::<31>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_push32(m) { + PUSH32: ins_push32(m) { m.pc += push::<32>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - ins_dup1(m) { + DUP1: ins_dup1(m) { dup::<1>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup2(m) { + DUP2: ins_dup2(m) { dup::<2>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup3(m) { + DUP3: ins_dup3(m) { dup::<3>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup4(m) { + DUP4: ins_dup4(m) { dup::<4>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup5(m) { + DUP5: ins_dup5(m) { dup::<5>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup6(m) { + DUP6: ins_dup6(m) { dup::<6>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup7(m) { + DUP7: ins_dup7(m) { dup::<7>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup8(m) { + DUP8: ins_dup8(m) { dup::<8>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup9(m) { + DUP9: ins_dup9(m) { dup::<9>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup10(m) { + DUP10: ins_dup10(m) { dup::<10>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup11(m) { + DUP11: ins_dup11(m) { dup::<11>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup12(m) { + DUP12: ins_dup12(m) { dup::<12>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup13(m) { + DUP13: ins_dup13(m) { dup::<13>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup14(m) { + DUP14: ins_dup14(m) { dup::<14>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup15(m) { + DUP15: ins_dup15(m) { dup::<15>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_dup16(m) { + DUP16: ins_dup16(m) { dup::<16>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap1(m) { + SWAP1: ins_swap1(m) { swap::<1>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap2(m) { + SWAP2: ins_swap2(m) { swap::<2>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap3(m) { + SWAP3: ins_swap3(m) { swap::<3>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap4(m) { + SWAP4: ins_swap4(m) { swap::<4>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap5(m) { + SWAP5: ins_swap5(m) { swap::<5>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap6(m) { + SWAP6: ins_swap6(m) { swap::<6>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap7(m) { + SWAP7: ins_swap7(m) { swap::<7>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap8(m) { + SWAP8: ins_swap8(m) { swap::<8>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap9(m) { + SWAP9: ins_swap9(m) { swap::<9>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap10(m) { + SWAP10: ins_swap10(m) { swap::<10>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap11(m) { + SWAP11: ins_swap11(m) { swap::<11>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap12(m) { + SWAP12: ins_swap12(m) { swap::<12>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap13(m) { + SWAP13: ins_swap13(m) { swap::<13>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap14(m) { + SWAP14: ins_swap14(m) { swap::<14>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap15(m) { + SWAP15: ins_swap15(m) { swap::<15>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_swap16(m) { + SWAP16: ins_swap16(m) { swap::<16>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ins_log0(m) { + LOG0: ins_log0(m) { log(m.runtime, m.system, 0)?; Ok(ControlFlow::Continue) } - ins_log1(m) { + LOG1: ins_log1(m) { log(m.runtime, m.system, 1)?; Ok(ControlFlow::Continue) } - ins_log2(m) { + LOG2: ins_log2(m) { log(m.runtime, m.system, 2)?; Ok(ControlFlow::Continue) } - ins_log3(m) { + LOG3: ins_log3(m) { log(m.runtime, m.system, 3)?; Ok(ControlFlow::Continue) } - ins_log4(m) { + LOG4: ins_log4(m) { log(m.runtime, m.system, 4)?; Ok(ControlFlow::Continue) } - ins_create(m) { + CREATE: ins_create(m) { storage::create(m.runtime, m.system, false)?; Ok(ControlFlow::Continue) } - ins_call(m) { + CALL: ins_call(m) { call::call(m.runtime, m.system, CallKind::Call, false)?; Ok(ControlFlow::Continue) } - ins_callcode(m) { + CALLCODE: ins_callcode(m) { call::call(m.runtime, m.system, CallKind::CallCode, false)?; Ok(ControlFlow::Continue) } - ins_return(m) { + RETURN: ins_return(m) { control::ret(m.runtime)?; Ok(ControlFlow::Exit) } - ins_delegatecall(m) { + DELEGATECALL: ins_delegatecall(m) { call::call(m.runtime, m.system, CallKind::DelegateCall, false)?; Ok(ControlFlow::Continue) } - ins_create2(m) { + CREATE2: ins_create2(m) { storage::create(m.runtime, m.system, true)?; Ok(ControlFlow::Continue) } - ins_staticcall(m) { + STATICCALL: ins_staticcall(m) { call::call(m.runtime, m.system, CallKind::Call, true)?; Ok(ControlFlow::Continue) } - ins_revert(m) { + REVERT: ins_revert(m) { control::ret(m.runtime)?; m.reverted = true; Ok(ControlFlow::Exit) } - ins_invalid(_m) { + INVALID: ins_invalid(_m) { Err(StatusCode::InvalidInstruction) } - ins_selfdestruct(m) { + SELFDESTRUCT: ins_selfdestruct(m) { storage::selfdestruct(m.runtime, m.system)?; Ok(ControlFlow::Continue) } } + + const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); } pub fn execute<'r, BS: Blockstore, RT: Runtime>( From 7ea12d0ff733f45d3cb5fa4f9fb58b7297a503be Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 15:11:06 +0300 Subject: [PATCH 017/339] rustfmt --- actors/evm/src/interpreter/execution.rs | 17 ++++++----------- .../src/interpreter/instructions/arithmetic.rs | 5 +---- actors/evm/src/interpreter/instructions/call.rs | 6 ++++-- .../evm/src/interpreter/instructions/control.rs | 7 ++++--- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index e66c19494..c0a9344c2 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -69,7 +69,7 @@ macro_rules! def_ins1 { let $arg: &mut Self = unsafe { p.as_mut().unwrap() }; $body } - } + }; } macro_rules! def_ins { @@ -87,17 +87,12 @@ macro_rules! def_ins { } impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { - pub fn new(system: &'r System<'r, BS, RT>, - runtime: &'r mut ExecutionState, - bytecode: &'r Bytecode, + pub fn new( + system: &'r System<'r, BS, RT>, + runtime: &'r mut ExecutionState, + bytecode: &'r Bytecode, ) -> Self { - Machine { - system: system, - runtime: runtime, - bytecode: bytecode, - pc: 0, - reverted: false, - } + Machine { system: system, runtime: runtime, bytecode: bytecode, pc: 0, reverted: false } } pub fn execute(&mut self) -> Result<(), StatusCode> { diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index eadf6c3ba..53874144a 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -1,7 +1,4 @@ -use { - crate::interpreter::stack::Stack, - crate::interpreter::uints::*, crate::interpreter::U256, -}; +use {crate::interpreter::stack::Stack, crate::interpreter::uints::*, crate::interpreter::U256}; #[inline] pub fn add(stack: &mut Stack) { diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 47532cf9e..809385025 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -36,7 +36,8 @@ pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = + get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = ®ion { let input_len = U256::from(state.input_data.len()); @@ -68,7 +69,8 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = + get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = region { let src = core::cmp::min(U256::from(code.len()), input_index).as_usize(); diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 9c659aea8..6b323449f 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -9,8 +9,8 @@ pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { let offset = *state.stack.get(0); let size = *state.stack.get(1); - if let Some(region) = - super::memory::get_memory_region(state, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)? + if let Some(region) = super::memory::get_memory_region(state, offset, size) + .map_err(|_| StatusCode::InvalidMemoryAccess)? { state.output_data = state.memory[region.offset..region.offset + region.size.get()].to_vec().into(); @@ -30,7 +30,8 @@ pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = + get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; if input_index > U256::from(state.return_data.len()) { return Err(StatusCode::InvalidMemoryAccess); From a7536b3736c4f31f3b2b1918216b975f48224ecb Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 16:08:02 +0300 Subject: [PATCH 018/339] Add comment about safety of the unsafe block. Co-authored-by: Melanie Riise --- actors/evm/src/interpreter/execution.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index c0a9344c2..d87914a3b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -66,6 +66,7 @@ macro_rules! def_jmptable { macro_rules! def_ins1 { ($ins:ident ($arg:ident) $body:block) => { fn $ins(p: *mut Self) -> Result { + // 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 Self = unsafe { p.as_mut().unwrap() }; $body } From 5381035d4c19e1cafa672c2ee657f4a2af6d1850 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 19:02:39 +0300 Subject: [PATCH 019/339] remove redundant identifier names --- actors/evm/src/interpreter/execution.rs | 301 ++++++++++++------------ 1 file changed, 151 insertions(+), 150 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index d87914a3b..ef533bcf7 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -54,10 +54,10 @@ enum ControlFlow { type Instruction = fn(*mut M) -> Result; macro_rules! def_jmptable { - ($($op:ident: $ins:ident)*) => { + ($($op:ident)*) => { const fn jmptable() -> [Instruction>; 256] { - let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::ins_undefined; 256]; - $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$ins;)* + let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::UNDEFINED; 256]; + $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$op;)* table } } @@ -65,6 +65,7 @@ macro_rules! def_jmptable { macro_rules! def_ins1 { ($ins:ident ($arg:ident) $body:block) => { + #[allow(non_snake_case)] fn $ins(p: *mut Self) -> Result { // 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 Self = unsafe { p.as_mut().unwrap() }; @@ -74,15 +75,15 @@ macro_rules! def_ins1 { } macro_rules! def_ins { - ($($op:ident: $ins:ident ($arg:ident) $body:block)*) => { + ($($op:ident ($arg:ident) $body:block)*) => { def_ins1! { - ins_undefined(_m) { + UNDEFINED(_m) { Err(StatusCode::UndefinedInstruction) } } - $(def_ins1! { $ins($arg) $body })* + $(def_ins1! { $op ($arg) $body })* def_jmptable! { - $($op: $ins)* + $($op)* } } } @@ -127,301 +128,301 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } def_ins! { - STOP: ins_stop(_m) { + STOP(_m) { Ok(ControlFlow::Exit) } - ADD: ins_add(m) { + ADD(m) { arithmetic::add(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - MUL: ins_mul(m) { + MUL(m) { arithmetic::mul(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SUB: ins_sub(m) { + SUB(m) { arithmetic::sub(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DIV: ins_div(m) { + DIV(m) { arithmetic::div(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SDIV: ins_sdiv(m) { + SDIV(m) { arithmetic::sdiv(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - MOD: ins_mod(m) { + MOD(m) { arithmetic::modulo(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SMOD: ins_smod(m) { + SMOD(m) { arithmetic::smod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ADDMOD: ins_addmod(m) { + ADDMOD(m) { arithmetic::addmod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - MULMOD: ins_mulmod(m) { + MULMOD(m) { arithmetic::mulmod(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - EXP: ins_exp(m) { + EXP(m) { arithmetic::exp(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SIGNEXTEND: ins_signextend(m) { + SIGNEXTEND(m) { arithmetic::signextend(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - LT: ins_lt(m) { + LT(m) { boolean::lt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - GT: ins_gt(m) { + GT(m) { boolean::gt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SLT: ins_slt(m) { + SLT(m) { boolean::slt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SGT: ins_sgt(m) { + SGT(m) { boolean::sgt(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - EQ: ins_eq(m) { + EQ(m) { boolean::eq(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - ISZERO: ins_iszero(m) { + ISZERO(m) { boolean::iszero(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - AND: ins_and(m) { + AND(m) { boolean::and(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - OR: ins_or(m) { + OR(m) { boolean::or(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - XOR: ins_xor(m) { + XOR(m) { boolean::xor(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - NOT: ins_not(m) { + NOT(m) { boolean::not(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - BYTE: ins_byte(m) { + BYTE(m) { bitwise::byte(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SHL: ins_shl(m) { + SHL(m) { bitwise::shl(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SHR: ins_shr(m) { + SHR(m) { bitwise::shr(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SAR: ins_sar(m) { + SAR(m) { bitwise::sar(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - KECCAK256: ins_keccak256(m) { + KECCAK256(m) { hash::keccak256(m.runtime)?; Ok(ControlFlow::Continue) } - ADDRESS: ins_address(m) { + ADDRESS(m) { context::address(m.runtime, m.system); Ok(ControlFlow::Continue) } - BALANCE: ins_balance(m) { + BALANCE(m) { storage::balance(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - ORIGIN: ins_origin(m) { + ORIGIN(m) { context::origin(m.runtime, m.system); Ok(ControlFlow::Continue) } - CALLER: ins_caller(m) { + CALLER(m) { context::caller(m.runtime, m.system); Ok(ControlFlow::Continue) } - CALLVALUE: ins_callvalue(m) { + CALLVALUE(m) { context::call_value(m.runtime, m.system); Ok(ControlFlow::Continue) } - CALLDATALOAD: ins_calldataload(m) { + CALLDATALOAD(m) { call::calldataload(m.runtime); Ok(ControlFlow::Continue) } - CALLDATASIZE: ins_calldatasize(m) { + CALLDATASIZE(m) { call::calldatasize(m.runtime); Ok(ControlFlow::Continue) } - CALLDATACOPY: ins_calldatacopy(m) { + CALLDATACOPY(m) { call::calldatacopy(m.runtime)?; Ok(ControlFlow::Continue) } - CODESIZE: ins_codesize(m) { + CODESIZE(m) { call::codesize(&mut m.runtime.stack, m.bytecode.as_ref()); Ok(ControlFlow::Continue) } - CODECOPY: ins_codecopy(m) { + CODECOPY(m) { call::codecopy(m.runtime, m.bytecode.as_ref())?; Ok(ControlFlow::Continue) } - GASPRICE: ins_gasprice(m) { + GASPRICE(m) { context::gas_price(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - EXTCODESIZE: ins_extcodesize(m) { + EXTCODESIZE(m) { storage::extcodesize(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - EXTCODECOPY: ins_extcodecopy(m) { + EXTCODECOPY(m) { memory::extcodecopy(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - RETURNDATASIZE: ins_returndatasize(m) { + RETURNDATASIZE(m) { control::returndatasize(m.runtime); Ok(ControlFlow::Continue) } - RETURNDATACOPY: ins_returndatacopy(m) { + RETURNDATACOPY(m) { control::returndatacopy(m.runtime)?; Ok(ControlFlow::Continue) } - EXTCODEHASH: ins_extcodehash(m) { + EXTCODEHASH(m) { storage::extcodehash(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - BLOCKHASH: ins_blockhash(m) { + BLOCKHASH(m) { context::blockhash(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - COINBASE: ins_coinbase(m) { + COINBASE(m) { context::coinbase(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - TIMESTAMP: ins_timestamp(m) { + TIMESTAMP(m) { context::timestamp(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - NUMBER: ins_number(m) { + NUMBER(m) { context::block_number(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - DIFFICULTY: ins_difficulty(m) { + DIFFICULTY(m) { context::difficulty(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - GASLIMIT: ins_gaslimit(m) { + GASLIMIT(m) { context::gas_limit(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - CHAINID: ins_chainid(m) { + CHAINID(m) { context::chain_id(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - SELFBALANCE: ins_selfbalance(m) { + SELFBALANCE(m) { storage::selfbalance(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - BASEFEE: ins_basefee(m) { + BASEFEE(m) { context::base_fee(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - POP: ins_pop(m) { + POP(m) { stack::pop(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - MLOAD: ins_mload(m) { + MLOAD(m) { memory::mload(m.runtime)?; Ok(ControlFlow::Continue) } - MSTORE: ins_mstore(m) { + MSTORE(m) { memory::mstore(m.runtime)?; Ok(ControlFlow::Continue) } - MSTORE8: ins_mstore8(m) { + MSTORE8(m) { memory::mstore8(m.runtime)?; Ok(ControlFlow::Continue) } - SLOAD: ins_sload(m) { + SLOAD(m) { storage::sload(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - SSTORE: ins_sstore(m) { + SSTORE(m) { storage::sstore(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - JUMP: ins_jump(m) { + JUMP(m) { m.pc = control::jump(&mut m.runtime.stack, m.bytecode)?; Ok(ControlFlow::Jump) } - JUMPI: ins_jumpi(m) { + JUMPI(m) { if let Some(dest) = control::jumpi(&mut m.runtime.stack, m.bytecode)? { m.pc = dest; Ok(ControlFlow::Jump) @@ -430,417 +431,417 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } } - PC: ins_pc(m) { + PC(m) { control::pc(&mut m.runtime.stack, m.pc); Ok(ControlFlow::Continue) } - MSIZE: ins_msize(m) { + MSIZE(m) { memory::msize(m.runtime); Ok(ControlFlow::Continue) } - GAS: ins_gas(m) { + GAS(m) { control::gas(m.runtime); Ok(ControlFlow::Continue) } - JUMPDEST: ins_jumpdest(_m) { + JUMPDEST(_m) { // marker opcode for valid jumps addresses Ok(ControlFlow::Continue) } - PUSH1: ins_push1(m) { + PUSH1(m) { m.pc += push::<1>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH2: ins_push2(m) { + PUSH2(m) { m.pc += push::<2>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH3: ins_push3(m) { + PUSH3(m) { m.pc += push::<3>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH4: ins_push4(m) { + PUSH4(m) { m.pc += push::<4>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH5: ins_push5(m) { + PUSH5(m) { m.pc += push::<5>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH6: ins_push6(m) { + PUSH6(m) { m.pc += push::<6>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH7: ins_push7(m) { + PUSH7(m) { m.pc += push::<7>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH8: ins_push8(m) { + PUSH8(m) { m.pc += push::<8>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH9: ins_push9(m) { + PUSH9(m) { m.pc += push::<9>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH10: ins_push10(m) { + PUSH10(m) { m.pc += push::<10>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH11: ins_push11(m) { + PUSH11(m) { m.pc += push::<11>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH12: ins_push12(m) { + PUSH12(m) { m.pc += push::<12>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH13: ins_push13(m) { + PUSH13(m) { m.pc += push::<13>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH14: ins_push14(m) { + PUSH14(m) { m.pc += push::<14>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH15: ins_push15(m) { + PUSH15(m) { m.pc += push::<15>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH16: ins_push16(m) { + PUSH16(m) { m.pc += push::<16>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH17: ins_push17(m) { + PUSH17(m) { m.pc += push::<17>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH18: ins_push18(m) { + PUSH18(m) { m.pc += push::<18>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH19: ins_push19(m) { + PUSH19(m) { m.pc += push::<19>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH20: ins_push20(m) { + PUSH20(m) { m.pc += push::<20>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH21: ins_push21(m) { + PUSH21(m) { m.pc += push::<21>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH22: ins_push22(m) { + PUSH22(m) { m.pc += push::<22>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH23: ins_push23(m) { + PUSH23(m) { m.pc += push::<23>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH24: ins_push24(m) { + PUSH24(m) { m.pc += push::<24>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH25: ins_push25(m) { + PUSH25(m) { m.pc += push::<25>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH26: ins_push26(m) { + PUSH26(m) { m.pc += push::<26>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH27: ins_push27(m) { + PUSH27(m) { m.pc += push::<27>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH28: ins_push28(m) { + PUSH28(m) { m.pc += push::<28>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH29: ins_push29(m) { + PUSH29(m) { m.pc += push::<29>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH30: ins_push30(m) { + PUSH30(m) { m.pc += push::<30>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH31: ins_push31(m) { + PUSH31(m) { m.pc += push::<31>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - PUSH32: ins_push32(m) { + PUSH32(m) { m.pc += push::<32>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); Ok(ControlFlow::Continue) } - DUP1: ins_dup1(m) { + DUP1(m) { dup::<1>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP2: ins_dup2(m) { + DUP2(m) { dup::<2>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP3: ins_dup3(m) { + DUP3(m) { dup::<3>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP4: ins_dup4(m) { + DUP4(m) { dup::<4>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP5: ins_dup5(m) { + DUP5(m) { dup::<5>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP6: ins_dup6(m) { + DUP6(m) { dup::<6>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP7: ins_dup7(m) { + DUP7(m) { dup::<7>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP8: ins_dup8(m) { + DUP8(m) { dup::<8>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP9: ins_dup9(m) { + DUP9(m) { dup::<9>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP10: ins_dup10(m) { + DUP10(m) { dup::<10>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP11: ins_dup11(m) { + DUP11(m) { dup::<11>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP12: ins_dup12(m) { + DUP12(m) { dup::<12>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP13: ins_dup13(m) { + DUP13(m) { dup::<13>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP14: ins_dup14(m) { + DUP14(m) { dup::<14>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP15: ins_dup15(m) { + DUP15(m) { dup::<15>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - DUP16: ins_dup16(m) { + DUP16(m) { dup::<16>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP1: ins_swap1(m) { + SWAP1(m) { swap::<1>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP2: ins_swap2(m) { + SWAP2(m) { swap::<2>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP3: ins_swap3(m) { + SWAP3(m) { swap::<3>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP4: ins_swap4(m) { + SWAP4(m) { swap::<4>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP5: ins_swap5(m) { + SWAP5(m) { swap::<5>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP6: ins_swap6(m) { + SWAP6(m) { swap::<6>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP7: ins_swap7(m) { + SWAP7(m) { swap::<7>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP8: ins_swap8(m) { + SWAP8(m) { swap::<8>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP9: ins_swap9(m) { + SWAP9(m) { swap::<9>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP10: ins_swap10(m) { + SWAP10(m) { swap::<10>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP11: ins_swap11(m) { + SWAP11(m) { swap::<11>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP12: ins_swap12(m) { + SWAP12(m) { swap::<12>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP13: ins_swap13(m) { + SWAP13(m) { swap::<13>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP14: ins_swap14(m) { + SWAP14(m) { swap::<14>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP15: ins_swap15(m) { + SWAP15(m) { swap::<15>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - SWAP16: ins_swap16(m) { + SWAP16(m) { swap::<16>(&mut m.runtime.stack); Ok(ControlFlow::Continue) } - LOG0: ins_log0(m) { + LOG0(m) { log(m.runtime, m.system, 0)?; Ok(ControlFlow::Continue) } - LOG1: ins_log1(m) { + LOG1(m) { log(m.runtime, m.system, 1)?; Ok(ControlFlow::Continue) } - LOG2: ins_log2(m) { + LOG2(m) { log(m.runtime, m.system, 2)?; Ok(ControlFlow::Continue) } - LOG3: ins_log3(m) { + LOG3(m) { log(m.runtime, m.system, 3)?; Ok(ControlFlow::Continue) } - LOG4: ins_log4(m) { + LOG4(m) { log(m.runtime, m.system, 4)?; Ok(ControlFlow::Continue) } - CREATE: ins_create(m) { + CREATE(m) { storage::create(m.runtime, m.system, false)?; Ok(ControlFlow::Continue) } - CALL: ins_call(m) { + CALL(m) { call::call(m.runtime, m.system, CallKind::Call, false)?; Ok(ControlFlow::Continue) } - CALLCODE: ins_callcode(m) { + CALLCODE(m) { call::call(m.runtime, m.system, CallKind::CallCode, false)?; Ok(ControlFlow::Continue) } - RETURN: ins_return(m) { + RETURN(m) { control::ret(m.runtime)?; Ok(ControlFlow::Exit) } - DELEGATECALL: ins_delegatecall(m) { + DELEGATECALL(m) { call::call(m.runtime, m.system, CallKind::DelegateCall, false)?; Ok(ControlFlow::Continue) } - CREATE2: ins_create2(m) { + CREATE2(m) { storage::create(m.runtime, m.system, true)?; Ok(ControlFlow::Continue) } - STATICCALL: ins_staticcall(m) { + STATICCALL(m) { call::call(m.runtime, m.system, CallKind::Call, true)?; Ok(ControlFlow::Continue) } - REVERT: ins_revert(m) { + REVERT(m) { control::ret(m.runtime)?; m.reverted = true; Ok(ControlFlow::Exit) } - INVALID: ins_invalid(_m) { + INVALID(_m) { Err(StatusCode::InvalidInstruction) } - SELFDESTRUCT: ins_selfdestruct(m) { + SELFDESTRUCT(m) { storage::selfdestruct(m.runtime, m.system)?; Ok(ControlFlow::Continue) } From e37886f7df1fc60407492ce2dc6602f2b873ab38 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 24 Aug 2022 19:04:31 +0300 Subject: [PATCH 020/339] fix clippy --- actors/evm/src/interpreter/execution.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index ef533bcf7..a831f5c33 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -94,7 +94,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { runtime: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { - Machine { system: system, runtime: runtime, bytecode: bytecode, pc: 0, reverted: false } + Machine { system, runtime, bytecode, pc: 0, reverted: false } } pub fn execute(&mut self) -> Result<(), StatusCode> { From 65369ba399a01d266d78d0339a99e7d3f0be69ae Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:00:50 +1000 Subject: [PATCH 021/339] Duplicate fvm_shared::actor::builtin::Type into runtime::builtins::Type (#547) --- Cargo.lock | 2 ++ README.md | 13 +------------ actors/init/src/lib.rs | 3 +-- actors/market/src/lib.rs | 16 ++++++++-------- actors/miner/src/lib.rs | 8 ++++---- actors/multisig/src/lib.rs | 3 +-- actors/paych/src/lib.rs | 3 +-- actors/power/src/lib.rs | 4 ++-- runtime/src/builtin/shared.rs | 6 ++++++ runtime/src/runtime/builtins.rs | 22 ++++++++++++++++++++++ runtime/src/runtime/fvm.rs | 8 +++++--- runtime/src/runtime/mod.rs | 13 ++++++------- runtime/src/test_utils.rs | 3 +-- runtime/src/util/chaos/mod.rs | 2 +- state/Cargo.toml | 1 + state/src/check.rs | 10 +++++++--- test_vm/Cargo.toml | 1 + test_vm/src/lib.rs | 6 +++--- 18 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 runtime/src/runtime/builtins.rs diff --git a/Cargo.lock b/Cargo.lock index 2508f2204..60b151674 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -928,6 +928,7 @@ name = "fil_builtin_actors_state" version = "10.0.0-alpha.1" dependencies = [ "anyhow", + "bimap", "cid", "fil_actor_account", "fil_actor_cron", @@ -2050,6 +2051,7 @@ name = "test_vm" version = "10.0.0-alpha.1" dependencies = [ "anyhow", + "bimap", "blake2b_simd", "cid", "fil_actor_account", diff --git a/README.md b/README.md index ebe79075d..98f0e70b4 100644 --- a/README.md +++ b/README.md @@ -35,18 +35,7 @@ characteristics: entry represents a built-in actor. - Manifest keys (CID) point to the Wasm bytecode of an actor as a single block. - Manifest values (i32) identify the actor type, to be parsed as the - `fvm_shared::actor::builtin::Type` enum: - - System = 1 - - Init = 2 - - Cron = 3 - - Account = 4 - - Power = 5 - - Miner = 6 - - Market = 7 - - PaymentChannel = 8 - - Multisig = 9 - - Reward = 10 - - VerifiedRegistry = 11 + `runtime::builtins::Type` enum. Precompiled actor bundles are provided as [release binaries][releases] in this repo. The [`fil_builtin_actors_bundle`](https://crates.io/crates/fil_builtin_actors_bundle) crate on diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 26b2ddce2..8436cf9be 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use cid::Cid; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_blockstore::Blockstore; @@ -22,8 +23,6 @@ mod types; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); -// * Updated to specs-actors commit: 999e57a151cc7ada020ca2844b651499ab8c0dec (v3.0.1) - /// Init actor methods available #[derive(FromPrimitive)] #[repr(u64)] diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 72df51da4..30e4d11d4 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -6,7 +6,6 @@ use std::collections::{BTreeMap, BTreeSet}; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{Cbor, RawBytes}; -use fvm_shared::actor::builtin::{Type, CALLER_TYPES_SIGNABLE}; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED}; @@ -22,10 +21,12 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Signed, Zero}; use fil_actors_runtime::cbor::serialize_vec; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, - REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, + CRON_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use crate::ext::verifreg::UseBytesParams; @@ -35,16 +36,15 @@ use self::policy::*; pub use self::state::*; pub use self::types::*; +// exports for testing pub mod balance_table; -// export for testing -mod deal; #[doc(hidden)] pub mod ext; -// export for testing pub mod policy; -// export for testing -mod state; pub mod testing; + +mod deal; +mod state; mod types; #[cfg(feature = "fil-actor")] diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 073a4b862..3e10d4819 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -18,8 +18,8 @@ pub use deadlines::*; pub use expiration_queue::*; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, - REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + actor_error, cbor, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, + INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, }; use fvm_ipld_bitfield::{BitField, UnvalidatedBitField, Validate}; use fvm_ipld_blockstore::Blockstore; @@ -34,7 +34,7 @@ use fvm_shared::econ::TokenAmount; // They're not expected to ever happen, but if they do, distinguished codes can help us // diagnose the problem. use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; -use fvm_shared::actor::builtin::{Type, CALLER_TYPES_SIGNABLE}; +use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::error::*; use fvm_shared::randomness::*; use fvm_shared::reward::ThisEpochRewardReturn; @@ -3880,7 +3880,7 @@ where let is_principal = rt .resolve_builtin_actor_type(&owner_code) .as_ref() - .map(Type::is_principal) + .map(|t| CALLER_TYPES_SIGNABLE.contains(t)) .unwrap_or(false); if !is_principal { diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 9aab091b3..7c06c1520 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -7,11 +7,10 @@ use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_id_addr, ActorDowncast, - ActorError, Map, INIT_ACTOR_ADDR, + ActorError, Map, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; -use fvm_shared::actor::builtin::CALLER_TYPES_SIGNABLE; use fvm_shared::address::Address; use fvm_shared::bigint::Sign; use fvm_shared::econ::TokenAmount; diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index cdffa6e7a..792713963 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -1,11 +1,11 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{actor_error, cbor, resolve_to_id_addr, ActorDowncast, ActorError, Array}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::bigint::{BigInt, Sign}; use fvm_shared::econ::TokenAmount; @@ -85,7 +85,6 @@ impl Actor { .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", resolved))?; let typ = rt.resolve_builtin_actor_type(&code_cid); - if typ != Some(Type::Account) { Err(actor_error!( forbidden, diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 5e7ab09db..440b16229 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -6,14 +6,14 @@ use std::convert::TryInto; use anyhow::anyhow; use ext::init; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_map_with_root_and_bitwidth, ActorDowncast, ActorError, Multimap, - CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; -use fvm_shared::actor::builtin::{Type, CALLER_TYPES_SIGNABLE}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::{BigIntDe, BigIntSer}; use fvm_shared::econ::TokenAmount; diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 2ba233b78..e2ee7a39f 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -5,10 +5,16 @@ use fvm_ipld_blockstore::Blockstore; use fvm_shared::address::Address; use fvm_shared::METHOD_SEND; +use crate::runtime::builtins::Type; use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; +/// Types of built-in actors that can be treated as principles. +/// This distinction is legacy and should be removed prior to FVM support for +/// user-programmable actors. +pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; + /// ResolveToIDAddr resolves the given address to it's ID address form. /// If an ID address for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs new file mode 100644 index 000000000..86d9e66e6 --- /dev/null +++ b/runtime/src/runtime/builtins.rs @@ -0,0 +1,22 @@ +use num_derive::FromPrimitive; + +/// Identifies the builtin actor types for usage with the +/// actor::resolve_builtin_actor_type syscall. +/// Note that there is a mirror of this enum in the FVM SDK src/actors/builtins.rs. +/// These must be kept in sync for the syscall to work correctly, without either side +/// importing the other. +#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, FromPrimitive, Debug)] +#[repr(i32)] +pub enum Type { + System = 1, + Init = 2, + Cron = 3, + Account = 4, + Power = 5, + Miner = 6, + Market = 7, + PaymentChannel = 8, + Multisig = 9, + Reward = 10, + VerifiedRegistry = 11, +} diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 1c57f593e..d43d7186e 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -5,7 +5,6 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{to_vec, Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; use fvm_sdk::NO_DATA_BLOCK_ID; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; @@ -19,10 +18,12 @@ use fvm_shared::sector::{ }; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; +use num_traits::FromPrimitive; #[cfg(feature = "fake-proofs")] use sha2::{Digest, Sha256}; use crate::runtime::actor_blockstore::ActorBlockstore; +use crate::runtime::builtins::Type; use crate::runtime::{ ActorCode, ConsensusFault, DomainSeparationTag, MessageInfo, Policy, Primitives, RuntimePolicy, Verifier, @@ -169,11 +170,12 @@ where } fn resolve_builtin_actor_type(&self, code_id: &Cid) -> Option { - fvm::actor::get_builtin_actor_type(code_id) + fvm::actor::get_builtin_actor_type(code_id).and_then(|t| Type::from_i32(t as i32)) } fn get_code_cid_for_type(&self, typ: Type) -> Cid { - fvm::actor::get_code_cid_for_type(typ) + let t = fvm_shared::actor::builtin::Type::from_i32(typ as i32).unwrap(); + fvm::actor::get_code_cid_for_type(t) } fn get_randomness_from_tickets( diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index f94edf5f2..a62a59cad 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -4,7 +4,6 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{Cbor, RawBytes}; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; @@ -22,17 +21,17 @@ use fvm_shared::{ActorID, MethodNum}; pub use self::actor_code::*; pub use self::policy::*; pub use self::randomness::DomainSeparationTag; +use crate::runtime::builtins::Type; use crate::ActorError; -mod actor_code; - -#[cfg(feature = "fil-actor")] -pub mod fvm; - #[cfg(feature = "fil-actor")] mod actor_blockstore; - +#[cfg(feature = "fil-actor")] +pub mod builtins; +pub mod fvm; pub mod policy; + +mod actor_code; mod randomness; /// Runtime is the VM's internal runtime object. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 6fbb54b64..0c9db9d3b 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -11,10 +11,8 @@ use cid::Cid; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::{Address, Protocol}; use fvm_shared::clock::ChainEpoch; - use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::signature::Signature; @@ -34,6 +32,7 @@ use multihash::MultihashDigest; use rand::prelude::*; +use crate::runtime::builtins::Type; use crate::runtime::{ ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy, Verifier, diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs index 9e563e802..500245ea3 100644 --- a/runtime/src/util/chaos/mod.rs +++ b/runtime/src/util/chaos/mod.rs @@ -4,7 +4,6 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; @@ -13,6 +12,7 @@ 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}; diff --git a/state/Cargo.toml b/state/Cargo.toml index ca721b0aa..d569101da 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -31,6 +31,7 @@ fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" anyhow = "1.0.56" +bimap = { version = "0.6.2" } num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } diff --git a/state/src/check.rs b/state/src/check.rs index f3469d02b..8283f81c6 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::fmt::Debug; use anyhow::bail; +use bimap::BiBTreeMap; use cid::Cid; use fil_actor_account::State as AccountState; use fil_actor_cron::State as CronState; @@ -25,8 +26,6 @@ use fil_actors_runtime::MessageAccumulator; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::from_slice; use fvm_ipld_encoding::CborStore; -use fvm_shared::actor::builtin::Manifest; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::Address; use fvm_shared::address::Protocol; use fvm_shared::bigint::BigInt; @@ -48,6 +47,7 @@ use fil_actor_paych::testing as paych; use fil_actor_power::testing as power; use fil_actor_reward::testing as reward; use fil_actor_verifreg::testing as verifreg; +use fil_actors_runtime::runtime::builtins::Type; /// Value type of the top level of the state tree. /// Represents the on-chain state of a single actor. @@ -103,8 +103,12 @@ macro_rules! get_state { }; } +// Note: BiBTreeMap is an overly constrained type for what we are doing here, but chosen +// to match the Manifest implementation in the FVM. +// It could be replaced with a custom mapping trait (while Rust doesn't support +// abstract collection traits). pub fn check_state_invariants<'a, BS: Blockstore + Debug>( - manifest: &Manifest, + manifest: &BiBTreeMap, policy: &Policy, tree: Tree<'a, BS>, expected_balance_total: &TokenAmount, diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index dfbb39a9a..31ea10f69 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -41,6 +41,7 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0.30" anyhow = "1.0.56" +bimap = { version = "0.6.2" } blake2b_simd = "1.0" integer-encoding = { version = "3.0.3", default-features = false } regex = "1" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index c80741628..19f2d19c5 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::anyhow; +use bimap::BiBTreeMap; use cid::multihash::Code; use cid::Cid; use fil_actor_account::{Actor as AccountActor, State as AccountState}; @@ -14,6 +15,7 @@ use fil_actor_reward::{Actor as RewardActor, State as RewardState}; use fil_actor_system::{Actor as SystemActor, State as SystemState}; use fil_actor_verifreg::{Actor as VerifregActor, State as VerifRegState}; use fil_actors_runtime::cbor::serialize; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy, Verifier, @@ -31,8 +33,6 @@ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; -use fvm_shared::actor::builtin::Manifest; -use fvm_shared::actor::builtin::Type; use fvm_shared::address::{Address, Protocol}; use fvm_shared::bigint::{bigint_ser, BigInt, Zero}; use fvm_shared::clock::ChainEpoch; @@ -448,7 +448,7 @@ impl<'bs> VM<'bs> { ) .unwrap(); - let mut manifest = Manifest::new(); + let mut manifest = BiBTreeMap::new(); actors .for_each(|_, actor| { manifest.insert(actor.code, ACTOR_TYPES.get(&actor.code).unwrap().to_owned()); From 66c982613159674cd572217234260ddf8e3d437c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Aug 2022 03:29:14 -0700 Subject: [PATCH 022/339] fix: non-fil-actor-compile (#571) Fix compile when not compiling as a "fil actor" (e.g., for testing). --- runtime/src/runtime/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index a62a59cad..8cf273a28 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -24,15 +24,15 @@ pub use self::randomness::DomainSeparationTag; use crate::runtime::builtins::Type; use crate::ActorError; +mod actor_code; +pub mod builtins; +pub mod policy; +mod randomness; + #[cfg(feature = "fil-actor")] mod actor_blockstore; #[cfg(feature = "fil-actor")] -pub mod builtins; pub mod fvm; -pub mod policy; - -mod actor_code; -mod randomness; /// Runtime is the VM's internal runtime object. /// this is everything that is accessible to actors, beyond parameters. From 19c0d720fa22a7698f54dabfea0ded96a210048d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Aug 2022 09:27:27 -0700 Subject: [PATCH 023/339] feat: use Type from builtin-actors when building the bundle (#583) This change upgrades to the v4 bundler that _does not_ depend on the FVM to map actor types to IDs. Instead, it relies on the Type enum defined in this repo. This means new actor types can be added here without having to make changes in multiple places. --- Cargo.lock | 12 +++++++----- Cargo.toml | 6 ++++-- actors/init/src/lib.rs | 3 +-- build.rs | 21 +++++++++++++++------ runtime/src/runtime/builtins.rs | 20 ++++++++++++++++++++ 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60b151674..65c056cb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -628,8 +628,9 @@ dependencies = [ [[package]] name = "fil_actor_bundler" -version = "3.0.4" -source = "git+https://github.com/filecoin-project/builtin-actors-bundler?branch=next#f2fbadfa810d1544d1a60ebef41ffa4fe08dbf95" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1e563d609dede95fb0a847ea4ebfe71f5d7074703fc3e92736c74be4b237c2" dependencies = [ "anyhow", "async-std", @@ -639,7 +640,6 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_car", "fvm_ipld_encoding", - "fvm_shared", "serde", "serde_ipld_dagcbor", "serde_json", @@ -921,6 +921,8 @@ dependencies = [ "fil_actor_reward", "fil_actor_system", "fil_actor_verifreg", + "fil_actors_runtime", + "num-traits", ] [[package]] @@ -1119,9 +1121,9 @@ dependencies = [ [[package]] name = "fvm_ipld_car" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0af5e410b349533e8b1a2b46a7ca3d66cd68c53a477e2cf3d005845c1a346428" +checksum = "ade8c2f76a9e014f2fdaeb693a0884f49eca5ee663958e1c8c1db8d23290817c" dependencies = [ "cid", "futures", diff --git a/Cargo.toml b/Cargo.toml index 3dbe8d638..5d28d8160 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,11 +27,13 @@ fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] } [build-dependencies] -fil_actor_bundler = { git = "https://github.com/filecoin-project/builtin-actors-bundler", branch = "next" } +fil_actor_bundler = "4.0.0" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "runtime" } +num-traits = "0.2.15" [dependencies] -clap = { version = "3.1.8", features = ["derive"] } +clap = { version = "3.2.3", features = ["derive"] } [features] default = [] ## translates to mainnet diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 8436cf9be..1fb51e0de 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0, MIT use cid::Cid; -use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_blockstore::Blockstore; @@ -192,7 +191,7 @@ where BS: Blockstore, RT: Runtime, { - use fvm_shared::actor::builtin::Type; + use fil_actors_runtime::runtime::builtins::Type; rt.resolve_builtin_actor_type(exec) .map(|typ| match typ { diff --git a/build.rs b/build.rs index 1b02c1eb7..e52a20867 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,6 @@ use fil_actor_bundler::Bundler; +use fil_actors_runtime::runtime::builtins::Type; +use num_traits::cast::FromPrimitive; use std::error::Error; use std::io::{BufRead, BufReader}; use std::path::Path; @@ -16,11 +18,11 @@ const ACTORS: &[(&Package, &ID)] = &[ ("init", "init"), ("cron", "cron"), ("account", "account"), - ("multisig", "multisig"), ("power", "storagepower"), ("miner", "storageminer"), ("market", "storagemarket"), ("paych", "paymentchannel"), + ("multisig", "multisig"), ("reward", "reward"), ("verifreg", "verifiedregistry"), ("evm", "evm"), @@ -183,7 +185,12 @@ fn main() -> Result<(), Box> { let dst = Path::new(&out_dir).join("bundle.car"); let mut bundler = Bundler::new(&dst); - for (pkg, id) in ACTORS { + for (&(pkg, name), id) in ACTORS.iter().zip(1u32..) { + assert_eq!( + name, + Type::from_u32(id).expect("type not defined").name(), + "actor types don't match actors included in the bundle" + ); let bytecode_path = Path::new(&out_dir) .join("wasm32-unknown-unknown/wasm") .join(format!("fil_actor_{}.wasm", pkg)); @@ -192,10 +199,12 @@ fn main() -> Result<(), Box> { // content-addressed CIDs. let forced_cid = None; - let cid = bundler.add_from_file(id, forced_cid, &bytecode_path).unwrap_or_else(|err| { - panic!("failed to add file {:?} to bundle for actor {}: {}", bytecode_path, id, err) - }); - println!("cargo:warning=added actor {} to bundle with CID {}", id, cid); + let cid = bundler + .add_from_file(id, name.to_owned(), forced_cid, &bytecode_path) + .unwrap_or_else(|err| { + panic!("failed to add file {:?} to bundle for actor {}: {}", bytecode_path, id, err) + }); + println!("cargo:warning=added {} ({}) to bundle with CID {}", name, id, cid); } bundler.finish().expect("failed to finish bundle"); diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index 86d9e66e6..93245926b 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -19,4 +19,24 @@ pub enum Type { Multisig = 9, Reward = 10, VerifiedRegistry = 11, + EVM = 12, +} + +impl Type { + pub fn name(&self) -> &'static str { + match *self { + Type::System => "system", + Type::Init => "init", + Type::Cron => "cron", + Type::Account => "account", + Type::Power => "storagepower", + Type::Miner => "storageminer", + Type::Market => "storagemarket", + Type::PaymentChannel => "paymentchannel", + Type::Multisig => "multisig", + Type::Reward => "reward", + Type::VerifiedRegistry => "verifiedregistry", + Type::EVM => "evm", + } + } } From 032067e1845c6351d8d5c45b677cdb6d8451e811 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 25 Aug 2022 20:02:52 -0400 Subject: [PATCH 024/339] feat: Avoid blake2b hashing in wasm (#577) Avoid blake2b hashing in wasm Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> --- actors/market/src/lib.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 30e4d11d4..cb77691a3 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -1,6 +1,8 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use cid::multihash::{Code, MultihashGeneric}; +use cid::Cid; use std::collections::{BTreeMap, BTreeSet}; use fvm_ipld_bitfield::BitField; @@ -346,9 +348,7 @@ impl Actor { deal.proposal.provider = provider; deal.proposal.client = client; - let pcid = deal.proposal.cid().map_err( - |e| actor_error!(illegal_argument; "failed to take cid of proposal {}: {}", di, e), - )?; + let pcid = cid(rt, &deal.proposal)?; // check proposalCids for duplication within message batch // check state PendingProposals for duplication across messages @@ -572,9 +572,7 @@ impl Actor { })? .ok_or_else(|| actor_error!(not_found, "no such deal_id: {}", deal_id))?; - let propc = proposal - .cid() - .map_err(|e| ActorError::from(e).wrap("failed to calculate proposal Cid"))?; + let propc = cid(rt, proposal)?; let has = msm.pending_deals.as_ref().unwrap().has(&propc.to_bytes()).map_err(|e| { @@ -815,10 +813,7 @@ impl Actor { })? .clone(); - let dcid = deal.cid().map_err(|e| { - ActorError::from(e) - .wrap(format!("failed to calculate cid for proposal {}", deal_id)) - })?; + let dcid = cid(rt, &deal)?; let state = msm .deal_states @@ -1261,6 +1256,21 @@ where Ok(()) } +pub const DAG_CBOR: u64 = 0x71; // TODO is there a better place to get this? + +fn cid(rt: &RT, proposal: &DealProposal) -> Result +where + BS: Blockstore, + RT: Runtime, +{ + const DIGEST_SIZE: u32 = 32; + let data = &proposal.marshal_cbor()?; + let hash = MultihashGeneric::wrap(Code::Blake2b256.into(), &rt.hash_blake2b(data)) + .map_err(|e| actor_error!(illegal_argument; "failed to take cid of proposal {}", e))?; + debug_assert_eq!(u32::from(hash.size()), DIGEST_SIZE, "expected 32byte digest"); + Ok(Cid::new_v1(DAG_CBOR, hash)) +} + /// Resolves a provider or client address to the canonical form against which a balance should be held, and /// the designated recipient address of withdrawals (which is the same, for simple account parties). fn escrow_address( From 97ff14ee705bbae5d7b4a9c272caf8c016eb86bd Mon Sep 17 00:00:00 2001 From: Swift Liu <74490266+lyswifter@users.noreply.github.com> Date: Mon, 29 Aug 2022 11:03:37 +0800 Subject: [PATCH 025/339] Change runtime resolve address to return an ActorID (#549) Co-authored-by: lyswifter --- actors/init/src/lib.rs | 7 +++-- actors/market/src/lib.rs | 55 +++++++++++++++++++-------------- actors/miner/src/lib.rs | 6 ++-- actors/multisig/src/lib.rs | 46 +++++++++++++-------------- actors/paych/src/lib.rs | 16 +++++----- actors/reward/src/lib.rs | 5 +-- actors/verifreg/src/lib.rs | 55 ++++++++++++++++++++------------- runtime/src/builtin/shared.rs | 26 +++++++++------- runtime/src/runtime/fvm.rs | 11 ++++--- runtime/src/runtime/mod.rs | 4 +-- runtime/src/test_utils.rs | 22 +++++++++---- runtime/src/util/chaos/mod.rs | 5 +-- runtime/src/util/chaos/types.rs | 2 +- test_vm/src/lib.rs | 14 ++++++--- 14 files changed, 158 insertions(+), 116 deletions(-) diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 1fb51e0de..f302c9605 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -62,9 +62,10 @@ impl Actor { log::trace!("called exec; params.code_cid: {:?}", ¶ms.code_cid); - let caller_code = rt.get_actor_code_cid(&rt.message().caller()).ok_or_else(|| { - actor_error!(illegal_state, "no code for caller as {}", rt.message().caller()) - })?; + let caller_code = + rt.get_actor_code_cid(&rt.message().caller().id().unwrap()).ok_or_else(|| { + actor_error!(illegal_state, "no code for caller as {}", rt.message().caller()) + })?; log::trace!("caller code CID: {:?}", &caller_code); diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index cb77691a3..63520f79a 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -54,14 +54,14 @@ fil_actors_runtime::wasm_trampoline!(Actor); fn request_miner_control_addrs( rt: &mut RT, - miner_addr: Address, + miner_id: ActorID, ) -> Result<(Address, Address, Vec
), ActorError> where BS: Blockstore, RT: Runtime, { let ret = rt.send( - miner_addr, + Address::new_id(miner_id), ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), TokenAmount::zero(), @@ -230,13 +230,13 @@ impl Actor { // All deals should have the same provider so get worker once let provider_raw = params.deals[0].proposal.provider; - let provider = rt.resolve_address(&provider_raw).ok_or_else(|| { + let provider_id = rt.resolve_address(&provider_raw).ok_or_else(|| { actor_error!(not_found, "failed to resolve provider address {}", provider_raw) })?; let code_id = rt - .get_actor_code_cid(&provider) - .ok_or_else(|| actor_error!(illegal_argument, "no code ID for address {}", provider))?; + .get_actor_code_cid(&provider_id) + .ok_or_else(|| actor_error!(not_found, "no code ID for address {}", provider_id))?; if rt.resolve_builtin_actor_type(&code_id) != Some(Type::Miner) { return Err(actor_error!( @@ -245,7 +245,7 @@ impl Actor { )); } - let (_, worker, controllers) = request_miner_control_addrs(rt, provider)?; + let (_, worker, controllers) = request_miner_control_addrs(rt, provider_id)?; let caller = rt.message().caller(); let mut caller_ok = caller == worker; for controller in controllers.iter() { @@ -259,7 +259,7 @@ impl Actor { forbidden, "caller {} is not worker or control address of provider {}", caller, - provider + provider_id )); } @@ -291,14 +291,16 @@ impl Actor { continue; } - if deal.proposal.provider != provider && deal.proposal.provider != provider_raw { + if deal.proposal.provider != Address::new_id(provider_id) + && deal.proposal.provider != provider_raw + { info!( "invalid deal {}: cannot publish deals from multiple providers in one batch", di ); continue; } - let client = match rt.resolve_address(&deal.proposal.client) { + let client_id = match rt.resolve_address(&deal.proposal.client) { Some(client) => client, _ => { info!( @@ -310,17 +312,17 @@ impl Actor { }; // drop deals with insufficient lock up to cover costs - let client_id = client.id().expect("resolved address should be an ID address"); let mut client_lockup = total_client_lockup.get(&client_id).cloned().unwrap_or_default(); client_lockup += deal.proposal.client_balance_requirement(); - let client_balance_ok = msm.balance_covered(client, &client_lockup).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to check client balance coverage", - ) - })?; + let client_balance_ok = + msm.balance_covered(Address::new_id(client_id), &client_lockup).map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_STATE, + "failed to check client balance coverage", + ) + })?; if !client_balance_ok { info!("invalid deal: {}: insufficient client funds to cover proposal cost", di); @@ -329,8 +331,9 @@ impl Actor { let mut provider_lockup = total_provider_lockup.clone(); provider_lockup += &deal.proposal.provider_collateral; - let provider_balance_ok = - msm.balance_covered(provider, &provider_lockup).map_err(|e| { + let provider_balance_ok = msm + .balance_covered(Address::new_id(provider_id), &provider_lockup) + .map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, "failed to check provider balance coverage", @@ -346,9 +349,11 @@ impl Actor { // Normalise provider and client addresses in the proposal stored on chain. // Must happen after signature verification and before taking cid. - deal.proposal.provider = provider; - deal.proposal.client = client; - let pcid = cid(rt, &deal.proposal)?; + deal.proposal.provider = Address::new_id(provider_id); + deal.proposal.client = Address::new_id(client_id); + let pcid = deal.proposal.cid().map_err( + |e| actor_error!(illegal_argument; "failed to take cid of proposal {}: {}", di, e), + )?; // check proposalCids for duplication within message batch // check state PendingProposals for duplication across messages @@ -372,7 +377,7 @@ impl Actor { *VERIFIED_REGISTRY_ACTOR_ADDR, crate::ext::verifreg::USE_BYTES_METHOD as u64, RawBytes::serialize(UseBytesParams { - address: client, + address: Address::new_id(client_id), deal_size: BigInt::from(deal.proposal.piece_size.0), })?, TokenAmount::zero(), @@ -1290,13 +1295,15 @@ where .get_actor_code_cid(&nominal) .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", nominal))?; + let nominal_addr = Address::new_id(nominal); + if rt.resolve_builtin_actor_type(&code_id) == Some(Type::Miner) { // Storage miner actor entry; implied funds recipient is the associated owner address. let (owner_addr, worker_addr, _) = request_miner_control_addrs(rt, nominal)?; - return Ok((nominal, owner_addr, vec![owner_addr, worker_addr])); + return Ok((nominal_addr, owner_addr, vec![owner_addr, worker_addr])); } - Ok((nominal, nominal, vec![nominal])) + Ok((nominal_addr, nominal_addr, vec![nominal_addr])) } /// Requests the current epoch target block reward from the reward actor. diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 3e10d4819..6b81beee8 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -3891,7 +3891,7 @@ where )); } - Ok(resolved) + Ok(Address::new_id(resolved)) } /// Resolves an address to an ID address and verifies that it is address of an account actor with an associated BLS key. @@ -3918,7 +3918,7 @@ where if raw.protocol() != Protocol::BLS { let ret = rt.send( - resolved, + Address::new_id(resolved), ext::account::PUBKEY_ADDRESS_METHOD, RawBytes::default(), TokenAmount::zero(), @@ -3933,7 +3933,7 @@ where )); } } - Ok(resolved) + Ok(Address::new_id(resolved)) } fn burn_funds(rt: &mut RT, amount: TokenAmount) -> Result<(), ActorError> diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 7c06c1520..1760fb0b5 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -6,7 +6,7 @@ use std::collections::BTreeSet; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_id_addr, ActorDowncast, + actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorDowncast, ActorError, Map, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; @@ -73,18 +73,18 @@ impl Actor { let mut resolved_signers = Vec::with_capacity(params.signers.len()); let mut dedup_signers = BTreeSet::new(); for signer in ¶ms.signers { - let resolved = resolve_to_id_addr(rt, signer).map_err(|e| { + let resolved = resolve_to_actor_id(rt, signer).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", signer), + format!("failed to resolve addr {} to ID", signer), ) })?; - if !dedup_signers.insert(resolved.id().expect("address should be resolved")) { + if !dedup_signers.insert(resolved) { return Err( actor_error!(illegal_argument; "duplicate signer not allowed: {}", signer), ); } - resolved_signers.push(resolved); + resolved_signers.push(Address::new_id(resolved)); } if params.num_approvals_threshold > params.signers.len() as u64 { @@ -299,10 +299,10 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_new_signer = resolve_to_id_addr(rt, ¶ms.signer).map_err(|e| { + let resolved_new_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {}", params.signer), + format!("failed to resolve address {} to ID", params.signer), ) })?; @@ -314,12 +314,12 @@ impl Actor { SIGNERS_MAX )); } - if st.is_signer(&resolved_new_signer) { + if st.is_signer(&Address::new_id(resolved_new_signer)) { return Err(actor_error!(forbidden, "{} is already a signer", resolved_new_signer)); } // Add signer and increase threshold if set - st.signers.push(resolved_new_signer); + st.signers.push(Address::new_id(resolved_new_signer)); if params.increase { st.num_approvals_threshold += 1; } @@ -336,15 +336,15 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_old_signer = resolve_to_id_addr(rt, ¶ms.signer).map_err(|e| { + let resolved_old_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {}", params.signer), + format!("failed to resolve address {} to ID", params.signer), ) })?; rt.transaction(|st: &mut State, rt| { - if !st.is_signer(&resolved_old_signer) { + if !st.is_signer(&Address::new_id(resolved_old_signer)) { return Err(actor_error!(forbidden, "{} is not a signer", resolved_old_signer)); } @@ -374,13 +374,13 @@ impl Actor { } // Remove approvals from removed signer - st.purge_approvals(rt.store(), &resolved_old_signer).map_err(|e| { + st.purge_approvals(rt.store(), &Address::new_id(resolved_old_signer)).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, "failed to purge approvals of removed signer", ) })?; - st.signers.retain(|s| s != &resolved_old_signer); + st.signers.retain(|s| s != &Address::new_id(resolved_old_signer)); Ok(()) })?; @@ -396,35 +396,35 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let from_resolved = resolve_to_id_addr(rt, ¶ms.from).map_err(|e| { + let from_resolved = resolve_to_actor_id(rt, ¶ms.from).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {}", params.from), + format!("failed to resolve address {} to ID", params.from), ) })?; - let to_resolved = resolve_to_id_addr(rt, ¶ms.to).map_err(|e| { + let to_resolved = resolve_to_actor_id(rt, ¶ms.to).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {}", params.to), + format!("failed to resolve address {} to ID", params.to), ) })?; rt.transaction(|st: &mut State, rt| { - if !st.is_signer(&from_resolved) { + if !st.is_signer(&Address::new_id(from_resolved)) { return Err(actor_error!(forbidden; "{} is not a signer", from_resolved)); } - if st.is_signer(&to_resolved) { + if st.is_signer(&Address::new_id(to_resolved)) { return Err(actor_error!(illegal_argument; "{} is already a signer", to_resolved)); } // Remove signer from state (retain preserves order of elements) - st.signers.retain(|s| s != &from_resolved); + st.signers.retain(|s| s != &Address::new_id(from_resolved)); // Add new signer - st.signers.push(to_resolved); + st.signers.push(Address::new_id(to_resolved)); - st.purge_approvals(rt.store(), &from_resolved).map_err(|e| { + st.purge_approvals(rt.store(), &Address::new_id(from_resolved)).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, "failed to purge approvals of removed signer", diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 792713963..5a2b754be 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -3,7 +3,9 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, cbor, resolve_to_id_addr, ActorDowncast, ActorError, Array}; +use fil_actors_runtime::{ + actor_error, cbor, resolve_to_actor_id, ActorDowncast, ActorError, Array, +}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -73,10 +75,10 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let resolved = resolve_to_id_addr(rt, raw).map_err(|e| { + let resolved = resolve_to_actor_id(rt, raw).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {}", raw), + format!("failed to resolve address {} to ID", raw), ) })?; @@ -94,7 +96,7 @@ impl Actor { typ )) } else { - Ok(resolved) + Ok(Address::new_id(resolved)) } } @@ -140,17 +142,17 @@ impl Actor { })?; let pch_addr = rt.message().receiver(); - let svpch_id_addr = rt.resolve_address(&sv.channel_addr).ok_or_else(|| { + let svpch_id = rt.resolve_address(&sv.channel_addr).ok_or_else(|| { actor_error!( illegal_argument, "voucher payment channel address {} does not resolve to an ID address", sv.channel_addr ) })?; - if pch_addr != svpch_id_addr { + if pch_addr != Address::new_id(svpch_id) { return Err(actor_error!(illegal_argument; "voucher payment channel address {} does not match receiver {}", - svpch_id_addr, pch_addr)); + svpch_id, pch_addr)); } if rt.curr_epoch() < sv.time_lock_min { diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index d79f54d35..0018ea889 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -8,6 +8,7 @@ use fil_actors_runtime::{ }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntDe; use fvm_shared::bigint::{Integer, Sign}; use fvm_shared::econ::TokenAmount; @@ -113,7 +114,7 @@ impl Actor { return Err(actor_error!(illegal_argument, "invalid win count {}", params.win_count)); } - let miner_addr = rt + let miner_id = rt .resolve_address(¶ms.miner) .ok_or_else(|| actor_error!(not_found, "failed to resolve given owner address"))?; @@ -158,7 +159,7 @@ impl Actor { // if this fails, we can assume the miner is responsible and avoid failing here. let reward_params = ext::miner::ApplyRewardParams { reward: total_reward.clone(), penalty }; let res = rt.send( - miner_addr, + Address::new_id(miner_id), ext::miner::APPLY_REWARDS_METHOD, RawBytes::serialize(&reward_params)?, total_reward.clone(), diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index 6f14f3265..0d6205ca8 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -3,7 +3,7 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_id_addr, ActorDowncast, + actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_actor_id, ActorDowncast, ActorError, Map, STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; @@ -57,7 +57,7 @@ impl Actor { .resolve_address(&root_key) .ok_or_else(|| actor_error!(illegal_argument, "root should be an ID address"))?; - let st = State::new(rt.store(), id_addr).map_err(|e| { + let st = State::new(rt.store(), Address::new_id(id_addr)).map_err(|e| { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create verifreg state") })?; @@ -79,13 +79,15 @@ impl Actor { )); } - let verifier = resolve_to_id_addr(rt, ¶ms.address).map_err(|e| { + let verifier = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", params.address), + format!("failed to resolve addr {} to ID", params.address), ) })?; + let verifier = Address::new_id(verifier); + let st: State = rt.state()?; rt.validate_immediate_caller_is(std::iter::once(&st.root_key))?; @@ -143,13 +145,15 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let verifier = resolve_to_id_addr(rt, &verifier_addr).map_err(|e| { + let verifier = resolve_to_actor_id(rt, &verifier_addr).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", verifier_addr), + format!("failed to resolve addr {} to ID", verifier_addr), ) })?; + let verifier = Address::new_id(verifier); + let state: State = rt.state()?; rt.validate_immediate_caller_is(std::iter::once(&state.root_key))?; @@ -200,13 +204,15 @@ impl Actor { )); } - let client = resolve_to_id_addr(rt, ¶ms.address).map_err(|e| { + let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", params.address), + format!("failed to resolve addr {} to ID", params.address), ) })?; + let client = Address::new_id(client); + let st: State = rt.state()?; if client == st.root_key { return Err(actor_error!(illegal_argument, "Rootkey cannot be added as verifier")); @@ -321,13 +327,15 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_MARKET_ACTOR_ADDR))?; - let client = resolve_to_id_addr(rt, ¶ms.address).map_err(|e| { + let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", params.address), + format!("failed to resolve addr {} to ID", params.address), ) })?; + let client = Address::new_id(client); + if params.deal_size < rt.policy().minimum_verified_deal_size { return Err(actor_error!( illegal_argument, @@ -429,13 +437,15 @@ impl Actor { )); } - let client = resolve_to_id_addr(rt, ¶ms.address).map_err(|e| { + let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID addr", params.address), + format!("failed to resolve addr {} to ID", params.address), ) })?; + let client = Address::new_id(client); + let st: State = rt.state()?; if client == st.root_key { return Err(actor_error!(illegal_argument, "Cannot restore allowance for Rootkey")); @@ -510,38 +520,41 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let client = resolve_to_id_addr(rt, ¶ms.verified_client_to_remove).map_err(|e| { + let client = resolve_to_actor_id(rt, ¶ms.verified_client_to_remove).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to resolve client addr {} to ID addr", - params.verified_client_to_remove - ), + format!("failed to resolve client addr {} to ID", params.verified_client_to_remove), ) })?; + let client = Address::new_id(client); + let verifier_1 = - resolve_to_id_addr(rt, ¶ms.verifier_request_1.verifier).map_err(|e| { + resolve_to_actor_id(rt, ¶ms.verifier_request_1.verifier).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_ARGUMENT, format!( - "failed to resolve verifier addr {} to ID addr", + "failed to resolve verifier addr {} to ID", params.verifier_request_1.verifier ), ) })?; + let verifier_1 = Address::new_id(verifier_1); + let verifier_2 = - resolve_to_id_addr(rt, ¶ms.verifier_request_2.verifier).map_err(|e| { + resolve_to_actor_id(rt, ¶ms.verifier_request_2.verifier).map_err(|e| { e.downcast_default( ExitCode::USR_ILLEGAL_ARGUMENT, format!( - "failed to resolve verifier addr {} to ID addr", + "failed to resolve verifier addr {} to ID", params.verifier_request_2.verifier ), ) })?; + let verifier_2 = Address::new_id(verifier_2); + if verifier_1 == verifier_2 { return Err(actor_error!( illegal_argument, diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index e2ee7a39f..8e8617534 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -1,8 +1,10 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use anyhow::Ok; use fvm_ipld_blockstore::Blockstore; use fvm_shared::address::Address; +use fvm_shared::ActorID; use fvm_shared::METHOD_SEND; use crate::runtime::builtins::Type; @@ -15,27 +17,29 @@ pub const HAMT_BIT_WIDTH: u32 = 5; /// user-programmable actors. pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; -/// ResolveToIDAddr resolves the given address to it's ID address form. -/// If an ID address for the given address dosen't exist yet, it tries to create one by sending +/// ResolveToActorID resolves the given address to it's actor ID. +/// If an actor ID for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. -pub fn resolve_to_id_addr(rt: &mut RT, address: &Address) -> anyhow::Result
+pub fn resolve_to_actor_id(rt: &mut RT, address: &Address) -> anyhow::Result where BS: Blockstore, RT: Runtime, { // if we are able to resolve it to an ID address, return the resolved address - if let Some(addr) = rt.resolve_address(address) { - return Ok(addr); + if let Some(id) = rt.resolve_address(address) { + return Ok(id); } // send 0 balance to the account so an ID address for it is created and then try to resolve rt.send(*address, METHOD_SEND, Default::default(), Default::default()) .map_err(|e| e.wrap(&format!("failed to send zero balance to address {}", address)))?; - rt.resolve_address(address).ok_or_else(|| { - anyhow::anyhow!( - "failed to resolve address {} to ID address even after sending zero balance", - address, - ) - }) + if let Some(id) = rt.resolve_address(address) { + return Ok(id); + } + + Err(anyhow::anyhow!( + "failed to resolve address {} to ID even after sending zero balance", + address, + )) } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index d43d7186e..ca8be3ced 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -144,7 +144,8 @@ where self.assert_not_validated()?; let caller_cid = { let caller_addr = self.message().caller(); - self.get_actor_code_cid(&caller_addr).expect("failed to lookup caller code") + self.get_actor_code_cid(&caller_addr.id().unwrap()) + .expect("failed to lookup caller code") }; match self.resolve_builtin_actor_type(&caller_cid) { @@ -161,12 +162,12 @@ where fvm::sself::current_balance() } - fn resolve_address(&self, address: &Address) -> Option
{ - fvm::actor::resolve_address(address).map(Address::new_id) + fn resolve_address(&self, address: &Address) -> Option { + fvm::actor::resolve_address(address) } - fn get_actor_code_cid(&self, addr: &Address) -> Option { - fvm::actor::get_actor_code_cid(addr) + fn get_actor_code_cid(&self, id: &ActorID) -> Option { + fvm::actor::get_actor_code_cid(&Address::new_id(*id)) } fn resolve_builtin_actor_type(&self, code_id: &Cid) -> Option { diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 8cf273a28..cbe2654fc 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -62,10 +62,10 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Resolves an address of any protocol to an ID address (via the Init actor's table). /// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form. /// If the argument is an ID address it is returned directly. - fn resolve_address(&self, address: &Address) -> Option
; + fn resolve_address(&self, address: &Address) -> Option; /// Look up the code ID at an actor address. - fn get_actor_code_cid(&self, addr: &Address) -> Option; + fn get_actor_code_cid(&self, id: &ActorID) -> Option; /// Randomness returns a (pseudo)random byte array drawing from the latest /// ticket chain from a given epoch and incorporating requisite entropy. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 0c9db9d3b..7857c3c97 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -11,6 +11,7 @@ use cid::Cid; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; +use fvm_shared::address::Payload; use fvm_shared::address::{Address, Protocol}; use fvm_shared::clock::ChainEpoch; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; @@ -776,17 +777,26 @@ impl Runtime for MockRuntime { self.balance.borrow().clone() } - fn resolve_address(&self, address: &Address) -> Option
{ + fn resolve_address(&self, address: &Address) -> Option { self.require_in_call(); - if address.protocol() == Protocol::ID { - return Some(*address); + if let &Payload::ID(id) = address.payload() { + return Some(id); + } + + match self.get_id_address(address) { + None => None, + Some(addr) => { + if let &Payload::ID(id) = addr.payload() { + return Some(id); + } + None + } } - self.id_addresses.get(address).cloned() } - fn get_actor_code_cid(&self, addr: &Address) -> Option { + fn get_actor_code_cid(&self, id: &ActorID) -> Option { self.require_in_call(); - self.actor_code_cids.get(addr).cloned() + self.actor_code_cids.get(&Address::new_id(*id)).cloned() } fn get_randomness_from_tickets( diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs index 500245ea3..e6bf0a643 100644 --- a/runtime/src/util/chaos/mod.rs +++ b/runtime/src/util/chaos/mod.rs @@ -137,10 +137,7 @@ impl Actor { { rt.validate_immediate_caller_accept_any()?; let resolved = rt.resolve_address(&args); - Ok(ResolveAddressResponse { - address: resolved.unwrap_or_else(|| Address::new_id(0)), - success: resolved.is_some(), - }) + Ok(ResolveAddressResponse { id: resolved.unwrap_or(0), success: resolved.is_some() }) } pub fn delete_actor(rt: &mut RT, beneficiary: Address) -> Result<(), ActorError> diff --git a/runtime/src/util/chaos/types.rs b/runtime/src/util/chaos/types.rs index bd953e75e..c61bb6fb6 100644 --- a/runtime/src/util/chaos/types.rs +++ b/runtime/src/util/chaos/types.rs @@ -25,7 +25,7 @@ pub struct CreateActorArgs { /// Holds the response of a call to runtime.ResolveAddress #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ResolveAddressResponse { - pub address: Address, + pub id: ActorID, pub success: bool, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 19f2d19c5..805b7fab0 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -33,6 +33,7 @@ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; +use fvm_shared::address::Payload; use fvm_shared::address::{Address, Protocol}; use fvm_shared::bigint::{bigint_ser, BigInt, Zero}; use fvm_shared::clock::ChainEpoch; @@ -768,12 +769,17 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat self.v.get_actor(self.to()).unwrap().balance } - fn resolve_address(&self, addr: &Address) -> Option
{ - self.v.normalize_address(addr) + fn resolve_address(&self, addr: &Address) -> Option { + if let Some(normalize_addr) = self.v.normalize_address(addr) { + if let &Payload::ID(id) = normalize_addr.payload() { + return Some(id); + } + } + None } - fn get_actor_code_cid(&self, addr: &Address) -> Option { - let maybe_act = self.v.get_actor(*addr); + fn get_actor_code_cid(&self, id: &ActorID) -> Option { + let maybe_act = self.v.get_actor(Address::new_id(*id)); match maybe_act { None => None, Some(act) => Some(act.code), From 79e399cd2e591a858771bb3cf82f8548b999c129 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 29 Aug 2022 23:34:33 +0200 Subject: [PATCH 026/339] Optimize test_vm get_actor (#585) * Optimize test_vm get_actor Improves extend_sector_with_deals by 40% Signed-off-by: Jakub Sztandera * Keep the cache across invocations Gives another 32% Signed-off-by: Jakub Sztandera Signed-off-by: Jakub Sztandera --- test_vm/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 805b7fab0..673bf629a 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -329,7 +329,11 @@ impl<'bs> VM<'bs> { self.store, ) .unwrap(); - actors.get(&addr.to_bytes()).unwrap().cloned() + let actor = actors.get(&addr.to_bytes()).unwrap().cloned(); + actor.iter().for_each(|a| { + self.actors_cache.borrow_mut().insert(addr, a.clone()); + }); + actor } // blindly overwrite the actor at this address whether it previously existed or not @@ -349,8 +353,8 @@ impl<'bs> VM<'bs> { actors.set(addr.to_bytes().into(), act.clone()).unwrap(); } - // roll "back" to latest head, flushing cache - self.rollback(actors.flush().unwrap()); + self.state_root.replace(actors.flush().unwrap()); + self.actors_dirty.replace(false); *self.state_root.borrow() } From 422a48ac2432b1cc6eb12cc43af53136f5334ae4 Mon Sep 17 00:00:00 2001 From: Swift Liu <74490266+lyswifter@users.noreply.github.com> Date: Tue, 30 Aug 2022 06:03:36 +0800 Subject: [PATCH 027/339] Change runtime address parameters to a reference (#581) --- actors/cron/src/lib.rs | 2 +- actors/init/src/lib.rs | 2 +- actors/market/src/lib.rs | 14 +++++++------- actors/miner/src/lib.rs | 31 ++++++++++++++++--------------- actors/multisig/src/lib.rs | 2 +- actors/paych/src/lib.rs | 4 ++-- actors/power/src/lib.rs | 10 +++++----- actors/reward/src/lib.rs | 4 ++-- runtime/src/builtin/shared.rs | 2 +- runtime/src/runtime/fvm.rs | 4 ++-- runtime/src/runtime/mod.rs | 2 +- runtime/src/test_utils.rs | 4 ++-- runtime/src/util/chaos/mod.rs | 2 +- test_vm/src/lib.rs | 6 +++--- 14 files changed, 45 insertions(+), 44 deletions(-) diff --git a/actors/cron/src/lib.rs b/actors/cron/src/lib.rs index 395cb6971..72120f687 100644 --- a/actors/cron/src/lib.rs +++ b/actors/cron/src/lib.rs @@ -65,7 +65,7 @@ impl Actor { for entry in st.entries { // Intentionally ignore any error when calling cron methods let res = rt.send( - entry.receiver, + &entry.receiver, entry.method_num, RawBytes::default(), TokenAmount::from(0u8), diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index f302c9605..eef66440b 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -97,7 +97,7 @@ impl Actor { // Invoke constructor rt.send( - Address::new_id(id_address), + &Address::new_id(id_address), METHOD_CONSTRUCTOR, params.constructor_params, rt.message().value_received(), diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 63520f79a..aa8b8820a 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -61,7 +61,7 @@ where RT: Runtime, { let ret = rt.send( - Address::new_id(miner_id), + &Address::new_id(miner_id), ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), TokenAmount::zero(), @@ -207,7 +207,7 @@ impl Actor { Ok(ex) })?; - rt.send(recipient, METHOD_SEND, RawBytes::default(), amount_extracted.clone())?; + rt.send(&recipient, METHOD_SEND, RawBytes::default(), amount_extracted.clone())?; Ok(WithdrawBalanceReturn { amount_withdrawn: amount_extracted }) } @@ -374,7 +374,7 @@ impl Actor { // drop deals with a DealSize that cannot be fully covered by VerifiedClient's available DataCap if deal.proposal.verified_deal { if let Err(e) = rt.send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + &VERIFIED_REGISTRY_ACTOR_ADDR, crate::ext::verifreg::USE_BYTES_METHOD as u64, RawBytes::serialize(UseBytesParams { address: Address::new_id(client_id), @@ -1024,7 +1024,7 @@ impl Actor { for d in timed_out_verified_deals { let res = rt.send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + &VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::RESTORE_BYTES_METHOD, RawBytes::serialize(ext::verifreg::RestoreBytesParams { address: d.client, @@ -1046,7 +1046,7 @@ impl Actor { } if !amount_slashed.is_zero() { - rt.send(*BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), amount_slashed)?; + rt.send(&BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), amount_slashed)?; } Ok(()) } @@ -1313,7 +1313,7 @@ where RT: Runtime, { let rwret = rt.send( - *REWARD_ACTOR_ADDR, + &REWARD_ACTOR_ADDR, ext::reward::THIS_EPOCH_REWARD_METHOD, RawBytes::default(), 0.into(), @@ -1332,7 +1332,7 @@ where RT: Runtime, { let rwret = rt.send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::CURRENT_TOTAL_POWER_METHOD, RawBytes::default(), 0.into(), diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 6b81beee8..d89b426ed 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -984,7 +984,7 @@ impl Actor { } let res = rt.send( - *STORAGE_MARKET_ACTOR_ADDR, + &STORAGE_MARKET_ACTOR_ADDR, ext::market::ACTIVATE_DEALS_METHOD, RawBytes::serialize(ext::market::ActivateDealsParams { deal_ids: update.deals.clone(), @@ -1505,7 +1505,8 @@ impl Actor { request_update_power(rt, power_delta)?; if !to_reward.is_zero() { - if let Err(e) = rt.send(reporter, METHOD_SEND, RawBytes::default(), to_reward.clone()) { + if let Err(e) = rt.send(&reporter, METHOD_SEND, RawBytes::default(), to_reward.clone()) + { error!("failed to send reward: {}", e); to_burn += to_reward; } @@ -1863,7 +1864,7 @@ impl Actor { )?; rt.send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::SUBMIT_POREP_FOR_BULK_VERIFY_METHOD, RawBytes::serialize(&svi)?, BigInt::zero(), @@ -3056,7 +3057,7 @@ impl Actor { Ok((burn_amount, reward_amount)) })?; - if let Err(e) = rt.send(reporter, METHOD_SEND, RawBytes::default(), reward_amount) { + if let Err(e) = rt.send(&reporter, METHOD_SEND, RawBytes::default(), reward_amount) { error!("failed to send reward: {}", e); } @@ -3144,7 +3145,7 @@ impl Actor { } if amount_withdrawn.is_positive() { - rt.send(info.owner, METHOD_SEND, RawBytes::default(), amount_withdrawn.clone())?; + rt.send(&info.owner, METHOD_SEND, RawBytes::default(), amount_withdrawn.clone())?; } burn_funds(rt, fee_to_burn)?; @@ -3574,7 +3575,7 @@ where let ser_params = serialize(&ext::power::EnrollCronEventParams { event_epoch, payload }, "cron params")?; rt.send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::ENROLL_CRON_EVENT_METHOD, ser_params, TokenAmount::zero(), @@ -3595,7 +3596,7 @@ where let delta_clone = delta.clone(); rt.send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::UPDATE_CLAIMED_POWER_METHOD, RawBytes::serialize(ext::power::UpdateClaimedPowerParams { raw_byte_delta: delta.raw, @@ -3621,7 +3622,7 @@ where for chunk in deal_ids.chunks(MAX_LENGTH) { rt.send( - *STORAGE_MARKET_ACTOR_ADDR, + &STORAGE_MARKET_ACTOR_ADDR, ext::market::ON_MINER_SECTORS_TERMINATE_METHOD, RawBytes::serialize(ext::market::OnMinerSectorsTerminateParamsRef { epoch, @@ -3811,7 +3812,7 @@ where return Ok(empty_result); } let serialized = rt.send( - *STORAGE_MARKET_ACTOR_ADDR, + &STORAGE_MARKET_ACTOR_ADDR, ext::market::VERIFY_DEALS_FOR_ACTIVATION_METHOD, RawBytes::serialize(ext::market::VerifyDealsForActivationParamsRef { sectors })?, TokenAmount::zero(), @@ -3831,7 +3832,7 @@ where { let ret = rt .send( - *REWARD_ACTOR_ADDR, + &REWARD_ACTOR_ADDR, ext::reward::THIS_EPOCH_REWARD_METHOD, Default::default(), TokenAmount::zero(), @@ -3852,7 +3853,7 @@ where { let ret = rt .send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::CURRENT_TOTAL_POWER_METHOD, Default::default(), TokenAmount::zero(), @@ -3918,7 +3919,7 @@ where if raw.protocol() != Protocol::BLS { let ret = rt.send( - Address::new_id(resolved), + &Address::new_id(resolved), ext::account::PUBKEY_ADDRESS_METHOD, RawBytes::default(), TokenAmount::zero(), @@ -3943,7 +3944,7 @@ where { log::debug!("storage provder {} burning {}", rt.message().receiver(), amount); if amount.is_positive() { - rt.send(*BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), amount)?; + rt.send(&BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), amount)?; } Ok(()) } @@ -3955,7 +3956,7 @@ where { if !pledge_delta.is_zero() { rt.send( - *STORAGE_POWER_ACTOR_ADDR, + &STORAGE_POWER_ACTOR_ADDR, ext::power::UPDATE_PLEDGE_TOTAL_METHOD, RawBytes::serialize(BigIntSer(pledge_delta))?, TokenAmount::zero(), @@ -4245,7 +4246,7 @@ where if !pre_commit.info.deal_ids.is_empty() { // Check (and activate) storage deals associated to sector. Abort if checks failed. let res = rt.send( - *STORAGE_MARKET_ACTOR_ADDR, + &STORAGE_MARKET_ACTOR_ADDR, ext::market::ACTIVATE_DEALS_METHOD, RawBytes::serialize(ext::market::ActivateDealsParams { deal_ids: pre_commit.info.deal_ids.clone(), diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 1760fb0b5..726db39e7 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -561,7 +561,7 @@ where st.check_available(rt.current_balance(), &txn.value, rt.curr_epoch()) .map_err(|e| actor_error!(insufficient_funds, "insufficient funds unlocked: {}", e))?; - match rt.send(txn.to, txn.method, txn.params.clone(), txn.value.clone()) { + match rt.send(&txn.to, txn.method, txn.params.clone(), txn.value.clone()) { Ok(ser) => { out = ser; } diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 5a2b754be..7ccde472f 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -176,7 +176,7 @@ impl Actor { } if let Some(extra) = &sv.extra { - rt.send(extra.actor, extra.method, extra.data.clone(), TokenAmount::from(0u8)) + rt.send(&extra.actor, extra.method, extra.data.clone(), TokenAmount::from(0u8)) .map_err(|e| e.wrap("spend voucher verification failed"))?; } @@ -313,7 +313,7 @@ impl Actor { } // send ToSend to `to` - rt.send(st.to, METHOD_SEND, RawBytes::default(), st.to_send) + rt.send(&st.to, METHOD_SEND, RawBytes::default(), st.to_send) .map_err(|e| e.wrap("Failed to send funds to `to` address"))?; // the remaining balance will be returned to "From" upon deletion. diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 440b16229..91a9f4127 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -107,7 +107,7 @@ impl Actor { let miner_actor_code_cid = rt.get_code_cid_for_type(Type::Miner); let ext::init::ExecReturn { id_address, robust_address } = rt .send( - *INIT_ACTOR_ADDR, + &INIT_ACTOR_ADDR, ext::init::EXEC_METHOD, RawBytes::serialize(init::ExecParams { code_cid: miner_actor_code_cid, @@ -253,7 +253,7 @@ impl Actor { let rewret: ThisEpochRewardReturn = rt .send( - *REWARD_ACTOR_ADDR, + &REWARD_ACTOR_ADDR, ext::reward::Method::ThisEpochReward as MethodNum, RawBytes::default(), TokenAmount::zero(), @@ -279,7 +279,7 @@ impl Actor { // Update network KPA in reward actor rt.send( - *REWARD_ACTOR_ADDR, + &REWARD_ACTOR_ADDR, ext::reward::UPDATE_NETWORK_KPI, this_epoch_raw_byte_power?, TokenAmount::from(0_u32), @@ -513,7 +513,7 @@ impl Actor { continue; } if let Err(e) = rt.send( - m, + &m, ext::miner::CONFIRM_SECTOR_PROOFS_VALID_METHOD, RawBytes::serialize(&ext::miner::ConfirmSectorProofsParams { sectors: successful, @@ -608,7 +608,7 @@ impl Actor { quality_adj_power_smoothed: st.this_epoch_qa_power_smoothed.clone(), })?; let res = rt.send( - event.miner_addr, + &event.miner_addr, ext::miner::ON_DEFERRED_CRON_EVENT_METHOD, params, Default::default(), diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index 0018ea889..5852f87eb 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -159,7 +159,7 @@ impl Actor { // if this fails, we can assume the miner is responsible and avoid failing here. let reward_params = ext::miner::ApplyRewardParams { reward: total_reward.clone(), penalty }; let res = rt.send( - Address::new_id(miner_id), + &Address::new_id(miner_id), ext::miner::APPLY_REWARDS_METHOD, RawBytes::serialize(&reward_params)?, total_reward.clone(), @@ -171,7 +171,7 @@ impl Actor { e.exit_code() ); let res = - rt.send(*BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), total_reward); + rt.send(&BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), total_reward); if let Err(e) = res { error!( "failed to send unsent reward to the burnt funds actor, code: {:?}", diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 8e8617534..4b5b0d14e 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -31,7 +31,7 @@ where } // send 0 balance to the account so an ID address for it is created and then try to resolve - rt.send(*address, METHOD_SEND, Default::default(), Default::default()) + rt.send(address, METHOD_SEND, Default::default(), Default::default()) .map_err(|e| e.wrap(&format!("failed to send zero balance to address {}", address)))?; if let Some(id) = rt.resolve_address(address) { diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index ca8be3ced..4937a7900 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -285,7 +285,7 @@ where fn send( &self, - to: Address, + to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, @@ -293,7 +293,7 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - match fvm::send::send(&to, method, params, value) { + match fvm::send::send(to, method, params, value) { Ok(ret) => { if ret.exit_code.is_success() { Ok(ret.return_data) diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index cbe2654fc..6b0faabd5 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -118,7 +118,7 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// invoking the target actor/method. fn send( &self, - to: Address, + to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 7857c3c97..4375df332 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -901,7 +901,7 @@ impl Runtime for MockRuntime { fn send( &self, - to: Address, + to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, @@ -923,7 +923,7 @@ impl Runtime for MockRuntime { let expected_msg = self.expectations.borrow_mut().expect_sends.pop_front().unwrap(); assert!( - expected_msg.to == to + expected_msg.to == *to && expected_msg.method == method && expected_msg.params == params && expected_msg.value == value, diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs index e6bf0a643..de8f4a807 100644 --- a/runtime/src/util/chaos/mod.rs +++ b/runtime/src/util/chaos/mod.rs @@ -57,7 +57,7 @@ impl Actor { { rt.validate_immediate_caller_accept_any()?; - let result = rt.send(arg.to, arg.method, arg.params, arg.value); + 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 { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 673bf629a..fee6c2fdb 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -526,7 +526,7 @@ impl MessageInfo for InvocationCtx<'_, '_> { self.msg.from } fn origin(&self) -> Address { - self.resolve_address(&self.top.originator_stable_addr).unwrap() + Address::new_id(self.resolve_address(&self.top.originator_stable_addr).unwrap()) } fn receiver(&self) -> Address { self.to() @@ -792,7 +792,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat fn send( &self, - to: Address, + to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, @@ -804,7 +804,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat )); } - let new_actor_msg = InternalMessage { from: self.to(), to, value, method, params }; + let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; let mut new_ctx = InvocationCtx { v: self.v, top: self.top.clone(), From dcc66437ef9eacb64132c7ed2cd21fdf5c4b6857 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Aug 2022 12:09:31 -0700 Subject: [PATCH 028/339] feat: update sdk & shared for TokenAmount and hashing changes (#588) --- Cargo.lock | 136 ++++++++------- Cargo.toml | 16 +- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/cron/src/lib.rs | 4 +- actors/cron/tests/cron_actor_test.rs | 10 +- actors/evm/Cargo.toml | 4 +- actors/evm/src/interpreter/uints.rs | 2 +- actors/init/Cargo.toml | 2 +- actors/init/tests/init_actor_test.rs | 13 +- actors/market/Cargo.toml | 2 +- actors/market/src/balance_table.rs | 57 ++++--- actors/market/src/deal.rs | 4 - actors/market/src/ext.rs | 1 - actors/market/src/lib.rs | 32 ++-- actors/market/src/policy.rs | 12 +- actors/market/src/state.rs | 8 +- actors/market/src/testing.rs | 10 +- actors/market/src/types.rs | 2 - actors/market/tests/cron_tick_deal_expiry.rs | 2 - .../market/tests/cron_tick_deal_slashing.rs | 10 +- actors/market/tests/harness.rs | 40 ++--- actors/market/tests/market_actor_test.rs | 122 +++++++------- .../tests/publish_storage_deals_failures.rs | 37 +++-- .../tests/random_cron_epoch_during_publish.rs | 2 - actors/miner/Cargo.toml | 2 +- actors/miner/src/expiration_queue.rs | 3 +- actors/miner/src/expiration_queue/tests.rs | 2 +- actors/miner/src/ext.rs | 1 - actors/miner/src/internal_tests.rs | 22 +-- actors/miner/src/lib.rs | 20 +-- actors/miner/src/monies.rs | 72 ++++---- actors/miner/src/policy.rs | 7 +- actors/miner/src/state.rs | 8 +- actors/miner/src/testing.rs | 15 +- actors/miner/src/types.rs | 9 - actors/miner/src/vesting_state.rs | 9 +- .../miner/tests/aggregate_network_fee_test.rs | 52 +++--- actors/miner/tests/aggregate_prove_commit.rs | 11 +- actors/miner/tests/apply_rewards.rs | 28 ++-- .../tests/batch_method_network_fees_test.rs | 49 +++--- .../miner/tests/change_owner_address_test.rs | 5 +- actors/miner/tests/change_peer_id_test.rs | 4 +- .../miner/tests/change_worker_address_test.rs | 3 +- .../miner/tests/check_sector_proven_test.rs | 5 +- actors/miner/tests/compact_partitions_test.rs | 10 +- .../tests/compact_sector_numbers_tests.rs | 6 +- .../tests/confirm_update_worker_key_test.rs | 5 +- actors/miner/tests/deadline_cron.rs | 18 +- .../deadline_cron_defers_stops_restarts.rs | 10 +- actors/miner/tests/declare_faults.rs | 11 +- actors/miner/tests/declare_recoveries.rs | 16 +- ...ard_for_power_clampted_at_atto_fil_test.rs | 8 +- actors/miner/tests/expiration_queue.rs | 54 +++--- .../tests/extend_sector_expiration_test.rs | 7 +- .../tests/miner_actor_test_commitment.rs | 24 ++- .../tests/miner_actor_test_construction.rs | 11 +- .../tests/miner_actor_test_partitions.rs | 6 +- .../tests/miner_actor_test_precommit_batch.rs | 23 ++- actors/miner/tests/miner_actor_test_wpost.rs | 50 +++--- actors/miner/tests/monies_test.rs | 18 +- .../tests/pledge_penalty_for_termination.rs | 36 ++-- actors/miner/tests/policy_test.rs | 5 +- ...eposit_and_initial_pledge_positive_test.rs | 10 +- .../miner/tests/precommitted_sector_stores.rs | 18 +- actors/miner/tests/prove_commit.rs | 29 ++-- actors/miner/tests/repay_debt_test.rs | 6 +- actors/miner/tests/repay_debts.rs | 21 ++- actors/miner/tests/report_consensus_fault.rs | 5 +- actors/miner/tests/sector_assignment.rs | 8 +- actors/miner/tests/terminate_sectors_test.rs | 8 +- actors/miner/tests/util.rs | 127 +++++++------- .../miner/tests/vesting_add_locked_funds.rs | 14 +- .../tests/vesting_add_locked_funds_table.rs | 9 +- actors/miner/tests/vesting_unvested_funds.rs | 54 +++--- actors/miner/tests/withdraw_balance.rs | 26 +-- actors/multisig/Cargo.toml | 3 +- actors/multisig/src/lib.rs | 8 +- actors/multisig/src/state.rs | 12 +- actors/multisig/src/testing.rs | 1 - actors/multisig/src/types.rs | 6 +- actors/multisig/tests/multisig_actor_test.rs | 157 +++++++++--------- actors/paych/Cargo.toml | 2 +- actors/paych/src/lib.rs | 12 +- actors/paych/src/state.rs | 6 +- actors/paych/src/testing.rs | 2 +- actors/paych/src/types.rs | 8 +- actors/paych/tests/paych_actor_test.rs | 35 ++-- actors/power/Cargo.toml | 2 +- actors/power/src/lib.rs | 8 +- actors/power/src/state.rs | 2 - actors/power/src/types.rs | 1 - actors/power/tests/harness/mod.rs | 11 +- actors/power/tests/power_actor_tests.rs | 38 ++--- actors/reward/Cargo.toml | 2 +- actors/reward/src/ext.rs | 3 - actors/reward/src/lib.rs | 11 +- actors/reward/src/logic.rs | 21 ++- actors/reward/src/state.rs | 21 +-- actors/reward/src/testing.rs | 4 +- actors/reward/src/types.rs | 3 - actors/reward/tests/reward_actor_test.rs | 73 ++++---- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 41 ++--- runtime/src/builtin/network.rs | 3 - runtime/src/runtime/fvm.rs | 13 +- runtime/src/util/chaos/types.rs | 4 - runtime/tests/alpha_beta_filter_test.rs | 3 +- state/Cargo.toml | 2 +- state/src/check.rs | 6 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 34 ++-- test_vm/src/util.rs | 6 +- test_vm/tests/batch_onboarding.rs | 5 +- test_vm/tests/commit_post_test.rs | 25 ++- test_vm/tests/extend_sectors_test.rs | 8 +- test_vm/tests/market_miner_withdrawal_test.rs | 47 +++--- test_vm/tests/multisig_test.rs | 14 +- test_vm/tests/power_scenario_tests.rs | 18 +- test_vm/tests/publish_deals_test.rs | 18 +- test_vm/tests/replica_update_test.rs | 45 +++-- test_vm/tests/terminate_test.rs | 14 +- test_vm/tests/test_vm_test.rs | 36 ++-- test_vm/tests/verifreg_remove_datacap_test.rs | 2 +- 125 files changed, 1129 insertions(+), 1185 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65c056cb8..88dd61b73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" +checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a" [[package]] name = "arrayref" @@ -70,9 +70,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" +checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" dependencies = [ "async-channel", "async-executor", @@ -80,7 +80,6 @@ dependencies = [ "async-lock", "blocking", "futures-lite", - "num_cpus", "once_cell", ] @@ -196,9 +195,6 @@ name = "bimap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" -dependencies = [ - "serde", -] [[package]] name = "bitflags" @@ -337,9 +333,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.17" +version = "3.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "68d43934757334b5c0519ff882e1ab9647ac0258b47c24c4f490d78e42697fd5" dependencies = [ "atty", "bitflags", @@ -354,9 +350,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -406,9 +402,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1079fb8528d9f9c888b1e8aa651e6e079ade467323d58f75faf1d30b1808f540" +checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813" dependencies = [ "libc", ] @@ -776,6 +772,7 @@ dependencies = [ "fvm_shared", "indexmap", "integer-encoding", + "lazy_static", "num-derive", "num-traits", "serde", @@ -897,7 +894,7 @@ dependencies = [ "regex", "serde", "serde_repr", - "sha2 0.10.2", + "sha2 0.10.3", "thiserror", "unsigned-varint", ] @@ -979,9 +976,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -994,9 +991,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -1004,15 +1001,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -1021,9 +1018,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -1042,9 +1039,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -1053,21 +1050,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -1084,7 +1081,8 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d09e5aa7de45452676d18fcb70b750acd65faae7a4fe18fe784b4c85f869fb" dependencies = [ "ahash", "anyhow", @@ -1100,7 +1098,8 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1587207c7455ea70a762d4360f502b6728ea6e2dfbec2b66b6d4d943ee29ae" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1112,7 +1111,8 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" dependencies = [ "anyhow", "cid", @@ -1137,7 +1137,8 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce9cfcb4ae444801009182fded790dd56aff9c9a567e54510e2ccf14fb9daf8e" dependencies = [ "anyhow", "cid", @@ -1154,7 +1155,8 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b5c939897aa1bfd63e7cb9c458ba10689371af3278ff20d66c6f8ca152c6c0" dependencies = [ "anyhow", "byteorder", @@ -1167,14 +1169,15 @@ dependencies = [ "multihash", "once_cell", "serde", - "sha2 0.10.2", + "sha2 0.10.3", "thiserror", ] [[package]] name = "fvm_sdk" -version = "2.0.0-alpha.1" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +version = "3.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977dc7824079d057d149ea9b42ac50072928f5f37f941e07860eb2432f62fa1f" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1187,11 +1190,11 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "0.9.0" -source = "git+https://github.com/filecoin-project/ref-fvm?branch=master#433a63f6d017c6c47e3a158a14e57c9aadedb7a4" +version = "3.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261314c3de2e9d6d479977dfba985d38e05cacf59de1dc412e92aab6ccd61d51" dependencies = [ "anyhow", - "bimap", "blake2b_simd", "byteorder", "cid", @@ -1504,7 +1507,7 @@ dependencies = [ "ripemd", "serde", "serde-big-array", - "sha2 0.10.2", + "sha2 0.10.3", "sha3", "unsigned-varint", ] @@ -1610,16 +1613,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.13.1" @@ -1658,10 +1651,11 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg", "cfg-if", "libc", "log", @@ -1835,9 +1829,9 @@ checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] @@ -1862,9 +1856,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -1885,9 +1879,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -1941,9 +1935,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215" dependencies = [ "cfg-if", "cpufeatures", @@ -1971,9 +1965,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa" dependencies = [ "libc", "winapi", @@ -2097,18 +2091,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5d28d8160..f94f26d5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,14 +54,14 @@ members = [ "test_vm", ] -[patch.crates-io] -fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } -fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +#[patch.crates-io] +#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } ## 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. diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index cd4f14c80..00b1c63dd 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 90202ecd7..9a1049244 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/cron/src/lib.rs b/actors/cron/src/lib.rs index 72120f687..891a53d0f 100644 --- a/actors/cron/src/lib.rs +++ b/actors/cron/src/lib.rs @@ -10,7 +10,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; -use num_traits::FromPrimitive; +use num_traits::{FromPrimitive, Zero}; pub use self::state::{Entry, State}; @@ -68,7 +68,7 @@ impl Actor { &entry.receiver, entry.method_num, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), ); if let Err(e) = res { log::error!( diff --git a/actors/cron/tests/cron_actor_test.rs b/actors/cron/tests/cron_actor_test.rs index 6064e66f7..4b6201a8f 100644 --- a/actors/cron/tests/cron_actor_test.rs +++ b/actors/cron/tests/cron_actor_test.rs @@ -7,7 +7,9 @@ use fil_actors_runtime::test_utils::*; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; +use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use num_traits::Zero; fn check_state(rt: &MockRuntime) { let (_, acc) = check_state_invariants(&rt.get_state()); @@ -79,7 +81,7 @@ fn epoch_tick_with_entries() { entry1.receiver, entry1.method_num, RawBytes::default(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -87,7 +89,7 @@ fn epoch_tick_with_entries() { entry2.receiver, entry2.method_num, RawBytes::default(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -95,7 +97,7 @@ fn epoch_tick_with_entries() { entry3.receiver, entry3.method_num, RawBytes::default(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -103,7 +105,7 @@ fn epoch_tick_with_entries() { entry4.receiver, entry4.method_num, RawBytes::default(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index d7f601ba6..174be0f69 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_sdk = { version = "2.0.0-alpha.1" } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_sdk = { version = "3.0.0-alpha.1" } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 607df7a6e..5a4dc7803 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -19,7 +19,7 @@ construct_fixed_hash! { pub struct H256(32); } // Keccak256 impl From<&TokenAmount> for U256 { fn from(amount: &TokenAmount) -> U256 { - let (_, bytes) = amount.to_bytes_be(); + let (_, bytes) = amount.atto().to_bytes_be(); U256::from(bytes.as_slice()) } } diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 3a012c0c8..e991179b7 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 0a4609c65..163ce4290 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -16,6 +16,7 @@ use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; +use num_traits::Zero; use serde::Serialize; fn check_state(rt: &MockRuntime) { @@ -59,8 +60,8 @@ fn create_2_payment_channels() { let pay_channel_string = format!("paych_{}", n); let paych = pay_channel_string.as_bytes(); - rt.set_balance(TokenAmount::from(100)); - rt.value_received = TokenAmount::from(100); + rt.set_balance(TokenAmount::from_atto(100)); + rt.value_received = TokenAmount::from_atto(100); let unique_address = Address::new_actor(paych); rt.new_actor_addr = Some(Address::new_actor(paych)); @@ -72,7 +73,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(100u8); + let balance = TokenAmount::from_atto(100u8); rt.expect_send( expected_id_addr, @@ -120,7 +121,7 @@ fn create_storage_miner() { expected_id_addr, METHOD_CONSTRUCTOR, RawBytes::serialize(&fake_params).unwrap(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -171,7 +172,7 @@ fn create_multisig_actor() { expected_id_addr, METHOD_CONSTRUCTOR, RawBytes::serialize(&fake_params).unwrap(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -206,7 +207,7 @@ fn sending_constructor_failure() { expected_id_addr, METHOD_CONSTRUCTOR, RawBytes::serialize(&fake_params).unwrap(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::USR_ILLEGAL_STATE, ); diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 86a9df263..6eaed859d 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_bitfield = "0.5.2" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/market/src/balance_table.rs b/actors/market/src/balance_table.rs index 9de8cb076..d2fb2246d 100644 --- a/actors/market/src/balance_table.rs +++ b/actors/market/src/balance_table.rs @@ -5,16 +5,15 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::BigIntDe; use fvm_shared::econ::TokenAmount; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use fil_actors_runtime::{make_empty_map, make_map_with_root_and_bitwidth, Map}; pub const BALANCE_TABLE_BITWIDTH: u32 = 6; /// Balance table which handles getting and updating token balances specifically -pub struct BalanceTable<'a, BS>(pub Map<'a, BS, BigIntDe>); +pub struct BalanceTable<'a, BS>(pub Map<'a, BS, TokenAmount>); impl<'a, BS> BalanceTable<'a, BS> where @@ -38,9 +37,9 @@ where /// Gets token amount for given address in balance table pub fn get(&self, key: &Address) -> Result { if let Some(v) = self.0.get(&key.to_bytes())? { - Ok(v.0.clone()) + Ok(v.clone()) } else { - Ok(0.into()) + Ok(TokenAmount::zero()) } } @@ -54,7 +53,7 @@ where self.0.delete(&key.to_bytes())?; Ok(()) } else { - self.0.set(key.to_bytes().into(), BigIntDe(sum))?; + self.0.set(key.to_bytes().into(), sum)?; Ok(()) } } @@ -93,10 +92,10 @@ where /// Returns total balance held by this balance table #[allow(dead_code)] pub fn total(&self) -> Result { - let mut total = TokenAmount::default(); + let mut total = TokenAmount::zero(); - self.0.for_each(|_, v: &BigIntDe| { - total += &v.0; + self.0.for_each(|_, v: &TokenAmount| { + total += v; Ok(()) })?; @@ -119,7 +118,7 @@ mod tests { let store = MemoryBlockstore::default(); let mut bt = BalanceTable::new(&store); - assert_eq!(bt.total().unwrap(), TokenAmount::from(0u8)); + assert!(bt.total().unwrap().is_zero()); struct TotalTestCase<'a> { amount: u64, @@ -134,9 +133,9 @@ mod tests { ]; for t in cases.iter() { - bt.add(t.addr, &TokenAmount::from(t.amount)).unwrap(); + bt.add(t.addr, &TokenAmount::from_atto(t.amount)).unwrap(); - assert_eq!(bt.total().unwrap(), TokenAmount::from(t.total)); + assert_eq!(bt.total().unwrap(), TokenAmount::from_atto(t.total)); } } @@ -146,29 +145,37 @@ mod tests { let store = MemoryBlockstore::default(); let mut bt = BalanceTable::new(&store); - bt.add(&addr, &TokenAmount::from(80u8)).unwrap(); - assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from(80u8)); + bt.add(&addr, &TokenAmount::from_atto(80u8)).unwrap(); + assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from_atto(80u8)); // Test subtracting past minimum only subtracts correct amount assert_eq!( - bt.subtract_with_minimum(&addr, &TokenAmount::from(20u8), &TokenAmount::from(70u8)) - .unwrap(), - TokenAmount::from(10u8) + bt.subtract_with_minimum( + &addr, + &TokenAmount::from_atto(20u8), + &TokenAmount::from_atto(70u8) + ) + .unwrap(), + TokenAmount::from_atto(10u8) ); - assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from(70u8)); + assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from_atto(70u8)); // Test subtracting to limit assert_eq!( - bt.subtract_with_minimum(&addr, &TokenAmount::from(10u8), &TokenAmount::from(60u8)) - .unwrap(), - TokenAmount::from(10u8) + bt.subtract_with_minimum( + &addr, + &TokenAmount::from_atto(10u8), + &TokenAmount::from_atto(60u8) + ) + .unwrap(), + TokenAmount::from_atto(10u8) ); - assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from(60u8)); + assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from_atto(60u8)); // Test must subtract success - bt.must_subtract(&addr, &TokenAmount::from(10u8)).unwrap(); - assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from(50u8)); + bt.must_subtract(&addr, &TokenAmount::from_atto(10u8)).unwrap(); + assert_eq!(bt.get(&addr).unwrap(), TokenAmount::from_atto(50u8)); // Test subtracting more than available - assert!(bt.must_subtract(&addr, &TokenAmount::from(100u8)).is_err()); + assert!(bt.must_subtract(&addr, &TokenAmount::from_atto(100u8)).is_err()); } } diff --git a/actors/market/src/deal.rs b/actors/market/src/deal.rs index 8d9a86a68..661461876 100644 --- a/actors/market/src/deal.rs +++ b/actors/market/src/deal.rs @@ -6,7 +6,6 @@ use fil_actors_runtime::DealWeight; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{BytesSer, Cbor}; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::ChainEpoch; use fvm_shared::commcid::{FIL_COMMITMENT_UNSEALED, SHA2_256_TRUNC254_PADDED}; use fvm_shared::crypto::signature::Signature; @@ -108,12 +107,9 @@ pub struct DealProposal { // otherwise it is invalid. pub start_epoch: ChainEpoch, pub end_epoch: ChainEpoch, - #[serde(with = "bigint_ser")] pub storage_price_per_epoch: TokenAmount, - #[serde(with = "bigint_ser")] pub provider_collateral: TokenAmount, - #[serde(with = "bigint_ser")] pub client_collateral: TokenAmount, } diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index efddd9af1..1b17fffef 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -52,7 +52,6 @@ pub mod power { pub raw_byte_power: StoragePower, #[serde(with = "bigint_ser")] pub quality_adj_power: StoragePower, - #[serde(with = "bigint_ser")] pub pledge_collateral: TokenAmount, pub quality_adj_power_smoothed: FilterEstimate, } diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index aa8b8820a..1053974ab 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -1,7 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use cid::multihash::{Code, MultihashGeneric}; +use cid::multihash::{Code, MultihashDigest, MultihashGeneric}; use cid::Cid; use std::collections::{BTreeMap, BTreeSet}; @@ -20,7 +20,7 @@ use fvm_shared::sector::StoragePower; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use log::info; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, Signed, Zero}; +use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::builtins::Type; @@ -114,7 +114,7 @@ impl Actor { { let msg_value = rt.message().value_received(); - if msg_value <= TokenAmount::from(0) { + if msg_value <= TokenAmount::zero() { return Err(actor_error!( illegal_argument, "balance to add must be greater than zero was: {}", @@ -163,7 +163,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - if params.amount < TokenAmount::from(0) { + if params.amount < TokenAmount::zero() { return Err(actor_error!(illegal_argument, "negative amount: {}", params.amount)); } @@ -351,7 +351,7 @@ impl Actor { deal.proposal.provider = Address::new_id(provider_id); deal.proposal.client = Address::new_id(client_id); - let pcid = deal.proposal.cid().map_err( + let pcid = rt_deal_cid(rt, &deal.proposal).map_err( |e| actor_error!(illegal_argument; "failed to take cid of proposal {}: {}", di, e), )?; @@ -577,7 +577,7 @@ impl Actor { })? .ok_or_else(|| actor_error!(not_found, "no such deal_id: {}", deal_id))?; - let propc = cid(rt, proposal)?; + let propc = rt_deal_cid(rt, proposal)?; let has = msm.pending_deals.as_ref().unwrap().has(&propc.to_bytes()).map_err(|e| { @@ -764,7 +764,7 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&*CRON_ACTOR_ADDR))?; - let mut amount_slashed = BigInt::zero(); + let mut amount_slashed = TokenAmount::zero(); let curr_epoch = rt.curr_epoch(); let mut timed_out_verified_deals: Vec = Vec::new(); @@ -818,7 +818,7 @@ impl Actor { })? .clone(); - let dcid = cid(rt, &deal)?; + let dcid = rt_deal_cid(rt, &deal)?; let state = msm .deal_states @@ -1263,7 +1263,8 @@ where pub const DAG_CBOR: u64 = 0x71; // TODO is there a better place to get this? -fn cid(rt: &RT, proposal: &DealProposal) -> Result +/// Compute a deal CID using the runtime. +pub(crate) fn rt_deal_cid(rt: &RT, proposal: &DealProposal) -> Result where BS: Blockstore, RT: Runtime, @@ -1276,6 +1277,15 @@ where Ok(Cid::new_v1(DAG_CBOR, hash)) } +/// Compute a deal CID directly. +pub(crate) fn deal_cid(proposal: &DealProposal) -> Result { + const DIGEST_SIZE: u32 = 32; + let data = &proposal.marshal_cbor()?; + let hash = Code::Blake2b256.digest(data); + debug_assert_eq!(u32::from(hash.size()), DIGEST_SIZE, "expected 32byte digest"); + Ok(Cid::new_v1(DAG_CBOR, hash)) +} + /// Resolves a provider or client address to the canonical form against which a balance should be held, and /// the designated recipient address of withdrawals (which is the same, for simple account parties). fn escrow_address( @@ -1316,7 +1326,7 @@ where &REWARD_ACTOR_ADDR, ext::reward::THIS_EPOCH_REWARD_METHOD, RawBytes::default(), - 0.into(), + TokenAmount::zero(), )?; let ret: ThisEpochRewardReturn = rwret.deserialize()?; Ok(ret.this_epoch_baseline_power) @@ -1335,7 +1345,7 @@ where &STORAGE_POWER_ACTOR_ADDR, ext::power::CURRENT_TOTAL_POWER_METHOD, RawBytes::default(), - 0.into(), + TokenAmount::zero(), )?; let ret: ext::power::CurrentTotalPowerReturnParams = rwret.deserialize()?; Ok((ret.raw_byte_power, ret.quality_adj_power)) diff --git a/actors/market/src/policy.rs b/actors/market/src/policy.rs index b87b30ef0..3ca161bdc 100644 --- a/actors/market/src/policy.rs +++ b/actors/market/src/policy.rs @@ -6,7 +6,7 @@ use std::cmp::max; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::DealWeight; -use fvm_shared::bigint::Integer; +use fvm_shared::bigint::{BigInt, Integer}; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::piece::PaddedPieceSize; @@ -38,7 +38,7 @@ pub(super) fn deal_price_per_epoch_bounds( _size: PaddedPieceSize, _duration: ChainEpoch, ) -> (TokenAmount, &'static TokenAmount) { - (0.into(), &TOTAL_FILECOIN) + (TokenAmount::zero(), &TOTAL_FILECOIN) } pub fn deal_provider_collateral_bounds( @@ -53,12 +53,12 @@ pub fn deal_provider_collateral_bounds( // dealPowerShare = dealRawPower / max(BaselinePower(t), NetworkRawPower(t), dealRawPower) let lock_target_num = network_circulating_supply * policy.prov_collateral_percent_supply_num; - let power_share_num = TokenAmount::from(size.0); + let power_share_num = BigInt::from(size.0); let power_share_denom = max(max(network_raw_power, baseline_power), &power_share_num).clone(); - let num: TokenAmount = power_share_num * lock_target_num; - let denom: TokenAmount = power_share_denom * policy.prov_collateral_percent_supply_denom; - ((num.div_floor(&denom)), TOTAL_FILECOIN.clone()) + let num: BigInt = power_share_num * lock_target_num.atto(); + let denom: BigInt = power_share_denom * policy.prov_collateral_percent_supply_denom; + (TokenAmount::from_atto(num.div_floor(&denom)), TOTAL_FILECOIN.clone()) } pub(super) fn deal_client_collateral_bounds( diff --git a/actors/market/src/state.rs b/actors/market/src/state.rs index 0c2b2a614..9f0fe5873 100644 --- a/actors/market/src/state.rs +++ b/actors/market/src/state.rs @@ -12,13 +12,12 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::{ChainEpoch, EPOCH_UNDEFINED}; use fvm_shared::deal::DealID; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::HAMT_BIT_WIDTH; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use super::policy::*; use super::types::*; @@ -58,13 +57,10 @@ pub struct State { pub last_cron: ChainEpoch, /// Total Client Collateral that is locked -> unlocked when deal is terminated - #[serde(with = "bigint_ser")] pub total_client_locked_collateral: TokenAmount, /// Total Provider Collateral that is locked -> unlocked when deal is terminated - #[serde(with = "bigint_ser")] pub total_provider_locked_collateral: TokenAmount, /// Total storage fee that is locked in escrow -> unlocked when payments are made - #[serde(with = "bigint_ser")] pub total_client_storage_fee: TokenAmount, } @@ -398,7 +394,7 @@ where let num_epochs_elapsed = payment_end_epoch - payment_start_epoch; let total_payment = &deal.storage_price_per_epoch * num_epochs_elapsed; - if total_payment > 0.into() { + if total_payment.is_positive() { self.transfer_balance(&deal.client, &deal.provider, &total_payment)?; } diff --git a/actors/market/src/testing.rs b/actors/market/src/testing.rs index b808a0473..e154cff2a 100644 --- a/actors/market/src/testing.rs +++ b/actors/market/src/testing.rs @@ -9,16 +9,17 @@ use fil_actors_runtime::{ make_map_with_root_and_bitwidth, parse_uint_key, MessageAccumulator, SetMultimap, }; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::Cbor; use fvm_shared::{ address::{Address, Protocol}, clock::{ChainEpoch, EPOCH_UNDEFINED}, deal::DealID, econ::TokenAmount, }; -use num_traits::{Signed, Zero}; +use num_traits::Zero; -use crate::{balance_table::BalanceTable, DealArray, DealMetaArray, State, PROPOSALS_AMT_BITWIDTH}; +use crate::{ + balance_table::BalanceTable, deal_cid, DealArray, DealMetaArray, State, PROPOSALS_AMT_BITWIDTH, +}; pub struct DealSummary { pub provider: Address, @@ -90,7 +91,7 @@ pub fn check_state_invariants( match DealArray::load(&state.proposals, store) { Ok(proposals) => { let ret = proposals.for_each(|deal_id, proposal| { - let proposal_cid = proposal.cid()?; + let proposal_cid = deal_cid(proposal)?; if proposal.start_epoch >= current_epoch { expected_deal_ops.insert(deal_id); @@ -212,7 +213,6 @@ pub fn check_state_invariants( (Ok(escrow_table), Ok(lock_table)) => { let mut locked_total = TokenAmount::zero(); let ret = lock_table.0.for_each(|key, locked_amount| { - let locked_amount = &locked_amount.0; let address = Address::from_bytes(key)?; locked_total += locked_amount; diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index 4a9839be8..1c85fb711 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -21,7 +21,6 @@ pub const STATES_AMT_BITWIDTH: u32 = 6; #[derive(Serialize_tuple, Deserialize_tuple)] pub struct WithdrawBalanceParams { pub provider_or_client: Address, - #[serde(with = "bigint_ser")] pub amount: TokenAmount, } @@ -30,7 +29,6 @@ impl Cbor for WithdrawBalanceParams {} #[derive(Serialize_tuple, Deserialize_tuple)] #[serde(transparent)] pub struct WithdrawBalanceReturn { - #[serde(with = "bigint_ser")] pub amount_withdrawn: TokenAmount, } diff --git a/actors/market/tests/cron_tick_deal_expiry.rs b/actors/market/tests/cron_tick_deal_expiry.rs index 47add376a..38b7bce7c 100644 --- a/actors/market/tests/cron_tick_deal_expiry.rs +++ b/actors/market/tests/cron_tick_deal_expiry.rs @@ -5,8 +5,6 @@ use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fvm_shared::clock::ChainEpoch; -use num_traits::Zero; - mod harness; use harness::*; diff --git a/actors/market/tests/cron_tick_deal_slashing.rs b/actors/market/tests/cron_tick_deal_slashing.rs index c4b95fb17..83bbaae42 100644 --- a/actors/market/tests/cron_tick_deal_slashing.rs +++ b/actors/market/tests/cron_tick_deal_slashing.rs @@ -35,7 +35,7 @@ fn deal_is_slashed() { deal_end: 10 + 200 * EPOCHS_IN_DAY, activation_epoch: 5, termination_epoch: 15, - payment: TokenAmount::from(50), // (15 - 10) * 10 as deal storage fee is 10 per epoch + payment: TokenAmount::from_atto(50), // (15 - 10) * 10 as deal storage fee is 10 per epoch }, Case { name: "deal is slashed at the startepoch and then the first crontick happens", @@ -43,7 +43,7 @@ fn deal_is_slashed() { deal_end: 10 + 200 * EPOCHS_IN_DAY, activation_epoch: 5, termination_epoch: 10, - payment: TokenAmount::from(0), // (10 - 10) * 10 + payment: TokenAmount::zero(), // (10 - 10) * 10 }, Case { name: "deal is slashed before the startepoch and then the first crontick happens", @@ -51,7 +51,7 @@ fn deal_is_slashed() { deal_end: 10 + 200 * EPOCHS_IN_DAY, activation_epoch: 5, termination_epoch: 6, - payment: TokenAmount::from(0), // (10 - 10) * 10 + payment: TokenAmount::zero(), // (10 - 10) * 10 }, Case { name: "deal is terminated at the activation epoch and then the first crontick happens", @@ -59,7 +59,7 @@ fn deal_is_slashed() { deal_end: 10 + 200 * EPOCHS_IN_DAY, activation_epoch: 5, termination_epoch: 5, - payment: TokenAmount::from(0), // (10 - 10) * 10 + payment: TokenAmount::zero(), // (10 - 10) * 10 }, Case { name: "deal is slashed just BEFORE the end epoch", @@ -67,7 +67,7 @@ fn deal_is_slashed() { deal_end: 10 + 200 * EPOCHS_IN_DAY, activation_epoch: 5, termination_epoch: 19, - payment: TokenAmount::from(90), // (19 - 10) * 10 + payment: TokenAmount::from_atto(90), // (19 - 10) * 10 }, ]; for tc in cases { diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index e65673266..8c815123c 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -83,7 +83,7 @@ pub fn setup() -> MockRuntime { caller: *SYSTEM_ACTOR_ADDR, caller_type: *INIT_ACTOR_CODE_ID, actor_code_cids, - balance: RefCell::new(10u64.pow(19).into()), + balance: RefCell::new(TokenAmount::from_whole(10)), ..Default::default() }; @@ -137,7 +137,7 @@ pub fn expect_get_control_addresses( provider, ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::serialize(result).unwrap(), ExitCode::OK, ) @@ -353,7 +353,7 @@ pub fn cron_tick_and_assert_balances( let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); let p_locked = get_locked_balance(rt, provider_addr); let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); - let mut amount_slashed = TokenAmount::from(0u8); + let mut amount_slashed = TokenAmount::zero(); let s = get_deal_state(rt, deal_id); let d = get_deal_proposal(rt, deal_id); @@ -396,8 +396,8 @@ pub fn cron_tick_and_assert_balances( // if the deal has expired or been slashed, locked amount will be zero for provider and client. let is_deal_expired = payment_end == d.end_epoch; if is_deal_expired || s.slash_epoch != EPOCH_UNDEFINED { - updated_client_locked = TokenAmount::from(0u8); - updated_provider_locked = TokenAmount::from(0u8); + updated_client_locked = TokenAmount::zero(); + updated_provider_locked = TokenAmount::zero(); } cron_tick(rt); @@ -445,7 +445,7 @@ pub fn publish_deals( addrs.provider, ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(return_value).unwrap(), ExitCode::OK, ); @@ -480,7 +480,7 @@ pub fn publish_deals( *VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::USE_BYTES_METHOD as u64, param, - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -577,15 +577,15 @@ pub fn cron_tick_raw(rt: &mut MockRuntime) -> Result { pub fn expect_query_network_info(rt: &mut MockRuntime) { //networkQAPower //networkBaselinePower - let reward = TokenAmount::from(10u8) * TokenAmount::from(10_i128.pow(18)); + let reward = TokenAmount::from_whole(10); let power = StoragePower::from_i128(1 << 50).unwrap(); - let epoch_reward_smooth = FilterEstimate::new(reward.clone(), BigInt::from(0u8)); + let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8)); let current_power = CurrentTotalPowerReturn { raw_byte_power: StoragePower::default(), quality_adj_power: power.clone(), pledge_collateral: TokenAmount::default(), - quality_adj_power_smoothed: FilterEstimate::new(reward, TokenAmount::default()), + quality_adj_power_smoothed: FilterEstimate::new(reward.atto().clone(), BigInt::zero()), }; let current_reward = ThisEpochRewardReturn { this_epoch_baseline_power: power, @@ -784,9 +784,9 @@ pub fn generate_and_publish_deal_for_piece( piece_size: PaddedPieceSize, ) -> DealID { // generate deal - let storage_per_epoch = BigInt::from(10u8); - let client_collateral = TokenAmount::from(10u8); - let provider_collateral = TokenAmount::from(10u8); + let storage_price_per_epoch = TokenAmount::from_atto(10u8); + let client_collateral = TokenAmount::from_atto(10u8); + let provider_collateral = TokenAmount::from_atto(10u8); let deal = DealProposal { piece_cid, @@ -797,7 +797,7 @@ pub fn generate_and_publish_deal_for_piece( label: Label::String("label".to_string()), start_epoch, end_epoch, - storage_price_per_epoch: storage_per_epoch, + storage_price_per_epoch, provider_collateral, client_collateral, }; @@ -829,8 +829,8 @@ pub fn generate_deal_with_collateral_and_add_funds( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, - provider_collateral: BigInt, - client_collateral: BigInt, + provider_collateral: TokenAmount, + client_collateral: TokenAmount, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { @@ -857,7 +857,7 @@ fn generate_deal_proposal_with_collateral( ) -> DealProposal { let piece_cid = make_piece_cid("1".as_bytes()); let piece_size = PaddedPieceSize(2048u64); - let storage_per_epoch = BigInt::from(10u8); + let storage_price_per_epoch = TokenAmount::from_atto(10u8); DealProposal { piece_cid, piece_size, @@ -867,7 +867,7 @@ fn generate_deal_proposal_with_collateral( label: Label::String("label".to_string()), start_epoch, end_epoch, - storage_price_per_epoch: storage_per_epoch, + storage_price_per_epoch, provider_collateral, client_collateral, } @@ -879,8 +879,8 @@ pub fn generate_deal_proposal( start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { - let client_collateral = TokenAmount::from(10u8); - let provider_collateral = TokenAmount::from(10u8); + let client_collateral = TokenAmount::from_atto(10u8); + let provider_collateral = TokenAmount::from_atto(10u8); generate_deal_proposal_with_collateral( client, provider, diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 59cbf7f47..810b0d7cf 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -20,7 +20,6 @@ use fil_actors_runtime::{ use fvm_ipld_amt::Amt; use fvm_ipld_encoding::{to_vec, RawBytes}; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::BigIntDe; use fvm_shared::bigint::BigInt; use fvm_shared::clock::{ChainEpoch, EPOCH_UNDEFINED}; use fvm_shared::crypto::signature::Signature; @@ -33,7 +32,7 @@ use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR, METHOD_SEND}; use regex::Regex; use std::ops::Add; -use num_traits::FromPrimitive; +use num_traits::{FromPrimitive, Zero}; mod harness; @@ -69,7 +68,7 @@ fn simple_construction() { let store = &rt.store; let empty_balance_table = - make_empty_map::<_, BigIntDe>(store, BALANCE_TABLE_BITWIDTH).flush().unwrap(); + make_empty_map::<_, TokenAmount>(store, BALANCE_TABLE_BITWIDTH).flush().unwrap(); let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH).flush().unwrap(); let empty_proposals_array = Amt::<(), _>::new_with_bit_width(store, PROPOSALS_AMT_BITWIDTH).flush().unwrap(); @@ -192,7 +191,7 @@ fn adds_to_provider_escrow_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); - rt.set_value(TokenAmount::from(tc.delta)); + rt.set_value(TokenAmount::from_atto(tc.delta)); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); @@ -209,7 +208,7 @@ fn adds_to_provider_escrow_funds() { assert_eq!( get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), - TokenAmount::from(tc.total) + TokenAmount::from_atto(tc.total) ); check_state(&rt); } @@ -220,14 +219,16 @@ fn adds_to_provider_escrow_funds() { fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() { let mut rt = setup(); - add_participant_funds(&mut rt, CLIENT_ADDR, TokenAmount::from(20u8)); + add_participant_funds(&mut rt, CLIENT_ADDR, TokenAmount::from_atto(20u8)); - assert_eq!(TokenAmount::from(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); rt.expect_validate_caller_addr(vec![CLIENT_ADDR]); - let params = - WithdrawBalanceParams { provider_or_client: CLIENT_ADDR, amount: TokenAmount::from(1u8) }; + let params = WithdrawBalanceParams { + provider_or_client: CLIENT_ADDR, + amount: TokenAmount::from_atto(1u8), + }; // caller is not the recipient rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(909)); @@ -241,7 +242,7 @@ fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() rt.verify(); // verify there was no withdrawal - assert_eq!(TokenAmount::from(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); check_state(&rt); } @@ -267,8 +268,8 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun assert_eq!(deal.provider_collateral, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); assert_eq!(deal.client_balance_requirement(), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); - let withdraw_amount = TokenAmount::from(1u8); - let withdrawable_amount = TokenAmount::from(0u8); + let withdraw_amount = TokenAmount::from_atto(1u8); + let withdrawable_amount = TokenAmount::zero(); // client cannot withdraw any funds since all it's balance is locked withdraw_client_balance( &mut rt, @@ -287,8 +288,8 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun ); // add some more funds to the provider & ensure withdrawal is limited by the locked funds - let withdraw_amount = TokenAmount::from(30u8); - let withdrawable_amount = TokenAmount::from(25u8); + let withdraw_amount = TokenAmount::from_atto(30u8); + let withdrawable_amount = TokenAmount::from_atto(25u8); add_provider_funds(&mut rt, withdrawable_amount.clone(), &MinerAddresses::default()); withdraw_provider_balance( @@ -336,8 +337,8 @@ fn worker_balance_after_withdrawal_must_account_for_slashed_funds() { assert_eq!(publish_epoch + 1, st.slash_epoch); // provider cannot withdraw any funds since all it's balance is locked - let withdraw_amount = TokenAmount::from(1); - let actual_withdrawn = TokenAmount::from(0); + let withdraw_amount = TokenAmount::from_atto(1); + let actual_withdrawn = TokenAmount::zero(); withdraw_provider_balance( &mut rt, withdraw_amount, @@ -348,9 +349,9 @@ fn worker_balance_after_withdrawal_must_account_for_slashed_funds() { ); // add some more funds to the provider & ensure withdrawal is limited by the locked funds - add_provider_funds(&mut rt, TokenAmount::from(25), &MinerAddresses::default()); - let withdraw_amount = TokenAmount::from(30); - let actual_withdrawn = TokenAmount::from(25); + add_provider_funds(&mut rt, TokenAmount::from_atto(25), &MinerAddresses::default()); + let withdraw_amount = TokenAmount::from_atto(30); + let actual_withdrawn = TokenAmount::from_atto(25); withdraw_provider_balance( &mut rt, @@ -367,7 +368,7 @@ fn worker_balance_after_withdrawal_must_account_for_slashed_funds() { fn fails_unless_called_by_an_account_actor() { let mut rt = setup(); - rt.set_value(TokenAmount::from(10)); + rt.set_value(TokenAmount::from_atto(10)); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); @@ -402,7 +403,7 @@ fn adds_to_non_provider_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); - rt.set_value(TokenAmount::from(tc.delta)); + rt.set_value(TokenAmount::from_atto(tc.delta)); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); assert_eq!( @@ -416,7 +417,10 @@ fn adds_to_non_provider_funds() { rt.verify(); - assert_eq!(get_escrow_balance(&rt, caller_addr).unwrap(), TokenAmount::from(tc.total)); + assert_eq!( + get_escrow_balance(&rt, caller_addr).unwrap(), + TokenAmount::from_atto(tc.total) + ); check_state(&rt); } } @@ -426,13 +430,13 @@ fn adds_to_non_provider_funds() { fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { let mut rt = setup(); - let amount = TokenAmount::from(20); + let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); assert_eq!(amount, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); // worker calls WithdrawBalance, balance is transferred to owner - let withdraw_amount = TokenAmount::from(1); + let withdraw_amount = TokenAmount::from_atto(1); withdraw_provider_balance( &mut rt, withdraw_amount.clone(), @@ -442,7 +446,7 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { WORKER_ADDR, ); - assert_eq!(TokenAmount::from(19), get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(19), get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); check_state(&rt); } @@ -450,15 +454,15 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { fn withdraws_from_non_provider_escrow_funds() { let mut rt = setup(); - let amount = TokenAmount::from(20); + let amount = TokenAmount::from_atto(20); add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), amount); - let withdraw_amount = TokenAmount::from(1); + let withdraw_amount = TokenAmount::from_atto(1); withdraw_client_balance(&mut rt, withdraw_amount.clone(), withdraw_amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::from(19)); + assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::from_atto(19)); check_state(&rt); } @@ -466,14 +470,14 @@ fn withdraws_from_non_provider_escrow_funds() { fn client_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let mut rt = setup(); - let amount = TokenAmount::from(20); + let amount = TokenAmount::from_atto(20); add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); // withdraw amount greater than escrow balance - let withdraw_amount = TokenAmount::from(25); + let withdraw_amount = TokenAmount::from_atto(25); withdraw_client_balance(&mut rt, withdraw_amount, amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::from(0)); + assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::zero()); check_state(&rt); } @@ -481,12 +485,12 @@ fn client_withdrawing_more_than_escrow_balance_limits_to_available_funds() { fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let mut rt = setup(); - let amount = TokenAmount::from(20); + let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); - let withdraw_amount = TokenAmount::from(25); + let withdraw_amount = TokenAmount::from_atto(25); withdraw_provider_balance( &mut rt, withdraw_amount, @@ -496,7 +500,7 @@ fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { WORKER_ADDR, ); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), TokenAmount::from(0)); + assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), TokenAmount::zero()); check_state(&rt); } @@ -505,7 +509,7 @@ fn fail_when_balance_is_zero() { let mut rt = setup(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); - rt.set_received(BigInt::from(0_i32)); + rt.set_received(TokenAmount::zero()); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -525,7 +529,7 @@ fn fails_with_a_negative_withdraw_amount() { let params = WithdrawBalanceParams { provider_or_client: PROVIDER_ADDR, - amount: TokenAmount::from(-1_i32), + amount: TokenAmount::from_atto(-1_i32), }; expect_abort( @@ -544,15 +548,17 @@ fn fails_with_a_negative_withdraw_amount() { fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker() { let mut rt = setup(); - let amount = TokenAmount::from(20u8); + let amount = TokenAmount::from_atto(20u8); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); // only signing parties can add balance for client AND provider. rt.expect_validate_caller_addr(vec![OWNER_ADDR, WORKER_ADDR]); - let params = - WithdrawBalanceParams { provider_or_client: PROVIDER_ADDR, amount: TokenAmount::from(1u8) }; + let params = WithdrawBalanceParams { + provider_or_client: PROVIDER_ADDR, + amount: TokenAmount::from_atto(1u8), + }; // caller is not owner or worker rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(909)); @@ -790,7 +796,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t *VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::USE_BYTES_METHOD as u64, param, - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -859,11 +865,11 @@ fn publish_a_deal_with_enough_collateral_when_circulating_supply_is_superior_to_ let mut rt = setup(); - let client_collateral = TokenAmount::from(10u8); // min is zero so this is placeholder + let client_collateral = TokenAmount::from_atto(10u8); // min is zero so this is placeholder // given power and circ supply cancel this should be 1*dealqapower / 100 let deal_size = PaddedPieceSize(2048u64); // generateDealProposal's deal size - let provider_collateral = TokenAmount::from( + let provider_collateral = TokenAmount::from_atto( (deal_size.0 * (policy.prov_collateral_percent_supply_num as u64)) / policy.prov_collateral_percent_supply_denom as u64, ); @@ -878,7 +884,7 @@ fn publish_a_deal_with_enough_collateral_when_circulating_supply_is_superior_to_ end_epoch, ); let qa_power = StoragePower::from_i128(1 << 50).unwrap(); - rt.set_circulating_supply(qa_power); // convenient for these two numbers to cancel out + rt.set_circulating_supply(TokenAmount::from_atto(qa_power)); // convenient for these two numbers to cancel out // publish the deal successfully rt.set_epoch(publish_epoch); @@ -1188,8 +1194,8 @@ fn crontick_for_a_deal_at_its_start_epoch_results_in_zero_payment_and_no_slashin rt.set_epoch(current); let (pay, slashed) = cron_tick_and_assert_balances(&mut rt, CLIENT_ADDR, PROVIDER_ADDR, current, deal_id); - assert_eq!(TokenAmount::from(0u8), pay); - assert_eq!(TokenAmount::from(0u8), slashed); + assert_eq!(TokenAmount::zero(), pay); + assert_eq!(TokenAmount::zero(), slashed); // deal proposal and state should NOT be deleted get_deal_proposal(&mut rt, deal_id); @@ -1434,9 +1440,9 @@ fn locked_fund_tracking_states() { let st: State = rt.get_state(); // assert values are zero - assert_eq!(st.total_client_locked_collateral, BigInt::from(0_i32)); - assert_eq!(st.total_provider_locked_collateral, BigInt::from(0_i32)); - assert_eq!(st.total_client_storage_fee, BigInt::from(0_i32)); + assert!(st.total_client_locked_collateral.is_zero()); + assert!(st.total_provider_locked_collateral.is_zero()); + assert!(st.total_client_storage_fee.is_zero()); // Publish deal1, deal2, and deal3 with different client and provider let deal_id1 = generate_and_publish_deal(&mut rt, c1, &m1, start_epoch, end_epoch); @@ -1475,7 +1481,7 @@ fn locked_fund_tracking_states() { ); cron_tick(&mut rt); let duration = curr - start_epoch; - let payment = BigInt::from(2_i32) * &d1.storage_price_per_epoch * duration; + let payment: TokenAmount = 2 * &d1.storage_price_per_epoch * duration; let mut csf = (csf - payment) - d3.total_storage_fee(); let mut plc = plc - d3.provider_collateral; let mut clc = clc - d3.client_collateral; @@ -1491,8 +1497,8 @@ fn locked_fund_tracking_states() { // one more round of payment for deal1 and deal2 let curr = curr + 1; rt.set_epoch(curr); - let duration = BigInt::from(deal_updates_interval); - let payment = BigInt::from(2_i32) * d1.storage_price_per_epoch * duration; + let duration = deal_updates_interval; + let payment = 2 * d1.storage_price_per_epoch * duration; csf -= payment; cron_tick(&mut rt); assert_locked_fund_states(&rt, csf.clone(), plc.clone(), clc.clone()); @@ -1503,9 +1509,9 @@ fn locked_fund_tracking_states() { // cron tick to slash deal1 and expire deal2 rt.set_epoch(end_epoch); - csf = BigInt::from(0_i32); - clc = BigInt::from(0_i32); - plc = BigInt::from(0_i32); + csf = TokenAmount::zero(); + clc = TokenAmount::zero(); + plc = TokenAmount::zero(); rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -1543,7 +1549,7 @@ fn market_actor_deals() { }; // test adding provider funds - let funds = TokenAmount::from_u32(20_000_000).unwrap(); + let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); @@ -1581,7 +1587,7 @@ fn max_deal_label_size() { }; // Test adding provider funds from both worker and owner address - let funds = TokenAmount::from_u32(20_000_000).unwrap(); + let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); @@ -1624,7 +1630,7 @@ fn insufficient_client_balance_in_a_batch() { ChainEpoch::from(1), 200 * EPOCHS_IN_DAY, ); - deal1.client_collateral = deal2.clone().client_collateral + 1; + deal1.client_collateral = &deal2.client_collateral + TokenAmount::from_atto(1); // Client gets enough funds for the 2nd deal add_participant_funds(&mut rt, CLIENT_ADDR, deal2.client_balance_requirement()); @@ -1718,7 +1724,7 @@ fn insufficient_provider_balance_in_a_batch() { ChainEpoch::from(1), 200 * EPOCHS_IN_DAY, ); - deal1.provider_collateral = deal2.clone().provider_collateral + 1; + deal1.provider_collateral = &deal2.provider_collateral + TokenAmount::from_atto(1); // Client gets enough funds for both deals add_participant_funds( diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index 9ccfde020..c67f09bd2 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -17,12 +17,11 @@ use fvm_shared::crypto::signature::Signature; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; -use fvm_shared::sector::StoragePower; + use fvm_shared::TOTAL_FILECOIN; use anyhow::anyhow; use cid::Cid; -use num_traits::FromPrimitive; mod harness; use harness::*; @@ -56,7 +55,7 @@ fn deal_duration_greater_than_max_deal_duration() { #[test] fn negative_price_per_epoch() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.storage_price_per_epoch = TokenAmount::from(-1); + d.storage_price_per_epoch = TokenAmount::from_atto(-1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -64,7 +63,7 @@ fn negative_price_per_epoch() { #[test] fn price_per_epoch_greater_than_total_filecoin() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.storage_price_per_epoch = TOTAL_FILECOIN.clone() + 1; + d.storage_price_per_epoch = &*TOTAL_FILECOIN + TokenAmount::from_atto(1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -72,7 +71,7 @@ fn price_per_epoch_greater_than_total_filecoin() { #[test] fn negative_provider_collateral() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.provider_collateral = TokenAmount::from(-1); + d.provider_collateral = TokenAmount::from_atto(-1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -80,7 +79,7 @@ fn negative_provider_collateral() { #[test] fn provider_collateral_greater_than_max_collateral() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.provider_collateral = TOTAL_FILECOIN.clone() + 1; + d.provider_collateral = &*TOTAL_FILECOIN + TokenAmount::from_atto(1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -88,15 +87,15 @@ fn provider_collateral_greater_than_max_collateral() { #[test] fn provider_collateral_less_than_bound() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - let power = StoragePower::from_i128(1 << 50).unwrap(); + let circ_supply = TokenAmount::from_atto(1i64 << 50); let (provider_min, _) = deal_provider_collateral_bounds( &Policy::default(), PaddedPieceSize(2048), &BigInt::from(0u8), &BigInt::from(0u8), - &power, + &circ_supply, ); - d.provider_collateral = provider_min - 1; + d.provider_collateral = provider_min - TokenAmount::from_atto(1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -104,7 +103,7 @@ fn provider_collateral_less_than_bound() { #[test] fn negative_client_collateral() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.client_collateral = TokenAmount::from(-1); + d.client_collateral = TokenAmount::from_atto(-1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -112,7 +111,7 @@ fn negative_client_collateral() { #[test] fn client_collateral_greater_than_max_collateral() { let f = |_rt: &mut MockRuntime, d: &mut DealProposal| { - d.client_collateral = TOTAL_FILECOIN.clone() + 1; + d.client_collateral = &*TOTAL_FILECOIN + TokenAmount::from_atto(1); }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -120,7 +119,11 @@ fn client_collateral_greater_than_max_collateral() { #[test] fn client_does_not_have_enough_balance_for_collateral() { let f = |rt: &mut MockRuntime, d: &mut DealProposal| { - add_participant_funds(rt, CLIENT_ADDR, d.client_balance_requirement() - 1); + add_participant_funds( + rt, + CLIENT_ADDR, + d.client_balance_requirement() - TokenAmount::from_atto(1), + ); add_provider_funds(rt, d.provider_collateral.clone(), &MinerAddresses::default()); }; assert_deal_failure(false, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); @@ -130,7 +133,11 @@ fn client_does_not_have_enough_balance_for_collateral() { fn provider_does_not_have_enough_balance_for_collateral() { let f = |rt: &mut MockRuntime, d: &mut DealProposal| { add_participant_funds(rt, CLIENT_ADDR, d.client_balance_requirement()); - add_provider_funds(rt, d.provider_collateral.clone() - 1, &MinerAddresses::default()); + add_provider_funds( + rt, + d.provider_collateral.clone() - TokenAmount::from_atto(1), + &MinerAddresses::default(), + ); }; assert_deal_failure(false, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } @@ -209,7 +216,7 @@ fn piece_size_is_not_a_power_of_2() { fn fail_when_client_has_some_funds_but_not_enough_for_a_deal() { let mut rt = setup(); - let amount = TokenAmount::from(100u8); + let amount = TokenAmount::from_atto(100u8); add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); let start_epoch = 42; let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; @@ -233,7 +240,7 @@ fn fail_when_provider_has_some_funds_but_not_enough_for_a_deal() { let mut rt = setup(); - let amount = TokenAmount::from(1u8); + let amount = TokenAmount::from_atto(1u8); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); let deal1 = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); assert!(amount < deal1.client_balance_requirement()); diff --git a/actors/market/tests/random_cron_epoch_during_publish.rs b/actors/market/tests/random_cron_epoch_during_publish.rs index 9bcd5c857..831e9beb2 100644 --- a/actors/market/tests/random_cron_epoch_during_publish.rs +++ b/actors/market/tests/random_cron_epoch_during_publish.rs @@ -10,8 +10,6 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; -use num_traits::Zero; - mod harness; use harness::*; diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 1c9be4ae6..bf7790a35 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_hamt = "0.5.1" diff --git a/actors/miner/src/expiration_queue.rs b/actors/miner/src/expiration_queue.rs index 026bb972c..b006f49d2 100644 --- a/actors/miner/src/expiration_queue.rs +++ b/actors/miner/src/expiration_queue.rs @@ -12,7 +12,7 @@ use fvm_ipld_amt::{Error as AmtError, ValueMut}; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; -use fvm_shared::bigint::bigint_ser; + use fvm_shared::clock::{ChainEpoch, QuantSpec}; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{SectorNumber, SectorSize}; @@ -38,7 +38,6 @@ pub struct ExpirationSet { /// Sectors expiring "early" due to being faulty for too long pub early_sectors: BitField, /// Pledge total for the on-time sectors - #[serde(with = "bigint_ser")] pub on_time_pledge: TokenAmount, /// Power that is currently active (not faulty) pub active_power: PowerPair, diff --git a/actors/miner/src/expiration_queue/tests.rs b/actors/miner/src/expiration_queue/tests.rs index 5860eb7a1..796a2d4fe 100644 --- a/actors/miner/src/expiration_queue/tests.rs +++ b/actors/miner/src/expiration_queue/tests.rs @@ -66,7 +66,7 @@ fn test_sector( sector_number, deal_weight: DealWeight::from(deal_weight), verified_deal_weight: DealWeight::from(verified_deal_weight), - initial_pledge: TokenAmount::from(initial_pledge), + initial_pledge: TokenAmount::from_atto(initial_pledge), ..Default::default() } } diff --git a/actors/miner/src/ext.rs b/actors/miner/src/ext.rs index 8804d7146..8b913112d 100644 --- a/actors/miner/src/ext.rs +++ b/actors/miner/src/ext.rs @@ -97,7 +97,6 @@ pub mod power { pub raw_byte_power: StoragePower, #[serde(with = "bigint_ser")] pub quality_adj_power: StoragePower, - #[serde(with = "bigint_ser")] pub pledge_collateral: TokenAmount, pub quality_adj_power_smoothed: FilterEstimate, } diff --git a/actors/miner/src/internal_tests.rs b/actors/miner/src/internal_tests.rs index 2f96e1c2d..5c31b9993 100644 --- a/actors/miner/src/internal_tests.rs +++ b/actors/miner/src/internal_tests.rs @@ -107,8 +107,8 @@ fn test_current_proving_period_start() { #[test] fn br_looks_right_in_plausible_sector_power_network_power_reward_range() { // between 10 and 100 FIL is reasonable for near-mid future - let tens_of_fil: TokenAmount = TokenAmount::from(10).pow(18) * 50; - let reward_estimate = FilterEstimate::new(tens_of_fil.clone(), Zero::zero()); + let tens_of_fil: TokenAmount = TokenAmount::from_whole(50); + let reward_estimate = FilterEstimate::new(tens_of_fil.atto().clone(), Zero::zero()); let small_power = StoragePower::from(32_u64 << 30); // 32 GiB let huge_power = StoragePower::from(1_u64 << 60); // 1 EiB let small_power_br_num = &small_power * EPOCHS_IN_DAY * &tens_of_fil; @@ -131,8 +131,8 @@ fn br_looks_right_in_plausible_sector_power_network_power_reward_range() { &huge_power, EPOCHS_IN_DAY, ); - assert_eq!(&small_power_br_num / &tens_of_eibs, br_small_low); - assert_eq!(&huge_power_br_num / &tens_of_eibs, br_huge_low); + assert_eq!(small_power_br_num.div_floor(tens_of_eibs.clone()), br_small_low); + assert_eq!(huge_power_br_num.div_floor(tens_of_eibs), br_huge_low); // 100s of EiBs // 1.2e18 * 100 bytes * 5 quality ~ 6e20 @@ -150,8 +150,8 @@ fn br_looks_right_in_plausible_sector_power_network_power_reward_range() { &huge_power, EPOCHS_IN_DAY, ); - assert_eq!(&small_power_br_num / &hundreds_of_eibs, br_small_mid); - assert_eq!(&huge_power_br_num / &hundreds_of_eibs, br_huge_mid); + assert_eq!(small_power_br_num.div_floor(hundreds_of_eibs.clone()), br_small_mid); + assert_eq!(huge_power_br_num.div_floor(hundreds_of_eibs), br_huge_mid); // 1000s of EiBs -- upper range // 1.2e18 * 1000 bytes * 10 quality = 1.2e22 ~ 2e22 @@ -169,14 +169,14 @@ fn br_looks_right_in_plausible_sector_power_network_power_reward_range() { &huge_power, EPOCHS_IN_DAY, ); - assert_eq!(&small_power_br_num / &thousands_of_eibs, br_small_upper); - assert_eq!(&huge_power_br_num / &thousands_of_eibs, br_huge_upper); + assert_eq!(small_power_br_num.div_floor(thousands_of_eibs.clone()), br_small_upper); + assert_eq!(huge_power_br_num.div_floor(thousands_of_eibs), br_huge_upper); } #[test] fn declared_and_undeclared_fault_penalties_are_linear_over_sector_qa_power_term() { // Construct plausible reward and qa power filtered estimates - let epoch_reward = TokenAmount::from(100_u64 << 53); + let epoch_reward = BigInt::from(100_u64 << 53); // not too much growth over ~3000 epoch projection in BR let reward_estimate = FilterEstimate::new(epoch_reward, Zero::zero()); @@ -213,7 +213,7 @@ fn declared_and_undeclared_fault_penalties_are_linear_over_sector_qa_power_term( // we can at best expect n calculations of 1 power to be within n of 1 calculation of n powers. let diff = &ff_all - (&ff_c + &ff_a + &ff_b); assert!(diff >= Zero::zero()); - assert!(diff < TokenAmount::from(3)); + assert!(diff < TokenAmount::from_atto(3)); // Undeclared faults let sp_a = pledge_penalty_for_termination_lower_bound( @@ -242,5 +242,5 @@ fn declared_and_undeclared_fault_penalties_are_linear_over_sector_qa_power_term( // we can at best expect n calculations of 1 power to be within n of 1 calculation of n powers. let diff = &sp_all - (&sp_c + &sp_a + &sp_b); assert!(diff >= Zero::zero()); - assert!(diff < TokenAmount::from(3)); + assert!(diff < TokenAmount::from_atto(3)); } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index d89b426ed..bc508da2f 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -25,7 +25,6 @@ use fvm_ipld_bitfield::{BitField, UnvalidatedBitField, Validate}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{from_slice, BytesDe, Cbor, CborStore, RawBytes}; use fvm_shared::address::{Address, Payload, Protocol}; -use fvm_shared::bigint::bigint_ser::BigIntSer; use fvm_shared::bigint::{BigInt, Integer}; use fvm_shared::clock::ChainEpoch; use fvm_shared::deal::DealID; @@ -44,7 +43,7 @@ use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use log::{error, info, warn}; pub use monies::*; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, Signed, Zero}; +use num_traits::{FromPrimitive, Zero}; pub use partition_state::*; pub use policy::*; pub use sector_map::*; @@ -1646,7 +1645,7 @@ impl Actor { params.sectors.len() )); } - let mut fee_to_burn = TokenAmount::from(0_u32); + let mut fee_to_burn = TokenAmount::zero(); let mut needs_cron = false; rt.transaction(|state: &mut State, rt| { // Aggregate fee applies only when batching. @@ -1689,7 +1688,7 @@ impl Actor { } let mut chain_infos = Vec::with_capacity(params.sectors.len()); - let mut total_deposit_required = BigInt::zero(); + let mut total_deposit_required = TokenAmount::zero(); let mut clean_up_events = Vec::with_capacity(params.sectors.len()); let deal_count_max = sector_deals_max(rt.policy(), info.sector_size); @@ -1867,7 +1866,7 @@ impl Actor { &STORAGE_POWER_ACTOR_ADDR, ext::power::SUBMIT_POREP_FOR_BULK_VERIFY_METHOD, RawBytes::serialize(&svi)?, - BigInt::zero(), + TokenAmount::zero(), )?; Ok(()) @@ -3006,11 +3005,12 @@ impl Actor { // The policy amounts we should burn and send to reporter // These may differ from actual funds send when miner goes into fee debt - let this_epoch_reward = reward_stats.this_epoch_reward_smoothed.estimate(); + let this_epoch_reward = + TokenAmount::from_atto(reward_stats.this_epoch_reward_smoothed.estimate()); let fault_penalty = consensus_fault_penalty(this_epoch_reward.clone()); let slasher_reward = reward_for_consensus_slash_report(&this_epoch_reward); - let mut pledge_delta = TokenAmount::from(0); + let mut pledge_delta = TokenAmount::zero(); let (burn_amount, reward_amount) = rt.transaction(|st: &mut State, rt| { let mut info = get_miner_info(rt.store(), st)?; @@ -3767,7 +3767,7 @@ where } let ret: ext::market::ComputeDataCommitmentReturn = rt .send( - *STORAGE_MARKET_ACTOR_ADDR, + &*STORAGE_MARKET_ACTOR_ADDR, ext::market::COMPUTE_DATA_COMMITMENT_METHOD, RawBytes::serialize(ext::market::ComputeDataCommitmentParamsRef { inputs: data_commitment_inputs, @@ -3949,7 +3949,7 @@ where Ok(()) } -fn notify_pledge_changed(rt: &mut RT, pledge_delta: &BigInt) -> Result<(), ActorError> +fn notify_pledge_changed(rt: &mut RT, pledge_delta: &TokenAmount) -> Result<(), ActorError> where BS: Blockstore, RT: Runtime, @@ -3958,7 +3958,7 @@ where rt.send( &STORAGE_POWER_ACTOR_ADDR, ext::power::UPDATE_PLEDGE_TOTAL_METHOD, - RawBytes::serialize(BigIntSer(pledge_delta))?, + RawBytes::serialize(pledge_delta)?, TokenAmount::zero(), )?; } diff --git a/actors/miner/src/monies.rs b/actors/miner/src/monies.rs index 895783a0a..04f2105ca 100644 --- a/actors/miner/src/monies.rs +++ b/actors/miner/src/monies.rs @@ -4,15 +4,13 @@ use std::cmp::{self, max}; use fil_actors_runtime::network::EPOCHS_IN_DAY; -use fil_actors_runtime::{EXPECTED_LEADERS_PER_EPOCH, ONE_NANO_FIL}; -use fvm_shared::bigint::num_integer::div_floor; +use fil_actors_runtime::EXPECTED_LEADERS_PER_EPOCH; use fvm_shared::bigint::{BigInt, Integer}; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::math::PRECISION; use fvm_shared::sector::StoragePower; use fvm_shared::smooth::{self, FilterEstimate}; -use fvm_shared::FILECOIN_PRECISION; use lazy_static::lazy_static; use num_traits::Zero; @@ -32,30 +30,28 @@ pub const PRE_COMMIT_DEPOSIT_PROJECTION_PERIOD: i64 = pub const INITIAL_PLEDGE_PROJECTION_PERIOD: i64 = (INITIAL_PLEDGE_FACTOR as ChainEpoch) * EPOCHS_IN_DAY; -lazy_static! { - static ref LOCK_TARGET_FACTOR_NUM: BigInt = BigInt::from(3); - static ref LOCK_TARGET_FACTOR_DENOM: BigInt = BigInt::from(10); +const LOCK_TARGET_FACTOR_NUM: u32 = 3; +const LOCK_TARGET_FACTOR_DENOM: u32 = 10; - pub static ref TERMINATION_REWARD_FACTOR_NUM: BigInt = BigInt::from(1); - pub static ref TERMINATION_REWARD_FACTOR_DENOM: BigInt = BigInt::from(2); +pub const TERMINATION_REWARD_FACTOR_NUM: u32 = 1; +pub const TERMINATION_REWARD_FACTOR_DENOM: u32 = 2; - // * go impl has 75/100 but this is just simplified - static ref LOCKED_REWARD_FACTOR_NUM: BigInt = BigInt::from(3); - static ref LOCKED_REWARD_FACTOR_DENOM: BigInt = BigInt::from(4); +// * go impl has 75/100 but this is just simplified +const LOCKED_REWARD_FACTOR_NUM: u32 = 3; +const LOCKED_REWARD_FACTOR_DENOM: u32 = 4; +lazy_static! { /// Cap on initial pledge requirement for sectors during the Space Race network. /// The target is 1 FIL (10**18 attoFIL) per 32GiB. /// This does not divide evenly, so the result is fractionally smaller. - static ref INITIAL_PLEDGE_MAX_PER_BYTE: BigInt = - BigInt::from(10_u64.pow(18) / (32 << 30)); + static ref INITIAL_PLEDGE_MAX_PER_BYTE: TokenAmount = + TokenAmount::from_whole(1).div_floor(32i64 << 30); /// Base reward for successfully disputing a window posts proofs. - pub static ref BASE_REWARD_FOR_DISPUTED_WINDOW_POST: BigInt = - BigInt::from(4 * FILECOIN_PRECISION); + pub static ref BASE_REWARD_FOR_DISPUTED_WINDOW_POST: TokenAmount = TokenAmount::from_whole(4); /// Base penalty for a successful disputed window post proof. - pub static ref BASE_PENALTY_FOR_DISPUTED_WINDOW_POST: BigInt = - BigInt::from(FILECOIN_PRECISION) * 20; + pub static ref BASE_PENALTY_FOR_DISPUTED_WINDOW_POST: TokenAmount = TokenAmount::from_whole(20); } // FF + 2BR const INVALID_WINDOW_POST_PROJECTION_PERIOD: ChainEpoch = @@ -93,7 +89,7 @@ pub fn expected_reward_for_power( let network_qa_power_smoothed = network_qa_power_estimate.estimate(); if network_qa_power_smoothed.is_zero() { - return reward_estimate.estimate(); + return TokenAmount::from_atto(reward_estimate.estimate()); } let expected_reward_for_proving_period = smooth::extrapolated_cum_sum_of_ratio( @@ -103,14 +99,14 @@ pub fn expected_reward_for_power( network_qa_power_estimate, ); let br128 = qa_sector_power * expected_reward_for_proving_period; // Q.0 * Q.128 => Q.128 - std::cmp::max(br128 >> PRECISION, Default::default()) + TokenAmount::from_atto(std::cmp::max(br128 >> PRECISION, Default::default())) } pub mod detail { use super::*; lazy_static! { - pub static ref BATCH_BALANCER: BigInt = BigInt::from(ONE_NANO_FIL) * BigInt::from(5); + pub static ref BATCH_BALANCER: TokenAmount = TokenAmount::from_nano(5); } // BR but zero values are clamped at 1 attofil @@ -128,8 +124,8 @@ pub mod detail { qa_sector_power, projection_duration, ); - if br.le(&TokenAmount::from(0)) { - 1.into() + if br.le(&TokenAmount::zero()) { + TokenAmount::from_atto(1) } else { br } @@ -200,8 +196,8 @@ pub fn pledge_penalty_for_termination( expected_reward += replaced_day_reward * relevant_replaced_age; - let penalized_reward = expected_reward * &*TERMINATION_REWARD_FACTOR_NUM; - let penalized_reward = penalized_reward / &*TERMINATION_REWARD_FACTOR_DENOM; + let penalized_reward = expected_reward * TERMINATION_REWARD_FACTOR_NUM; + let penalized_reward = penalized_reward.div_floor(TERMINATION_REWARD_FACTOR_DENOM); cmp::max( pledge_penalty_for_termination_lower_bound( @@ -209,7 +205,7 @@ pub fn pledge_penalty_for_termination( network_qa_power_estimate, qa_sector_power, ), - twenty_day_reward_at_activation + (penalized_reward / EPOCHS_IN_DAY), + twenty_day_reward_at_activation + (penalized_reward.div_floor(EPOCHS_IN_DAY)), ) } @@ -267,37 +263,37 @@ pub fn initial_pledge_for_power( INITIAL_PLEDGE_PROJECTION_PERIOD, ); - let lock_target_num = &*LOCK_TARGET_FACTOR_NUM * circulating_supply; - let lock_target_denom = &*LOCK_TARGET_FACTOR_DENOM; + let lock_target_num = circulating_supply.atto() * LOCK_TARGET_FACTOR_NUM; + let lock_target_denom = LOCK_TARGET_FACTOR_DENOM; let pledge_share_num = qa_power; let network_qa_power = network_qa_power_estimate.estimate(); let pledge_share_denom = cmp::max(cmp::max(&network_qa_power, baseline_power), qa_power); - let additional_ip_num: TokenAmount = lock_target_num * pledge_share_num; - let additional_ip_denom = lock_target_denom * pledge_share_denom; + let additional_ip_num = lock_target_num * pledge_share_num; + let additional_ip_denom = pledge_share_denom * lock_target_denom; let additional_ip = additional_ip_num.div_floor(&additional_ip_denom); - let nominal_pledge = ip_base + additional_ip; - let pledge_cap = &*INITIAL_PLEDGE_MAX_PER_BYTE * qa_power; + let nominal_pledge = ip_base + TokenAmount::from_atto(additional_ip); + let pledge_cap = TokenAmount::from_atto(INITIAL_PLEDGE_MAX_PER_BYTE.atto() * qa_power); cmp::min(nominal_pledge, pledge_cap) } pub fn consensus_fault_penalty(this_epoch_reward: TokenAmount) -> TokenAmount { - (this_epoch_reward * CONSENSUS_FAULT_FACTOR) - .div_floor(&TokenAmount::from(EXPECTED_LEADERS_PER_EPOCH)) + (this_epoch_reward * CONSENSUS_FAULT_FACTOR).div_floor(EXPECTED_LEADERS_PER_EPOCH) } /// Returns the amount of a reward to vest, and the vesting schedule, for a reward amount. pub fn locked_reward_from_reward(reward: TokenAmount) -> (TokenAmount, &'static VestSpec) { - let lock_amount = (reward * &*LOCKED_REWARD_FACTOR_NUM).div_floor(&*LOCKED_REWARD_FACTOR_DENOM); + let lock_amount = (reward * LOCKED_REWARD_FACTOR_NUM).div_floor(LOCKED_REWARD_FACTOR_DENOM); (lock_amount, &REWARD_VESTING_SPEC) } +const BATCH_DISCOUNT_NUM: u32 = 1; +const BATCH_DISCOUNT_DENOM: u32 = 20; + lazy_static! { static ref ESTIMATED_SINGLE_PROVE_COMMIT_GAS_USAGE: BigInt = BigInt::from(49299973); static ref ESTIMATED_SINGLE_PRE_COMMIT_GAS_USAGE: BigInt = BigInt::from(16433324); - static ref BATCH_DISCOUNT_NUM: BigInt = BigInt::from(1); - static ref BATCH_DISCOUNT_DENOM: BigInt = BigInt::from(20); } pub fn aggregate_prove_commit_network_fee( @@ -320,6 +316,6 @@ pub fn aggregate_network_fee( base_fee: &TokenAmount, ) -> TokenAmount { let effective_gas_fee = max(base_fee, &*BATCH_BALANCER); - let network_fee_num = effective_gas_fee * gas_usage * aggregate_size * &*BATCH_DISCOUNT_NUM; - div_floor(network_fee_num, BATCH_DISCOUNT_DENOM.clone()) + let network_fee_num = effective_gas_fee * gas_usage * aggregate_size * BATCH_DISCOUNT_NUM; + network_fee_num.div_floor(BATCH_DISCOUNT_DENOM) } diff --git a/actors/miner/src/policy.rs b/actors/miner/src/policy.rs index a0ffbc679..440fc9fc2 100644 --- a/actors/miner/src/policy.rs +++ b/actors/miner/src/policy.rs @@ -157,13 +157,10 @@ pub const REWARD_VESTING_SPEC: VestSpec = VestSpec { // Default share of block reward allocated as reward to the consensus fault reporter. // Applied as epochReward / (expectedLeadersPerEpoch * consensusFaultReporterDefaultShare) -pub const CONSENSUS_FAULT_REPORTER_DEFAULT_SHARE: i64 = 4; +pub const CONSENSUS_FAULT_REPORTER_DEFAULT_SHARE: u64 = 4; pub fn reward_for_consensus_slash_report(epoch_reward: &TokenAmount) -> TokenAmount { - epoch_reward.div_floor( - &(BigInt::from(EXPECTED_LEADERS_PER_EPOCH) - * BigInt::from(CONSENSUS_FAULT_REPORTER_DEFAULT_SHARE)), - ) + epoch_reward.div_floor(EXPECTED_LEADERS_PER_EPOCH * CONSENSUS_FAULT_REPORTER_DEFAULT_SHARE) } // The reward given for successfully disputing a window post. diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index ce91e4110..883ebbe9b 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -19,13 +19,13 @@ use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, CborStore}; use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; + use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, SectorSize, MAX_SECTOR_NUMBER}; use fvm_shared::HAMT_BIT_WIDTH; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use super::deadlines::new_deadline_info; use super::policy::*; @@ -52,22 +52,18 @@ pub struct State { pub info: Cid, /// Total funds locked as pre_commit_deposit - #[serde(with = "bigint_ser")] pub pre_commit_deposits: TokenAmount, /// Total rewards and added funds locked in vesting table - #[serde(with = "bigint_ser")] pub locked_funds: TokenAmount, /// VestingFunds (Vesting Funds schedule for the miner). pub vesting_funds: Cid, /// Absolute value of debt this miner owes from unpaid fees. - #[serde(with = "bigint_ser")] pub fee_debt: TokenAmount, /// Sum of initial pledge requirements of all active sectors. - #[serde(with = "bigint_ser")] pub initial_pledge: TokenAmount, /// Sectors that have been pre-committed but not yet proven. diff --git a/actors/miner/src/testing.rs b/actors/miner/src/testing.rs index b7c81c6dc..68dd2938a 100644 --- a/actors/miner/src/testing.rs +++ b/actors/miner/src/testing.rs @@ -8,12 +8,11 @@ use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::CborStore; use fvm_shared::address::Protocol; -use fvm_shared::bigint::BigInt; use fvm_shared::clock::{ChainEpoch, QuantSpec, NO_QUANTIZATION}; use fvm_shared::deal::DealID; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, SectorSize}; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; pub fn check_state_invariants( @@ -229,7 +228,7 @@ fn check_miner_balances( policy: &Policy, state: &State, store: &BS, - balance: &BigInt, + balance: &TokenAmount, acc: &MessageAccumulator, ) { acc.require( @@ -253,10 +252,10 @@ fn check_miner_balances( format!("miner fee debt is less than zero: {}", state.fee_debt), ); - acc.require(balance - &state.locked_funds - &state.pre_commit_deposits - &state.initial_pledge >= BigInt::zero(), format!("miner balance {balance} is less than sum of locked funds ({}), precommit deposit ({}) and initial pledge ({})", state.locked_funds, state.pre_commit_deposits, state.initial_pledge)); + acc.require(!(balance - &state.locked_funds - &state.pre_commit_deposits - &state.initial_pledge).is_negative(), format!("miner balance {balance} is less than sum of locked funds ({}), precommit deposit ({}) and initial pledge ({})", state.locked_funds, state.pre_commit_deposits, state.initial_pledge)); // locked funds must be sum of vesting table and vesting table payments must be quantized - let mut vesting_sum = BigInt::zero(); + let mut vesting_sum = TokenAmount::zero(); match state.load_vesting_funds(store) { Ok(funds) => { let quant = state.quant_spec_every_deadline(policy); @@ -329,7 +328,7 @@ fn check_precommits( } }; - let mut precommit_total = BigInt::zero(); + let mut precommit_total = TokenAmount::zero(); let precommited_sectors = Map::<_, SectorPreCommitOnChainInfo>::load(&state.pre_committed_sectors, store); @@ -609,7 +608,7 @@ impl ExpirationQueueStateSummary { let mut expiration_epochs: Vec = Vec::new(); let mut all_active_power = PowerPair::zero(); let mut all_faulty_power = PowerPair::zero(); - let mut all_on_time_pledge = BigInt::zero(); + let mut all_on_time_pledge = TokenAmount::zero(); let ret = expiration_queue.amt.for_each(|epoch, expiration_set| { let epoch = epoch as i64; @@ -619,7 +618,7 @@ impl ExpirationQueueStateSummary { expiration_epochs.push(epoch); - let mut on_time_sectors_pledge = BigInt::zero(); + let mut on_time_sectors_pledge = TokenAmount::zero(); for sector_number in expiration_set.on_time_sectors.iter() { // check sectors are present only once if !seen_sectors.insert(sector_number) { diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 8e6758918..4934320da 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -225,7 +225,6 @@ pub struct ReportConsensusFaultParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct WithdrawBalanceParams { - #[serde(with = "bigint_ser")] pub amount_requested: TokenAmount, } @@ -234,7 +233,6 @@ impl Cbor for WithdrawBalanceParams {} #[derive(Serialize_tuple, Deserialize_tuple)] #[serde(transparent)] pub struct WithdrawBalanceReturn { - #[serde(with = "bigint_ser")] pub amount_withdrawn: TokenAmount, } @@ -277,7 +275,6 @@ pub struct SectorPreCommitInfo { #[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct SectorPreCommitOnChainInfo { pub info: SectorPreCommitInfo, - #[serde(with = "bigint_ser")] pub pre_commit_deposit: TokenAmount, pub pre_commit_epoch: ChainEpoch, /// Integral of active deals over sector lifetime, 0 if CommittedCapacity sector @@ -308,18 +305,14 @@ pub struct SectorOnChainInfo { #[serde(with = "bigint_ser")] pub verified_deal_weight: DealWeight, /// Pledge collected to commit this sector - #[serde(with = "bigint_ser")] pub initial_pledge: TokenAmount, /// Expected one day projection of reward for sector computed at activation time - #[serde(with = "bigint_ser")] pub expected_day_reward: TokenAmount, /// Expected twenty day projection of reward for sector computed at activation time - #[serde(with = "bigint_ser")] pub expected_storage_pledge: TokenAmount, /// Age of sector this sector replaced or zero pub replaced_sector_age: ChainEpoch, /// Day reward of sector this sector replace or zero - #[serde(with = "bigint_ser")] pub replaced_day_reward: TokenAmount, /// The original SealedSectorCID, only gets set on the first ReplicaUpdate pub sector_key_cid: Option, @@ -334,9 +327,7 @@ pub struct Fault { // * Added in v2 -- param was previously a big int. #[derive(Debug, Serialize_tuple, Deserialize_tuple)] pub struct ApplyRewardParams { - #[serde(with = "bigint_ser")] pub reward: TokenAmount, - #[serde(with = "bigint_ser")] pub penalty: TokenAmount, } diff --git a/actors/miner/src/vesting_state.rs b/actors/miner/src/vesting_state.rs index de84a9005..861e99770 100644 --- a/actors/miner/src/vesting_state.rs +++ b/actors/miner/src/vesting_state.rs @@ -4,7 +4,6 @@ use std::{iter, mem}; use fvm_ipld_encoding::tuple::*; -use fvm_shared::bigint::{bigint_ser, Integer}; use fvm_shared::clock::{ChainEpoch, QuantSpec}; use fvm_shared::econ::TokenAmount; use itertools::{EitherOrBoth, Itertools}; @@ -16,7 +15,6 @@ use super::VestSpec; #[derive(Debug, Serialize_tuple, Deserialize_tuple)] pub struct VestingFund { pub epoch: ChainEpoch, - #[serde(with = "bigint_ser")] pub amount: TokenAmount, } @@ -41,7 +39,7 @@ impl VestingFunds { .position(|fund| fund.epoch >= current_epoch) .unwrap_or(self.funds.len()); - self.funds.drain(..i).map(|fund| fund.amount).sum() + self.funds.drain(..i).map(|f| f.amount).sum() } pub fn add_locked_funds( @@ -53,7 +51,6 @@ impl VestingFunds { ) { // Quantization is aligned with when regular cron will be invoked, in the last epoch of deadlines. let vest_begin = current_epoch + spec.initial_delay; // Nothing unlocks here, this is just the start of the clock. - let vest_period = TokenAmount::from(spec.vest_period); let mut vested_so_far = TokenAmount::zero(); let mut epoch = vest_begin; @@ -73,7 +70,7 @@ impl VestingFunds { let elapsed = vest_epoch - vest_begin; let target_vest = if elapsed < spec.vest_period { // Linear vesting - (vesting_sum * elapsed).div_floor(&vest_period) + (vesting_sum * elapsed).div_floor(spec.vest_period) } else { vesting_sum.clone() }; @@ -107,7 +104,7 @@ impl VestingFunds { current_epoch: ChainEpoch, target: &TokenAmount, ) -> TokenAmount { - let mut amount_unlocked = TokenAmount::from(0); + let mut amount_unlocked = TokenAmount::zero(); let mut last = None; let mut start = 0; for (i, vf) in self.funds.iter_mut().enumerate() { diff --git a/actors/miner/tests/aggregate_network_fee_test.rs b/actors/miner/tests/aggregate_network_fee_test.rs index 9fb5fa2fc..c78cc737c 100644 --- a/actors/miner/tests/aggregate_network_fee_test.rs +++ b/actors/miner/tests/aggregate_network_fee_test.rs @@ -1,9 +1,6 @@ -use std::ops::{Add, Mul, Sub}; - use fil_actor_miner::detail::BATCH_BALANCER; use fil_actor_miner::{aggregate_pre_commit_network_fee, aggregate_prove_commit_network_fee}; -use fil_actors_runtime::ONE_NANO_FIL; -use fvm_shared::bigint::BigInt; +use fvm_shared::econ::TokenAmount; use num_traits::zero; #[test] @@ -13,10 +10,10 @@ fn constant_fee_per_sector_when_base_fee_is_below_5_nfil() { let ten_sector_fee = fee_func(10, &zero()); assert_eq!(&one_sector_fee * 10, ten_sector_fee); - let forty_sector_fee = fee_func(40, &ONE_NANO_FIL.into()); + let forty_sector_fee = fee_func(40, &TokenAmount::from_nano(1)); assert_eq!(&one_sector_fee * 40, forty_sector_fee); - let two_hundred_sector_fee = fee_func(200, &BigInt::from(ONE_NANO_FIL * 3)); + let two_hundred_sector_fee = fee_func(200, &TokenAmount::from_nano(3)); assert_eq!(one_sector_fee * 200, two_hundred_sector_fee); } } @@ -25,13 +22,14 @@ fn constant_fee_per_sector_when_base_fee_is_below_5_nfil() { fn fee_increases_if_basefee_crosses_threshold() { for fee_func in [aggregate_prove_commit_network_fee, aggregate_pre_commit_network_fee] { let at_no_base_fee = fee_func(10, &zero()); - let at_balance_minus_one_base_fee = fee_func(10, &BATCH_BALANCER.to_owned().sub(1)); - let at_balance_base_fee = fee_func(10, &BATCH_BALANCER); + let at_balance_minus_one_base_fee = + fee_func(10, &(&*BATCH_BALANCER - TokenAmount::from_atto(1))); + let at_balance_base_fee = fee_func(10, &*BATCH_BALANCER); let at_balance_plus_one_base_fee = - fee_func(10, &BATCH_BALANCER.to_owned().add(ONE_NANO_FIL)); + fee_func(10, &(&*BATCH_BALANCER + TokenAmount::from_nano(1))); let at_balance_plus_two_base_fee = - fee_func(10, &BATCH_BALANCER.to_owned().add(ONE_NANO_FIL * 2)); - let at_balance_times_two_base = fee_func(10, &BATCH_BALANCER.to_owned().mul(2)); + fee_func(10, &(&*BATCH_BALANCER + TokenAmount::from_nano(2))); + let at_balance_times_two_base = fee_func(10, &(2 * &*BATCH_BALANCER)); assert_eq!(at_no_base_fee, at_balance_minus_one_base_fee); assert_eq!(at_no_base_fee, at_balance_base_fee); @@ -47,40 +45,44 @@ fn regression_tests() { let fee = |aggregate_size, base_fee_multiplier| { aggregate_prove_commit_network_fee( aggregate_size, - &BigInt::from(ONE_NANO_FIL * base_fee_multiplier), + &TokenAmount::from_nano(base_fee_multiplier), ) + aggregate_pre_commit_network_fee( aggregate_size, - &BigInt::from(ONE_NANO_FIL * base_fee_multiplier), + &TokenAmount::from_nano(base_fee_multiplier), ) }; // (5/20) * x * 10 = (5/2) * x - let expected = (ONE_NANO_FIL * 5 * magic_number) / 2; - assert_eq!(BigInt::from(expected), fee(10, 0)); - assert_eq!(BigInt::from(expected), fee(10, 1)); + let expected = (TokenAmount::from_nano(5) * magic_number).div_floor(2); + assert_eq!(expected, fee(10, 0)); + assert_eq!(expected, fee(10, 1)); - let expected = ONE_NANO_FIL * 25 * magic_number; - assert_eq!(BigInt::from(expected), fee(100, 3)); + let expected = TokenAmount::from_nano(25) * magic_number; + assert_eq!(expected, fee(100, 3)); - let expected = ONE_NANO_FIL * 30 * magic_number; - assert_eq!(BigInt::from(expected), fee(100, 6)); + let expected = TokenAmount::from_nano(30) * magic_number; + assert_eq!(expected, fee(100, 6)); } #[test] fn split_25_75() { // check 25/75% split up to uFIL precision - let one_micro_fil = BigInt::from(ONE_NANO_FIL) * 1000; + let one_micro_fil = TokenAmount::from_nano(1000); for base_fee_multiplier in [0, 5, 20] { for aggregate_size in [13, 303] { let fee_pre = aggregate_pre_commit_network_fee( aggregate_size, - &BigInt::from(ONE_NANO_FIL * base_fee_multiplier), - ) / &one_micro_fil; + &TokenAmount::from_nano(base_fee_multiplier), + ) + .atto() + / one_micro_fil.atto(); let fee_prove = aggregate_prove_commit_network_fee( aggregate_size, - &BigInt::from(ONE_NANO_FIL * base_fee_multiplier), - ) / &one_micro_fil; + &TokenAmount::from_nano(base_fee_multiplier), + ) + .atto() + / one_micro_fil.atto(); assert_eq!(fee_prove, 3 * fee_pre); } } diff --git a/actors/miner/tests/aggregate_prove_commit.rs b/actors/miner/tests/aggregate_prove_commit.rs index e2b855976..4d20a371d 100644 --- a/actors/miner/tests/aggregate_prove_commit.rs +++ b/actors/miner/tests/aggregate_prove_commit.rs @@ -6,7 +6,7 @@ use fil_actor_miner::{ }; use fil_actors_runtime::runtime::Runtime; use fvm_ipld_bitfield::BitField; -use fvm_shared::{bigint::BigInt, clock::ChainEpoch, sector::SectorSize}; +use fvm_shared::{bigint::BigInt, clock::ChainEpoch, econ::TokenAmount, sector::SectorSize}; mod util; use num_traits::Zero; @@ -22,8 +22,7 @@ fn valid_precommits_then_aggregate_provecommit() { let actor = ActorHarness::new(period_offset); let mut rt = actor.new_runtime(); - let balance = BigInt::from(1_000_000u64) * BigInt::from(10u64.pow(18)); - rt.add_balance(balance); + rt.add_balance(BIG_BALANCE.clone()); let precommit_epoch = period_offset + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -57,7 +56,7 @@ fn valid_precommits_then_aggregate_provecommit() { // run prove commit logic rt.set_epoch(prove_commit_epoch); - rt.set_balance(BigInt::from(1000u64) * BigInt::from(10u64.pow(18))); + rt.set_balance(TokenAmount::from_whole(1000)); actor .prove_commit_aggregate_sector( @@ -65,7 +64,7 @@ fn valid_precommits_then_aggregate_provecommit() { ProveCommitConfig::empty(), precommits, make_prove_commit_aggregate(§or_nos_bf), - BigInt::zero(), + &TokenAmount::zero(), ) .unwrap(); @@ -77,7 +76,7 @@ fn valid_precommits_then_aggregate_provecommit() { } // expect deposit to have been transferred to initial pledges - assert_eq!(BigInt::zero(), st.pre_commit_deposits); + assert!(st.pre_commit_deposits.is_zero()); // The sector is exactly full with verified deals, so expect fully verified power. let expected_power = BigInt::from(actor.sector_size as i64) diff --git a/actors/miner/tests/apply_rewards.rs b/actors/miner/tests/apply_rewards.rs index 2c0c4945f..3d1418cda 100644 --- a/actors/miner/tests/apply_rewards.rs +++ b/actors/miner/tests/apply_rewards.rs @@ -10,7 +10,7 @@ use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use fil_actors_runtime::REWARD_ACTOR_ADDR; use fil_actors_runtime::STORAGE_POWER_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; -use fvm_shared::bigint::bigint_ser::BigIntSer; + use fvm_shared::bigint::Zero; use fvm_shared::clock::{ChainEpoch, QuantSpec}; use fvm_shared::econ::TokenAmount; @@ -19,23 +19,21 @@ use fvm_shared::METHOD_SEND; mod util; use fil_actor_miner::testing::check_state_invariants; -use num_traits::Signed; use util::*; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; const PERIOD_OFFSET: ChainEpoch = 1808; #[test] fn funds_are_locked() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); - let rwd = TokenAmount::from(1_000_000); + let rwd = TokenAmount::from_atto(1_000_000); h.apply_rewards(&mut rt, rwd, TokenAmount::zero()); - let expected = TokenAmount::from(750_000); + let expected = TokenAmount::from_atto(750_000); assert_eq!(expected, h.get_locked_funds(&rt)); } @@ -43,7 +41,7 @@ fn funds_are_locked() { fn funds_vest() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let st = h.get_state(&rt); @@ -54,7 +52,7 @@ fn funds_vest() { assert!(st.locked_funds.is_zero()); // Lock some funds with AddLockedFund - let amt = TokenAmount::from(600_000); + let amt = TokenAmount::from_atto(600_000); h.apply_rewards(&mut rt, amt.clone(), TokenAmount::zero()); let st = h.get_state(&rt); let vesting_funds = st.load_vesting_funds(&rt.store).unwrap(); @@ -90,11 +88,11 @@ fn funds_vest() { fn penalty_is_burnt() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); - let rwd = TokenAmount::from(600_000); - let penalty = TokenAmount::from(300_000); + let rwd = TokenAmount::from_atto(600_000); + let penalty = TokenAmount::from_atto(300_000); rt.add_balance(rwd.clone()); h.apply_rewards(&mut rt, rwd.clone(), penalty.clone()); @@ -112,7 +110,7 @@ fn penalty_is_burnt() { fn penalty_is_partially_burnt_and_stored_as_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let st = h.get_state(&rt); assert!(st.fee_debt.is_zero()); @@ -160,7 +158,7 @@ fn penalty_is_partially_burnt_and_stored_as_fee_debt() { fn rewards_pay_back_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let mut st = h.get_state(&rt); @@ -194,8 +192,8 @@ fn rewards_pay_back_fee_debt() { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&pledge_delta)).unwrap(), - TokenAmount::from(0u8), + RawBytes::serialize(&pledge_delta).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); diff --git a/actors/miner/tests/batch_method_network_fees_test.rs b/actors/miner/tests/batch_method_network_fees_test.rs index daa0c0014..546808893 100644 --- a/actors/miner/tests/batch_method_network_fees_test.rs +++ b/actors/miner/tests/batch_method_network_fees_test.rs @@ -6,7 +6,7 @@ use fil_actor_miner::{ }; use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message}; use fvm_ipld_bitfield::BitField; -use fvm_shared::{bigint::BigInt, clock::ChainEpoch, error::ExitCode}; +use fvm_shared::{bigint::BigInt, clock::ChainEpoch, econ::TokenAmount, error::ExitCode}; use lazy_static::lazy_static; mod util; @@ -17,13 +17,11 @@ lazy_static! { static ref PERIOD_OFFSET: ChainEpoch = ChainEpoch::from(100); } -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; - #[test] fn insufficient_funds_for_aggregated_prove_commit_network_fee() { let actor = ActorHarness::new(*PERIOD_OFFSET); let mut rt = actor.new_runtime(); - rt.set_balance(BigInt::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let precommit_epoch = *PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -52,9 +50,9 @@ fn insufficient_funds_for_aggregated_prove_commit_network_fee() { // set base fee extremely high so AggregateProveCommitNetworkFee is > 1000 FIL. Set balance to 1000 FIL to easily cover IP but not cover network fee rt.set_epoch(prove_commit_epoch); - let balance = BigInt::from(1000u64) * BigInt::from(10u64.pow(18)); + let balance = TokenAmount::from_whole(1000); rt.set_balance(balance.clone()); - let base_fee = BigInt::from(10u64.pow(16)); + let base_fee = TokenAmount::from_atto(10u64.pow(16)); rt.base_fee = base_fee.clone(); assert!(aggregate_prove_commit_network_fee(precommits.len() as i64, &base_fee) > balance); @@ -63,7 +61,7 @@ fn insufficient_funds_for_aggregated_prove_commit_network_fee() { ProveCommitConfig::empty(), precommits, make_prove_commit_aggregate(§or_nos_bf), - base_fee, + &base_fee, ); expect_abort(ExitCode::USR_INSUFFICIENT_FUNDS, res); @@ -73,7 +71,7 @@ fn insufficient_funds_for_aggregated_prove_commit_network_fee() { fn insufficient_funds_for_batch_precommit_network_fee() { let actor = ActorHarness::new(*PERIOD_OFFSET); let mut rt = actor.new_runtime(); - rt.set_balance(BigInt::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let precommit_epoch = *PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -91,9 +89,9 @@ fn insufficient_funds_for_batch_precommit_network_fee() { } // set base fee extremely high so AggregateProveCommitNetworkFee is > 1000 FIL. Set balance to 1000 FIL to easily cover PCD but not network fee - let balance = BigInt::from(1000u64) * BigInt::from(10u64.pow(18)); + let balance = TokenAmount::from_whole(1000); rt.set_balance(balance.clone()); - let base_fee = BigInt::from(10u64.pow(16)); + let base_fee = TokenAmount::from_atto(10u64.pow(16)); rt.base_fee = base_fee.clone(); assert!(aggregate_pre_commit_network_fee(precommits.len() as i64, &base_fee) > balance); @@ -101,12 +99,12 @@ fn insufficient_funds_for_batch_precommit_network_fee() { &mut rt, PreCommitSectorBatchParams { sectors: precommits }, &PreCommitBatchConfig { first_for_miner: true, ..Default::default() }, - base_fee, + &base_fee, ); // state untouched let state: State = rt.get_state(); - assert!(state.pre_commit_deposits == BigInt::zero()); + assert!(state.pre_commit_deposits.is_zero()); let expirations = actor.collect_precommit_expirations(&rt, &state); assert_eq!(HashMap::new(), expirations); @@ -121,7 +119,7 @@ fn insufficient_funds_for_batch_precommit_network_fee() { fn insufficient_funds_for_batch_precommit_in_combination_of_fee_debt_and_network_fee() { let actor = ActorHarness::new(*PERIOD_OFFSET); let mut rt = actor.new_runtime(); - rt.set_balance(BigInt::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let precommit_epoch = *PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -139,7 +137,7 @@ fn insufficient_funds_for_batch_precommit_in_combination_of_fee_debt_and_network } // set base fee extremely high so AggregateProveCommitNetworkFee is > 1000 FIL. Set balance to 1000 FIL to easily cover PCD but not network fee - let base_fee = BigInt::from(10u64.pow(16)); + let base_fee = TokenAmount::from_atto(10u64.pow(16)); rt.base_fee = base_fee.clone(); let net_fee = aggregate_pre_commit_network_fee(precommits.len() as i64, &base_fee); @@ -149,20 +147,19 @@ fn insufficient_funds_for_batch_precommit_in_combination_of_fee_debt_and_network rt.replace_state(&state); // give miner almost enough balance to pay both - let mut balance = BigInt::from(2) * net_fee; - balance -= 1; + let balance = (2 * net_fee) - TokenAmount::from_atto(1); rt.set_balance(balance); let res = actor.pre_commit_sector_batch( &mut rt, PreCommitSectorBatchParams { sectors: precommits }, &PreCommitBatchConfig { first_for_miner: true, ..Default::default() }, - base_fee, + &base_fee, ); // state untouched let state: State = rt.get_state(); - assert!(state.pre_commit_deposits == BigInt::zero()); + assert!(state.pre_commit_deposits.is_zero()); let expirations = actor.collect_precommit_expirations(&rt, &state); assert_eq!(HashMap::new(), expirations); @@ -177,7 +174,7 @@ fn insufficient_funds_for_batch_precommit_in_combination_of_fee_debt_and_network fn enough_funds_for_fee_debt_and_network_fee_but_not_for_pcd() { let actor = ActorHarness::new(*PERIOD_OFFSET); let mut rt = actor.new_runtime(); - rt.set_balance(BigInt::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let precommit_epoch = *PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -195,7 +192,7 @@ fn enough_funds_for_fee_debt_and_network_fee_but_not_for_pcd() { } // set base fee and fee debt high - let base_fee = BigInt::from(10u64.pow(16)); + let base_fee = TokenAmount::from_atto(10u64.pow(16)); rt.base_fee = base_fee.clone(); let net_fee = aggregate_pre_commit_network_fee(precommits.len() as i64, &base_fee); // setup miner to have feed debt equal to net fee @@ -211,12 +208,12 @@ fn enough_funds_for_fee_debt_and_network_fee_but_not_for_pcd() { &mut rt, PreCommitSectorBatchParams { sectors: precommits }, &PreCommitBatchConfig { first_for_miner: true, ..Default::default() }, - base_fee, + &base_fee, ); expect_abort_contains_message( ExitCode::USR_INSUFFICIENT_FUNDS, - "insufficient funds 0 for pre-commit deposit", + "insufficient funds 0.0 for pre-commit deposit", res, ); @@ -231,7 +228,7 @@ fn enough_funds_for_fee_debt_and_network_fee_but_not_for_pcd() { fn enough_funds_for_everything() { let actor = ActorHarness::new(*PERIOD_OFFSET); let mut rt = actor.new_runtime(); - rt.set_balance(BigInt::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let precommit_epoch = *PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); actor.construct_and_verify(&mut rt); @@ -249,7 +246,7 @@ fn enough_funds_for_everything() { } // set base fee extremely high so AggregateProveCommitNetworkFee is > 1000 FIL. Set balance to 1000 FIL to easily cover PCD but not network fee - let base_fee = BigInt::from(10u64.pow(16)); + let base_fee = TokenAmount::from_atto(10u64.pow(16)); rt.base_fee = base_fee.clone(); let net_fee = aggregate_pre_commit_network_fee(precommits.len() as i64, &base_fee); @@ -259,7 +256,7 @@ fn enough_funds_for_everything() { rt.replace_state(&state); // give miner enough balance to pay both and pcd - let mut balance = BigInt::from(2) * net_fee; + let mut balance = 2 * net_fee; let one_sector_power_estimate = qa_power_for_weight( actor.sector_size, expiration - precommit_epoch, @@ -279,7 +276,7 @@ fn enough_funds_for_everything() { &mut rt, PreCommitSectorBatchParams { sectors: precommits }, &PreCommitBatchConfig { first_for_miner: true, ..Default::default() }, - base_fee, + &base_fee, ) .unwrap(); diff --git a/actors/miner/tests/change_owner_address_test.rs b/actors/miner/tests/change_owner_address_test.rs index a6b6603f0..efcb87c26 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_actors_runtime::test_utils::{ expect_abort, new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; -use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; +use fvm_shared::{address::Address, error::ExitCode}; mod util; use util::*; @@ -10,13 +10,12 @@ const NEW_ADDRESS: Address = Address::new_id(1001); const OTHER_ADDRESS: Address = Address::new_id(1002); fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); let period_offset = 100; let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); (h, rt) } diff --git a/actors/miner/tests/change_peer_id_test.rs b/actors/miner/tests/change_peer_id_test.rs index 08a1b03d9..1fd3cbeb2 100644 --- a/actors/miner/tests/change_peer_id_test.rs +++ b/actors/miner/tests/change_peer_id_test.rs @@ -1,18 +1,16 @@ use fil_actors_runtime::test_utils::MockRuntime; -use fvm_shared::econ::TokenAmount; mod util; use util::*; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); let period_offset = 100; let precommit_epoch = 1; let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); rt.set_epoch(precommit_epoch); (h, rt) diff --git a/actors/miner/tests/change_worker_address_test.rs b/actors/miner/tests/change_worker_address_test.rs index 6e61a8a13..58cd4d9b1 100644 --- a/actors/miner/tests/change_worker_address_test.rs +++ b/actors/miner/tests/change_worker_address_test.rs @@ -16,13 +16,12 @@ use num_traits::Zero; use util::*; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); let period_offset = 100; let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); (h, rt) } diff --git a/actors/miner/tests/check_sector_proven_test.rs b/actors/miner/tests/check_sector_proven_test.rs index 8a2fa0030..56223fe44 100644 --- a/actors/miner/tests/check_sector_proven_test.rs +++ b/actors/miner/tests/check_sector_proven_test.rs @@ -1,17 +1,16 @@ use fil_actors_runtime::test_utils::{expect_abort, MockRuntime}; -use fvm_shared::{econ::TokenAmount, error::ExitCode}; +use fvm_shared::error::ExitCode; mod util; use util::*; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); let period_offset = 100; let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); (h, rt) } diff --git a/actors/miner/tests/compact_partitions_test.rs b/actors/miner/tests/compact_partitions_test.rs index cbec84b3f..a873f3f39 100644 --- a/actors/miner/tests/compact_partitions_test.rs +++ b/actors/miner/tests/compact_partitions_test.rs @@ -11,18 +11,16 @@ use fvm_ipld_bitfield::BitField; use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, error::ExitCode, sector::SectorNumber}; mod util; -use fvm_shared::bigint::{BigInt, Zero}; +use fvm_shared::bigint::Zero; use itertools::Itertools; use util::*; const PERIOD_OFFSET: ChainEpoch = 100; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); - let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); (h, rt) } @@ -71,7 +69,7 @@ fn compacting_a_partition_with_both_live_and_dead_sectors_removes_dead_sectors_r // terminate sector 1 rt.set_epoch(rt.epoch + 100); - h.apply_rewards(&mut rt, BIG_REWARDS.into(), BigInt::zero()); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); let terminated_sector = §ors_info[0]; let sector_size = terminated_sector.seal_proof.sector_size().unwrap(); @@ -96,7 +94,7 @@ fn compacting_a_partition_with_both_live_and_dead_sectors_removes_dead_sectors_r &h.epoch_qa_power_smooth, §or_power, &h.epoch_reward_smooth, - &BigInt::zero(), + &TokenAmount::zero(), 0, ); diff --git a/actors/miner/tests/compact_sector_numbers_tests.rs b/actors/miner/tests/compact_sector_numbers_tests.rs index 79f7883da..1ba75fa1e 100644 --- a/actors/miner/tests/compact_sector_numbers_tests.rs +++ b/actors/miner/tests/compact_sector_numbers_tests.rs @@ -1,7 +1,7 @@ use fil_actors_runtime::test_utils::{expect_abort, MockRuntime}; use fvm_shared::address::Address; use fvm_shared::sector::MAX_SECTOR_NUMBER; -use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, error::ExitCode}; +use fvm_shared::{clock::ChainEpoch, error::ExitCode}; mod util; use util::*; @@ -9,12 +9,10 @@ use util::*; const PERIOD_OFFSET: ChainEpoch = 100; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); - let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); (h, rt) } diff --git a/actors/miner/tests/confirm_update_worker_key_test.rs b/actors/miner/tests/confirm_update_worker_key_test.rs index 296f2075d..d19ff4600 100644 --- a/actors/miner/tests/confirm_update_worker_key_test.rs +++ b/actors/miner/tests/confirm_update_worker_key_test.rs @@ -3,7 +3,7 @@ use fil_actors_runtime::{ runtime::{Runtime, RuntimePolicy}, test_utils::MockRuntime, }; -use fvm_shared::{address::Address, econ::TokenAmount}; +use fvm_shared::address::Address; mod util; use util::*; @@ -11,14 +11,13 @@ use util::*; const NEW_WORKER: Address = Address::new_id(999); fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 20u128.pow(23); let period_offset = 100; let current_epoch = 5; let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); rt.set_epoch(current_epoch); (h, rt) diff --git a/actors/miner/tests/deadline_cron.rs b/actors/miner/tests/deadline_cron.rs index 8be43ec78..f0536fba7 100644 --- a/actors/miner/tests/deadline_cron.rs +++ b/actors/miner/tests/deadline_cron.rs @@ -19,14 +19,12 @@ use crate::util::*; const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; const PERIOD_OFFSET: ChainEpoch = 100; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; -const BIG_REWARDS: u128 = 1_000 * 1e18 as u128; #[test] fn cron_on_inactive_state() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let st = h.get_state(&rt); @@ -45,7 +43,7 @@ fn cron_on_inactive_state() { fn sector_expires() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let sectors = @@ -92,7 +90,7 @@ fn sector_expires() { fn sector_expires_and_repays_fee_debt() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let sectors = @@ -122,7 +120,7 @@ fn sector_expires_and_repays_fee_debt() { // introduce lots of fee debt let mut st = h.get_state(&rt); - let fee_debt = TokenAmount::from(400) * TokenAmount::from(1e18 as u64); + let fee_debt = TokenAmount::from_whole(400); st.fee_debt = fee_debt; rt.replace_state(&st); // Miner balance = IP, debt repayment covered by unlocked funds @@ -149,7 +147,7 @@ fn sector_expires_and_repays_fee_debt() { fn detects_and_penalizes_faults() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let active_sectors = @@ -166,7 +164,7 @@ fn detects_and_penalizes_faults() { let all_sectors = [active_sectors.clone(), unproven_sectors].concat(); // add lots of funds so penalties come from vesting funds - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::zero()); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); let st = h.get_state(&rt); let (dl_idx, p_idx) = @@ -239,11 +237,11 @@ fn detects_and_penalizes_faults() { fn test_cron_run_trigger_faults() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); // add lots of funds so we can pay penalties without going into debt - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::zero()); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); // create enough sectors that one will be in a different partition let all_sectors = diff --git a/actors/miner/tests/deadline_cron_defers_stops_restarts.rs b/actors/miner/tests/deadline_cron_defers_stops_restarts.rs index 684f50744..a74c16a3a 100644 --- a/actors/miner/tests/deadline_cron_defers_stops_restarts.rs +++ b/actors/miner/tests/deadline_cron_defers_stops_restarts.rs @@ -1,17 +1,15 @@ use fvm_shared::clock::ChainEpoch; -use fvm_shared::econ::TokenAmount; mod util; use util::*; const PERIOD_OFFSET: ChainEpoch = 100; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; #[test] fn cron_enrolls_on_precommit_prove_commits_and_continues_enrolling() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let cron_ctrl = CronControl::default(); @@ -37,7 +35,7 @@ fn cron_enrolls_on_precommit_prove_commits_and_continues_enrolling() { fn cron_enrolls_on_precommit_expires_on_pcd_expiration_re_enrolls_on_new_precommit_immediately() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let epoch = PERIOD_OFFSET + 1; rt.set_epoch(epoch); h.construct_and_verify(&mut rt); @@ -52,7 +50,7 @@ fn cron_enrolls_on_precommit_expires_on_pcd_expiration_re_enrolls_on_new_precomm ) { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let mut epoch = PERIOD_OFFSET + 1; rt.set_epoch(epoch); h.construct_and_verify(&mut rt); @@ -71,7 +69,7 @@ fn cron_enrolls_on_precommit_expires_on_pcd_expiration_re_enrolls_on_new_precomm fn enroll_pcd_expire_re_enroll_x_3() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); let mut epoch = PERIOD_OFFSET + 1; rt.set_epoch(epoch); h.construct_and_verify(&mut rt); diff --git a/actors/miner/tests/declare_faults.rs b/actors/miner/tests/declare_faults.rs index 3e5768ecc..3872eaf60 100644 --- a/actors/miner/tests/declare_faults.rs +++ b/actors/miner/tests/declare_faults.rs @@ -1,9 +1,11 @@ use fil_actor_miner::pledge_penalty_for_continued_fault; use fil_actor_miner::power_for_sectors; -use fvm_shared::bigint::BigInt; + use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; +use num_traits::Zero; + mod util; use crate::util::*; @@ -12,23 +14,22 @@ use crate::util::*; const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; const PERIOD_OFFSET: ChainEpoch = 100; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; #[test] fn declare_fault_pays_fee_at_window_post() { - let big_rewards: BigInt = BigInt::from(1_000_000_000_000_000_000_000_i128); + let big_rewards = TokenAmount::from_whole(1000); // Get sector into proving state let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let all_sectors = h.commit_and_prove_sectors(&mut rt, 1, DEFAULT_SECTOR_EXPIRATION as u64, vec![], true); let pwr = power_for_sectors(h.sector_size, &all_sectors); // add lots of funds so penalties come from vesting funds - h.apply_rewards(&mut rt, big_rewards, BigInt::from(0)); + h.apply_rewards(&mut rt, big_rewards, TokenAmount::zero()); // find deadline for sector let st = h.get_state(&rt); diff --git a/actors/miner/tests/declare_recoveries.rs b/actors/miner/tests/declare_recoveries.rs index 209c778af..29ae87f3f 100644 --- a/actors/miner/tests/declare_recoveries.rs +++ b/actors/miner/tests/declare_recoveries.rs @@ -4,18 +4,18 @@ use fil_actors_runtime::test_utils::expect_abort_contains_message; use fil_actors_runtime::test_utils::MockRuntime; use fvm_ipld_bitfield::BitField; use fvm_shared::address::Address; -use fvm_shared::bigint::BigInt; + use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::{ConsensusFault, ConsensusFaultType}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; + use num_traits::Zero; mod util; use crate::util::*; const PERIOD_OFFSET: ChainEpoch = 100; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; #[test] fn recovery_happy_path() { @@ -38,7 +38,7 @@ fn recovery_happy_path() { dl_idx, p_idx, BitField::try_from_bits([one_sector[0].sector_number]).unwrap(), - BigInt::zero(), + TokenAmount::zero(), ) .unwrap(); @@ -79,7 +79,7 @@ fn recovery_must_pay_back_fee_debt() { h.advance_deadline( &mut rt, CronConfig { - continued_faults_penalty: BigInt::zero(), // fee is instead added to debt + continued_faults_penalty: TokenAmount::zero(), // fee is instead added to debt ..Default::default() }, ); @@ -96,7 +96,7 @@ fn recovery_must_pay_back_fee_debt() { dl_idx, p_idx, BitField::try_from_bits([one_sector[0].sector_number]).unwrap(), - BigInt::zero(), + TokenAmount::zero(), ), ); @@ -116,7 +116,7 @@ fn recovery_must_pay_back_fee_debt() { let p = dl.load_partition(&rt.store, p_idx).unwrap(); assert_eq!(p.faults, p.recoveries); st = h.get_state(&rt); - assert_eq!(BigInt::zero(), st.fee_debt); + assert!(st.fee_debt.is_zero()); h.check_state(&rt); } @@ -156,7 +156,7 @@ fn recovery_fails_during_active_consensus_fault() { dl_idx, p_idx, BitField::try_from_bits([one_sector[0].sector_number]).unwrap(), - BigInt::zero(), + TokenAmount::zero(), ), ); h.check_state(&rt); @@ -166,7 +166,7 @@ fn setup() -> (ActorHarness, MockRuntime) { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); (h, rt) } diff --git a/actors/miner/tests/expected_reward_for_power_clampted_at_atto_fil_test.rs b/actors/miner/tests/expected_reward_for_power_clampted_at_atto_fil_test.rs index d8a76627c..1577683bd 100644 --- a/actors/miner/tests/expected_reward_for_power_clampted_at_atto_fil_test.rs +++ b/actors/miner/tests/expected_reward_for_power_clampted_at_atto_fil_test.rs @@ -8,7 +8,7 @@ use fvm_shared::smooth::FilterEstimate; #[test] fn expected_zero_valued_br_clamped_at_1_attofil() { - let epoch_target_reward = TokenAmount::from(1u64 << 50); + let epoch_target_reward = BigInt::from(1u64 << 50); let zero_qa_power = StoragePower::zero(); let network_qa_power = StoragePower::from(1u64 << 10); let power_rate_of_change = StoragePower::from(1 << 10); @@ -21,12 +21,12 @@ fn expected_zero_valued_br_clamped_at_1_attofil() { &zero_qa_power, 1, ); - assert_eq!(BigInt::from(1), br_clamped); + assert_eq!(TokenAmount::from_atto(1), br_clamped); } #[test] fn expected_negative_value_br_clamped_at_1_atto_fil() { - let epoch_target_reward = TokenAmount::from(1u64 << 50); + let epoch_target_reward = BigInt::from(1u64 << 50); let qa_sector_power = StoragePower::from(1u64 << 36); let network_qa_power = StoragePower::from(1u64 << 10); let power_rate_of_change = StoragePower::from(1 << 10).neg(); @@ -39,5 +39,5 @@ fn expected_negative_value_br_clamped_at_1_atto_fil() { &qa_sector_power, 4, ); - assert_eq!(BigInt::from(1), four_br_clamped); + assert_eq!(TokenAmount::from_atto(1), four_br_clamped); } diff --git a/actors/miner/tests/expiration_queue.rs b/actors/miner/tests/expiration_queue.rs index b4d58c233..1000e3a8c 100644 --- a/actors/miner/tests/expiration_queue.rs +++ b/actors/miner/tests/expiration_queue.rs @@ -31,7 +31,7 @@ fn early_sectors() -> BitField { mk_bitfield([2, 3]) } fn on_time_pledge() -> TokenAmount { - TokenAmount::from(1_000) + TokenAmount::from_atto(1_000) } fn active_power() -> PowerPair { PowerPair { raw: StoragePower::from(1 << 13), qa: StoragePower::from(1 << 14) } @@ -78,7 +78,7 @@ fn adds_sectors_and_power_to_non_empty_set() { set.add( &mk_bitfield([6, 7, 11]), &mk_bitfield([1, 4]), - &TokenAmount::from(300), + &TokenAmount::from_atto(300), &power_pair(3, 13), &power_pair(3, 11), ) @@ -86,7 +86,7 @@ fn adds_sectors_and_power_to_non_empty_set() { assert_eq!(set.on_time_sectors, mk_bitfield([5, 6, 7, 8, 9, 11])); assert_eq!(set.early_sectors, mk_bitfield([1, 2, 3, 4])); - assert_eq!(set.on_time_pledge, TokenAmount::from(1300)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(1300)); let active = power_pair(1, 15); assert_eq!(set.active_power, active); let faulty = power_pair(1, 13); @@ -100,7 +100,7 @@ fn removes_sectors_and_power_set() { set.remove( &mk_bitfield([9]), &mk_bitfield([2]), - &TokenAmount::from(800), + &TokenAmount::from_atto(800), &power_pair(3, 11), &power_pair(3, 9), ) @@ -108,7 +108,7 @@ fn removes_sectors_and_power_set() { assert_eq!(set.on_time_sectors, mk_bitfield([5, 8])); assert_eq!(set.early_sectors, mk_bitfield([3])); - assert_eq!(set.on_time_pledge, TokenAmount::from(200)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(200)); let active = power_pair(1, 11); assert_eq!(set.active_power, active); let faulty = power_pair(1, 9); @@ -123,7 +123,7 @@ fn remove_fails_when_pledge_underflows() { .remove( &mk_bitfield([9]), &mk_bitfield([2]), - &TokenAmount::from(1200), + &TokenAmount::from_atto(1200), &power_pair(3, 11), &power_pair(3, 9), ) @@ -178,7 +178,7 @@ fn remove_fails_when_active_or_fault_qa_power_underflows() { .remove( &mk_bitfield([9]), &mk_bitfield([2]), - &TokenAmount::from(200), + &TokenAmount::from_atto(200), &power_pair(3, 12), &power_pair(3, 9), ) @@ -195,7 +195,7 @@ fn remove_fails_when_active_or_fault_qa_power_underflows() { .remove( &mk_bitfield([9]), &mk_bitfield([2]), - &TokenAmount::from(200), + &TokenAmount::from_atto(200), &power_pair(3, 11), &power_pair(3, 10), ) @@ -259,7 +259,7 @@ fn added_sectors_can_be_popped_off_queue() { let (sec_nums, power, pledge) = queue.add_active_sectors(§ors(), SECTOR_SIZE).unwrap(); assert_eq!(sec_nums, mk_bitfield([1, 2, 3, 4, 5, 6])); assert_eq!(power, power_for_sectors(SECTOR_SIZE, §ors())); - assert_eq!(pledge, TokenAmount::from(6015)); + assert_eq!(pledge, TokenAmount::from_atto(6015)); // default test quantizing of 1 means every sector is in its own expriation set assert_eq!(sectors().len(), queue.amt.count() as usize); @@ -278,7 +278,7 @@ fn added_sectors_can_be_popped_off_queue() { let active_power = power_for_sectors(SECTOR_SIZE, §ors()[0..3]); let faulty_power = PowerPair::zero(); - assert_eq!(set.on_time_pledge, TokenAmount::from(3003)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(3003)); assert_eq!(set.active_power, active_power); assert_eq!(set.faulty_power, faulty_power); @@ -288,7 +288,7 @@ fn added_sectors_can_be_popped_off_queue() { assert_eq!(set.on_time_sectors, mk_bitfield([4, 5, 6])); assert!(set.early_sectors.is_empty()); - assert_eq!(set.on_time_pledge, TokenAmount::from(3012)); // sum of last 3 sector pledges + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(3012)); // sum of last 3 sector pledges assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[0..3])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -306,7 +306,7 @@ fn quantizes_added_sectors_by_expiration() { let (sec_nums, power, pledge) = queue.add_active_sectors(§ors(), SECTOR_SIZE).unwrap(); assert_eq!(sec_nums, mk_bitfield([1, 2, 3, 4, 5, 6])); assert_eq!(power, power_for_sectors(SECTOR_SIZE, §ors())); - assert_eq!(pledge, TokenAmount::from(6015)); + assert_eq!(pledge, TokenAmount::from_atto(6015)); // quantizing spec means sectors should be grouped into 3 sets expiring at 3, 8 and 13 assert_eq!(queue.amt.count(), 3); @@ -373,7 +373,7 @@ fn reschedules_sectors_as_faults() { assert_eq!(set.on_time_sectors, mk_bitfield([1, 2])); assert!(set.early_sectors.is_empty()); - assert_eq!(set.on_time_pledge, TokenAmount::from(2001)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2001)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[0..1])); assert_eq!(set.faulty_power, power_for_sectors(SECTOR_SIZE, §ors()[1..2])); @@ -385,7 +385,7 @@ fn reschedules_sectors_as_faults() { assert_eq!(set.early_sectors, mk_bitfield([5])); // pledge is kept from original 2 sectors. Pledge from new early sector is NOT added. - assert_eq!(set.on_time_pledge, TokenAmount::from(2005)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2005)); assert_eq!(set.active_power, PowerPair::zero()); assert_eq!(set.faulty_power, power_for_sectors(SECTOR_SIZE, §ors()[2..5])); @@ -398,7 +398,7 @@ fn reschedules_sectors_as_faults() { assert!(set.early_sectors.is_empty()); // Pledge from sector moved from this set is dropped - assert_eq!(set.on_time_pledge, TokenAmount::from(1005)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(1005)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[5..])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -428,7 +428,7 @@ fn reschedules_all_sectors_as_faults() { assert_eq!(set.on_time_sectors, mk_bitfield([1, 2])); // sectors are unmoved assert!(set.early_sectors.is_empty()); - assert_eq!(set.on_time_pledge, TokenAmount::from(2001)); // pledge is same + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2001)); // pledge is same // active power is converted to fault power assert_eq!(set.active_power, PowerPair::zero()); @@ -442,7 +442,7 @@ fn reschedules_all_sectors_as_faults() { assert_eq!(set.early_sectors, mk_bitfield([5, 6])); // pledge is kept from original 2 sectors. Pledge from new early sectors is NOT added. - assert_eq!(set.on_time_pledge, TokenAmount::from(2005)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2005)); // fault power is all power for sectors previously in the second and third sets assert_eq!(set.active_power, PowerPair::zero()); @@ -491,7 +491,7 @@ fn reschedule_recover_restores_all_sector_stats() { assert!(set.early_sectors.is_empty()); // pledge from both sectors - assert_eq!(set.on_time_pledge, TokenAmount::from(2001)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2001)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[0..2])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -504,7 +504,7 @@ fn reschedule_recover_restores_all_sector_stats() { assert!(set.early_sectors.is_empty()); // pledge is kept from original 2 sectors - assert_eq!(set.on_time_pledge, TokenAmount::from(2005)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2005)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[2..4])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -517,7 +517,7 @@ fn reschedule_recover_restores_all_sector_stats() { assert!(set.early_sectors.is_empty()); // Pledge from sector 5 is restored - assert_eq!(set.on_time_pledge, TokenAmount::from(2009)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2009)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors()[4..])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -551,7 +551,7 @@ fn replaces_sectors_with_new_sectors() { assert_eq!(added, mk_bitfield([3, 5])); let added_power = power_for_sectors(SECTOR_SIZE, &to_add); assert_eq!(power_delta, &added_power - &power_for_sectors(SECTOR_SIZE, &to_remove)); - assert_eq!(TokenAmount::from(1002 + 1004 - 1000 - 1001 - 1003), pledge_delta); + assert_eq!(TokenAmount::from_atto(1002 + 1004 - 1000 - 1001 - 1003), pledge_delta); // first set is gone require_no_expiration_groups_before(9, &mut queue); @@ -563,7 +563,7 @@ fn replaces_sectors_with_new_sectors() { assert!(set.early_sectors.is_empty()); // pledge and power is only from sector 3 - assert_eq!(set.on_time_pledge, TokenAmount::from(1002)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(1002)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors[2..3])); assert_eq!(set.faulty_power, PowerPair::zero()); @@ -575,7 +575,7 @@ fn replaces_sectors_with_new_sectors() { assert!(set.early_sectors.is_empty()); // pledge and power are the sum of old and new sectors - assert_eq!(set.on_time_pledge, TokenAmount::from(2009)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(2009)); assert_eq!(set.active_power, power_for_sectors(SECTOR_SIZE, §ors[4..])); assert_eq!(set.faulty_power, PowerPair::zero()); } @@ -611,7 +611,7 @@ fn removes_sectors() { // assert all return values are correct assert_eq!(removed.on_time_sectors, mk_bitfield([1, 4])); assert_eq!(removed.early_sectors, mk_bitfield([5, 6])); - assert_eq!(removed.on_time_pledge, TokenAmount::from(1000 + 1003)); // only on-time sectors + assert_eq!(removed.on_time_pledge, TokenAmount::from_atto(1000 + 1003)); // only on-time sectors assert_eq!(removed.active_power, power_for_sectors(SECTOR_SIZE, §ors()[0..1])); assert_eq!(removed.faulty_power, power_for_sectors(SECTOR_SIZE, §ors()[3..6])); assert_eq!(recovering_power, power_for_sectors(SECTOR_SIZE, §ors()[5..6])); @@ -624,7 +624,7 @@ fn removes_sectors() { assert_eq!(set.on_time_sectors, mk_bitfield([2])); assert!(set.early_sectors.is_empty()); - assert_eq!(set.on_time_pledge, TokenAmount::from(1001)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(1001)); assert_eq!(set.active_power, PowerPair::zero()); assert_eq!(set.faulty_power, power_for_sectors(SECTOR_SIZE, §ors()[1..2])); @@ -634,7 +634,7 @@ fn removes_sectors() { assert_eq!(set.on_time_sectors, mk_bitfield([3])); assert!(set.early_sectors.is_empty()); - assert_eq!(set.on_time_pledge, TokenAmount::from(1002)); + assert_eq!(set.on_time_pledge, TokenAmount::from_atto(1002)); assert_eq!(set.active_power, PowerPair::zero()); assert_eq!(set.faulty_power, power_for_sectors(SECTOR_SIZE, §ors()[2..3])); @@ -703,7 +703,7 @@ fn test_sector( sector_number, deal_weight: DealWeight::from(weight), verified_deal_weight: DealWeight::from(vweight), - initial_pledge: TokenAmount::from(pledge), + initial_pledge: TokenAmount::from_atto(pledge), sealed_cid: make_sealed_cid(format!("commR-{}", sector_number).as_bytes()), ..Default::default() } diff --git a/actors/miner/tests/extend_sector_expiration_test.rs b/actors/miner/tests/extend_sector_expiration_test.rs index 7ac23aa22..d54ba95f6 100644 --- a/actors/miner/tests/extend_sector_expiration_test.rs +++ b/actors/miner/tests/extend_sector_expiration_test.rs @@ -7,9 +7,7 @@ use fil_actors_runtime::{ test_utils::{expect_abort_contains_message, MockRuntime}, }; use fvm_ipld_bitfield::{BitField, UnvalidatedBitField}; -use fvm_shared::{ - clock::ChainEpoch, econ::TokenAmount, error::ExitCode, sector::RegisteredSealProof, -}; +use fvm_shared::{clock::ChainEpoch, error::ExitCode, sector::RegisteredSealProof}; mod util; use itertools::Itertools; @@ -19,7 +17,6 @@ use util::*; const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; fn setup() -> (ActorHarness, MockRuntime) { - let big_balance = 10u128.pow(24); let period_offset = 100; let precommit_epoch = 1; @@ -27,7 +24,7 @@ fn setup() -> (ActorHarness, MockRuntime) { // reduce the partition size h.set_proof_type(RegisteredSealProof::StackedDRG512MiBV1); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(BIG_BALANCE.clone()); rt.set_epoch(precommit_epoch); (h, rt) diff --git a/actors/miner/tests/miner_actor_test_commitment.rs b/actors/miner/tests/miner_actor_test_commitment.rs index c9b9aa503..6d2ceae51 100644 --- a/actors/miner/tests/miner_actor_test_commitment.rs +++ b/actors/miner/tests/miner_actor_test_commitment.rs @@ -25,7 +25,6 @@ use util::*; const DEFAULT_SECTOR_EXPIRATION: i64 = 220; // A balance for use in tests where the miner's low balance is not interesting. -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; fn assert_simple_pre_commit( sector_number: SectorNumber, @@ -38,7 +37,7 @@ fn assert_simple_pre_commit( let mut h = ActorHarness::new(period_offset); h.set_proof_type(RegisteredSealProof::StackedDRG64GiBV1); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -136,7 +135,7 @@ mod miner_actor_test_commitment { #[test] fn insufficient_funds_for_pre_commit() { let period_offset = ChainEpoch::from(100); - let insufficient_balance = TokenAmount::from(10u8); // 10 AttoFIL + let insufficient_balance = TokenAmount::from_atto(10u8); // 10 AttoFIL let mut h = ActorHarness::new(period_offset); h.set_proof_type(RegisteredSealProof::StackedDRG64GiBV1); @@ -168,7 +167,7 @@ mod miner_actor_test_commitment { let mut h = ActorHarness::new(period_offset); h.set_proof_type(RegisteredSealProof::StackedDRG32GiBV1); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -206,7 +205,7 @@ mod miner_actor_test_commitment { let mut h = ActorHarness::new(period_offset); h.set_proof_type(RegisteredSealProof::StackedDRG64GiBV1); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -218,7 +217,7 @@ mod miner_actor_test_commitment { deadline.period_end() + DEFAULT_SECTOR_EXPIRATION * rt.policy.wpost_proving_period; let mut st: State = rt.get_state(); - st.fee_debt = TokenAmount::from(9999); + st.fee_debt = TokenAmount::from_atto(9999); rt.replace_state(&st); let precommit_params = h.make_pre_commit_params(101, challenge_epoch, expiration, vec![1]); @@ -236,7 +235,7 @@ mod miner_actor_test_commitment { let mut h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -499,8 +498,7 @@ mod miner_actor_test_commitment { // Try to precommit while in fee debt with insufficient balance { let mut st: State = rt.get_state(); - st.fee_debt = - rt.balance.borrow().clone() + TokenAmount::from(10_000_000_000_000_000_000i128); + st.fee_debt = &*rt.balance.borrow() + TokenAmount::from_whole(10); rt.replace_state(&st); let precommit_params = h.make_pre_commit_params(102, challenge_epoch, expiration, vec![]); @@ -554,7 +552,7 @@ mod miner_actor_test_commitment { let mut h = ActorHarness::new(period_offset); h.set_proof_type(proof); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); rt.set_epoch(period_offset + 1); @@ -619,7 +617,7 @@ mod miner_actor_test_commitment { let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); h.construct_and_verify(&mut rt); @@ -662,7 +660,7 @@ mod miner_actor_test_commitment { let mut h = ActorHarness::new(period_offset); h.set_proof_type(RegisteredSealProof::StackedDRG32GiBV1P1); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; rt.set_epoch(precommit_epoch); @@ -680,7 +678,7 @@ mod miner_actor_test_commitment { .add_locked_funds( &rt.store, rt.epoch, - &TokenAmount::from(1000u16), + &TokenAmount::from_atto(1000u16), &VestSpec { initial_delay: 0, vest_period: 1, step_duration: 1, quantization: 1 }, ) .unwrap(); diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 4243fa267..9f8c770cd 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -13,6 +13,7 @@ use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, SectorSize}; use cid::Cid; +use num_traits::Zero; mod util; @@ -72,7 +73,7 @@ fn simple_construction() { env.worker, AccountMethod::PubkeyAddress as u64, RawBytes::default(), - TokenAmount::from(0), + TokenAmount::zero(), RawBytes::serialize(env.worker_key).unwrap(), ExitCode::OK, ); @@ -96,8 +97,8 @@ fn simple_construction() { assert_eq!(SectorSize::_32GiB, info.sector_size); assert_eq!(2349, info.window_post_partition_sectors); - assert_eq!(TokenAmount::from(0), state.pre_commit_deposits); - assert_eq!(TokenAmount::from(0), state.locked_funds); + assert_eq!(TokenAmount::zero(), state.pre_commit_deposits); + assert_eq!(TokenAmount::zero(), state.locked_funds); assert_ne!(Cid::default(), state.pre_committed_sectors); assert_ne!(Cid::default(), state.sectors); @@ -145,7 +146,7 @@ fn control_addresses_are_resolved_during_construction() { env.worker, AccountMethod::PubkeyAddress as u64, RawBytes::default(), - TokenAmount::from(0), + TokenAmount::zero(), RawBytes::serialize(env.worker_key).unwrap(), ExitCode::OK, ); @@ -179,7 +180,7 @@ fn fails_if_control_address_is_not_an_account_actor() { env.worker, AccountMethod::PubkeyAddress as u64, RawBytes::default(), - TokenAmount::from(0), + TokenAmount::zero(), RawBytes::serialize(env.worker_key).unwrap(), ExitCode::OK, ); diff --git a/actors/miner/tests/miner_actor_test_partitions.rs b/actors/miner/tests/miner_actor_test_partitions.rs index de6af74ef..99f4e77fe 100644 --- a/actors/miner/tests/miner_actor_test_partitions.rs +++ b/actors/miner/tests/miner_actor_test_partitions.rs @@ -479,7 +479,7 @@ mod miner_actor_test_partitions { // remove 3 sectors starting with 2 let old_sectors = sectors()[1..4].to_vec(); let old_sector_power = power_for_sectors(SECTOR_SIZE, &old_sectors); - let old_sector_pledge: u64 = 1001 + 1002 + 1003; + let old_sector_pledge = TokenAmount::from_atto(1001 + 1002 + 1003); // replace 1 and add 2 new sectors let new_sectors = vec![ @@ -488,7 +488,7 @@ mod miner_actor_test_partitions { test_sector(18, 8, 152, 262, 3002), ]; let new_sector_power = power_for_sectors(SECTOR_SIZE, &new_sectors); - let new_sector_pledge = TokenAmount::from(3000u64 + 3001 + 3002); + let new_sector_pledge = TokenAmount::from_atto(3000u64 + 3001 + 3002); let (power_delta, pledge_delta) = partition .replace_sectors(&rt.store, &old_sectors, &new_sectors, SECTOR_SIZE, QUANT_SPEC) @@ -782,7 +782,7 @@ mod miner_actor_test_partitions { assert_bitfield_equals(&expset.on_time_sectors, &[1, 2]); assert_bitfield_equals(&expset.early_sectors, &[4]); - assert_eq!(TokenAmount::from(1000u64 + 1001), expset.on_time_pledge); + assert_eq!(TokenAmount::from_atto(1000u64 + 1001), expset.on_time_pledge); // active power only contains power from non-faulty sectors assert_eq!(expset.active_power, power_for_sectors(SECTOR_SIZE, §ors()[..2])); diff --git a/actors/miner/tests/miner_actor_test_precommit_batch.rs b/actors/miner/tests/miner_actor_test_precommit_batch.rs index 6527d2024..816cec36a 100644 --- a/actors/miner/tests/miner_actor_test_precommit_batch.rs +++ b/actors/miner/tests/miner_actor_test_precommit_batch.rs @@ -12,7 +12,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::SectorNumber; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use std::collections::HashMap; @@ -24,7 +24,6 @@ use util::*; const DEFAULT_SECTOR_EXPIRATION: i64 = 220; // A balance for use in tests where the miner's low balance is not interesting. -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; #[derive(Default, Clone)] struct DealSpec { @@ -63,7 +62,7 @@ fn assert_simple_batch( sector_weights: vec![SectorWeights::default(); batch_size], first_for_miner: true, }; - let mut deposits = vec![BigInt::default(); batch_size]; + let mut deposits = vec![TokenAmount::zero(); batch_size]; for i in 0..batch_size { let mut deals = DealSpec::default(); @@ -98,8 +97,8 @@ fn assert_simple_batch( ); } let net_fee = aggregate_pre_commit_network_fee(batch_size as i64, &base_fee); - let total_deposit: BigInt = deposits.iter().sum(); - let total_balance = net_fee + total_deposit.clone(); + let total_deposit: TokenAmount = deposits.iter().sum(); + let total_balance = net_fee + &total_deposit; rt.set_balance(total_balance + balance_surplus); if exit_code != ExitCode::OK { @@ -110,7 +109,7 @@ fn assert_simple_batch( &mut rt, PreCommitSectorBatchParams { sectors }, &conf, - base_fee, + &base_fee, ), ); rt.reset(); @@ -126,7 +125,7 @@ fn assert_simple_batch( &mut rt, PreCommitSectorBatchParams { sectors: sectors.clone() }, &conf, - base_fee, + &base_fee, ); // Check precommits @@ -239,7 +238,7 @@ mod miner_actor_precommit_batch { fn insufficient_balance() { assert_simple_batch( 10, - TokenAmount::from(-1), + TokenAmount::from_atto(-1), TokenAmount::zero(), &[], ExitCode::USR_INSUFFICIENT_FUNDS, @@ -257,7 +256,7 @@ mod miner_actor_precommit_batch { let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -280,7 +279,7 @@ mod miner_actor_precommit_batch { &mut rt, PreCommitSectorBatchParams { sectors }, &PreCommitBatchConfig { sector_weights: vec![], first_for_miner: true }, - BigInt::zero(), + &TokenAmount::zero(), ), ); rt.reset(); @@ -292,7 +291,7 @@ mod miner_actor_precommit_batch { let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); rt.set_received(TokenAmount::zero()); let precommit_epoch = period_offset + 1; @@ -315,7 +314,7 @@ mod miner_actor_precommit_batch { &mut rt, PreCommitSectorBatchParams { sectors }, &PreCommitBatchConfig { sector_weights: vec![], first_for_miner: true }, - BigInt::zero(), + &TokenAmount::zero(), ), ); rt.reset(); diff --git a/actors/miner/tests/miner_actor_test_wpost.rs b/actors/miner/tests/miner_actor_test_wpost.rs index 193d108ea..f6047a844 100644 --- a/actors/miner/tests/miner_actor_test_wpost.rs +++ b/actors/miner/tests/miner_actor_test_wpost.rs @@ -15,15 +15,13 @@ use fvm_shared::sector::RegisteredSealProof; mod util; +use num_traits::Zero; use util::*; // an expiration ~10 days greater than effective min expiration taking into account 30 days max // between pre and prove commit const DEFAULT_SECTOR_EXPIRATION: u64 = 220; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; -const BIG_REWARDS: u128 = 1_000_000_000_000_000_000_000u128; - #[test] fn basic_post_and_dispute() { let period_offset = ChainEpoch::from(100); @@ -34,7 +32,7 @@ fn basic_post_and_dispute() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -106,7 +104,7 @@ fn invalid_submissions() { let mut h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -502,7 +500,7 @@ fn duplicate_proof_rejected() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -584,7 +582,7 @@ fn duplicate_proof_rejected_with_many_partitions() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -689,14 +687,14 @@ fn successful_recoveries_recover_power() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let infos = h.commit_and_prove_sectors(&mut rt, 1, DEFAULT_SECTOR_EXPIRATION, vec![], true); let pwr = miner::power_for_sectors(h.sector_size, &infos); - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::from(0u8)); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); let initial_locked = h.get_locked_funds(&rt); // Submit first PoSt to ensure we are sufficiently early to add a fault @@ -715,7 +713,7 @@ fn successful_recoveries_recover_power() { let (dlidx, pidx) = state.find_sector(&rt.policy, &rt.store, infos[0].sector_number).unwrap(); let mut bf = BitField::new(); bf.set(infos[0].sector_number); - h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::from(0u8)).unwrap(); + h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::zero()).unwrap(); // advance to epoch when submitPoSt is due let mut dlinfo = h.deadline(&rt); @@ -761,13 +759,13 @@ fn skipped_faults_adjust_power() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let infos = h.commit_and_prove_sectors(&mut rt, 2, DEFAULT_SECTOR_EXPIRATION, vec![], true); - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::from(0u8)); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); // Skip to the due deadline. let state = h.get_state(&rt); @@ -848,13 +846,13 @@ fn skipping_all_sectors_in_a_partition_rejected() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let infos = h.commit_and_prove_sectors(&mut rt, 2, DEFAULT_SECTOR_EXPIRATION, vec![], true); - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::from(0u8)); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); // Skip to the due deadline. let state = h.get_state(&rt); @@ -883,7 +881,7 @@ fn skipping_all_sectors_in_a_partition_rejected() { rt.reset(); // These sectors are detected faulty and pay no penalty this time. - h.advance_deadline(&mut rt, CronConfig::with_continued_faults_penalty(TokenAmount::from(0u8))); + h.advance_deadline(&mut rt, CronConfig::with_continued_faults_penalty(TokenAmount::zero())); h.check_state(&rt); } @@ -897,13 +895,13 @@ fn skipped_recoveries_are_penalized_and_do_not_recover_power() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let infos = h.commit_and_prove_sectors(&mut rt, 2, DEFAULT_SECTOR_EXPIRATION, vec![], true); - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::from(0u8)); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); // Submit first PoSt to ensure we are sufficiently early to add a fault // advance to next proving period @@ -922,7 +920,7 @@ fn skipped_recoveries_are_penalized_and_do_not_recover_power() { let (dlidx, pidx) = state.find_sector(&rt.policy, &rt.store, infos[0].sector_number).unwrap(); let mut bf = BitField::new(); bf.set(infos[0].sector_number); - h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::from(0u8)).unwrap(); + h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::zero()).unwrap(); // Skip to the due deadline. let dlinfo = h.advance_to_deadline(&mut rt, dlidx); @@ -950,7 +948,7 @@ fn skipping_a_fault_from_the_wrong_partition_is_an_error() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1002,7 +1000,7 @@ fn cannot_dispute_posts_when_the_challenge_window_is_open() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1054,7 +1052,7 @@ fn can_dispute_up_till_window_end_but_not_after() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1116,7 +1114,7 @@ fn cant_dispute_up_with_an_invalid_deadline() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1143,7 +1141,7 @@ fn can_dispute_test_after_proving_period_changes() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1229,7 +1227,7 @@ fn bad_post_fails_when_verified() { let mut rt = h.new_runtime(); rt.epoch = precommit_epoch; - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -1237,7 +1235,7 @@ fn bad_post_fails_when_verified() { let power_for_sectors = &miner::power_for_sectors(h.sector_size, &vec![infos[0].clone(), infos[1].clone()]); - h.apply_rewards(&mut rt, TokenAmount::from(BIG_REWARDS), TokenAmount::from(0u8)); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); let state = h.get_state(&rt); let (dlidx, pidx) = state.find_sector(&rt.policy, &rt.store, infos[0].sector_number).unwrap(); @@ -1265,7 +1263,7 @@ fn bad_post_fails_when_verified() { let mut bf = BitField::new(); bf.set(infos[0].sector_number); bf.set(infos[1].sector_number); - h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::from(0u8)).unwrap(); + h.declare_recoveries(&mut rt, dlidx, pidx, bf, TokenAmount::zero()).unwrap(); // Now submit a PoSt, but a BAD one let dlinfo = h.advance_to_deadline(&mut rt, dlidx); diff --git a/actors/miner/tests/monies_test.rs b/actors/miner/tests/monies_test.rs index 96e1b5a6d..bb8f44720 100644 --- a/actors/miner/tests/monies_test.rs +++ b/actors/miner/tests/monies_test.rs @@ -1,14 +1,14 @@ use fil_actor_miner::{expected_reward_for_power, pledge_penalty_for_continued_fault}; use fvm_shared::bigint::{BigInt, Zero}; -use fvm_shared::{econ::TokenAmount, smooth::FilterEstimate}; +use fvm_shared::smooth::FilterEstimate; use std::ops::Neg; #[test] fn negative_br_clamp() { - let epoch_target_reward = TokenAmount::from(1_u64 << 50); - let qa_sector_power = TokenAmount::from(1_u64 << 36); - let network_qa_power = TokenAmount::from(1_u64 << 10); - let power_rate_of_change = TokenAmount::from(1_u64 << 10).neg(); + let epoch_target_reward = BigInt::from(1_u64 << 50); + let qa_sector_power = BigInt::from(1_u64 << 36); + let network_qa_power = BigInt::from(1_u64 << 10); + let power_rate_of_change = BigInt::from(1_u64 << 10).neg(); let reward_estimate = FilterEstimate::new(epoch_target_reward, BigInt::zero()); let power_estimate = FilterEstimate::new(network_qa_power.clone(), power_rate_of_change); assert!(power_estimate.extrapolate(4) < network_qa_power); @@ -19,10 +19,10 @@ fn negative_br_clamp() { #[test] fn zero_power_means_zero_fault_penalty() { - let epoch_target_reward = TokenAmount::from(1_u64 << 50); - let zero_qa_power = TokenAmount::zero(); - let network_qa_power = TokenAmount::from(1_u64 << 10); - let power_rate_of_change = TokenAmount::from(1_u64 << 10); + let epoch_target_reward = BigInt::from(1_u64 << 50); + let zero_qa_power = BigInt::zero(); + let network_qa_power = BigInt::from(1_u64 << 10); + let power_rate_of_change = BigInt::from(1_u64 << 10); let reward_estimate = FilterEstimate::new(epoch_target_reward, BigInt::zero()); let power_estimate = FilterEstimate::new(network_qa_power, power_rate_of_change); diff --git a/actors/miner/tests/pledge_penalty_for_termination.rs b/actors/miner/tests/pledge_penalty_for_termination.rs index 6a71f3174..2246e2057 100644 --- a/actors/miner/tests/pledge_penalty_for_termination.rs +++ b/actors/miner/tests/pledge_penalty_for_termination.rs @@ -10,7 +10,7 @@ use fvm_shared::sector::StoragePower; use fvm_shared::smooth::FilterEstimate; fn epoch_target_reward() -> TokenAmount { - TokenAmount::from(1_u128 << 50) + TokenAmount::from_atto(1_u128 << 50) } // 1 64 GiB sector @@ -25,7 +25,7 @@ fn network_qa_power() -> StoragePower { // exactly 1 attoFIL per byte of power, no estimated changes fn reward_estimate() -> FilterEstimate { - FilterEstimate::new(epoch_target_reward(), BigInt::zero()) + FilterEstimate::new(epoch_target_reward().atto().clone(), BigInt::zero()) } fn power_estimate() -> FilterEstimate { @@ -43,8 +43,8 @@ fn undeclared_penalty() -> TokenAmount { #[test] fn when_undeclared_fault_fee_exceeds_expected_reward_returns_undeclared_fault_fee() { // small pledge compared to current expected reward means - let initial_pledge = TokenAmount::from(1 << 10); - let day_reward = initial_pledge / INITIAL_PLEDGE_FACTOR; + let initial_pledge = TokenAmount::from_atto(1 << 10); + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let sector_age_in_days = 20; let sector_age = sector_age_in_days * EPOCHS_IN_DAY; @@ -67,7 +67,7 @@ fn when_undeclared_fault_fee_exceeds_expected_reward_returns_undeclared_fault_fe fn when_expected_reward_exceeds_undeclared_fault_fee_returns_expected_reward() { // initialPledge equal to undeclaredPenalty guarantees expected reward is greater let initial_pledge = undeclared_penalty(); - let day_reward = &initial_pledge / INITIAL_PLEDGE_FACTOR; + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let sector_age_in_days = 20; let sector_age = sector_age_in_days * EPOCHS_IN_DAY; @@ -85,15 +85,15 @@ fn when_expected_reward_exceeds_undeclared_fault_fee_returns_expected_reward() { // expect fee to be pledge + br * age * factor where br = pledge/initialPledgeFactor let expected_fee = &initial_pledge - + (&day_reward * sector_age_in_days * &*TERMINATION_REWARD_FACTOR_NUM) - / &*TERMINATION_REWARD_FACTOR_DENOM; + + (&day_reward * sector_age_in_days * TERMINATION_REWARD_FACTOR_NUM) + .div_floor(TERMINATION_REWARD_FACTOR_DENOM); assert_eq!(expected_fee, fee); } #[test] fn sector_age_is_capped() { let initial_pledge = undeclared_penalty(); - let day_reward = &initial_pledge / INITIAL_PLEDGE_FACTOR; + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let sector_age_in_days = 500; let sector_age = sector_age_in_days * EPOCHS_IN_DAY; @@ -111,8 +111,8 @@ fn sector_age_is_capped() { // expect fee to be pledge * br * age-cap * factor where br = pledge/initialPledgeFactor let expected_fee = &initial_pledge - + (&day_reward * TERMINATION_LIFETIME_CAP * &*TERMINATION_REWARD_FACTOR_NUM) - / &*TERMINATION_REWARD_FACTOR_DENOM; + + (&day_reward * TERMINATION_LIFETIME_CAP * TERMINATION_REWARD_FACTOR_NUM) + .div_floor(TERMINATION_REWARD_FACTOR_DENOM); assert_eq!(expected_fee, fee); } @@ -120,7 +120,7 @@ fn sector_age_is_capped() { fn fee_for_replacement_eq_fee_for_original_sector_when_power_br_are_unchanged() { // initialPledge equal to undeclaredPenalty guarantees expected reward is greater let initial_pledge = undeclared_penalty(); - let day_reward = &initial_pledge / INITIAL_PLEDGE_FACTOR; + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let sector_age = 20 * EPOCHS_IN_DAY; let replacement_age = 2 * EPOCHS_IN_DAY; @@ -159,7 +159,7 @@ fn fee_for_replacement_eq_fee_for_original_sector_when_power_br_are_unchanged() fn fee_for_replacement_eq_fee_for_same_sector_without_replacement_after_lifetime_cap() { // initialPledge equal to undeclaredPenalty guarantees expected reward is greater let initial_pledge = undeclared_penalty(); - let day_reward = &initial_pledge / INITIAL_PLEDGE_FACTOR; + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let sector_age = 20 * EPOCHS_IN_DAY; let replacement_age = (TERMINATION_LIFETIME_CAP + 1) * EPOCHS_IN_DAY; @@ -198,8 +198,8 @@ fn fee_for_replacement_eq_fee_for_same_sector_without_replacement_after_lifetime fn charges_for_replaced_sector_at_replaced_sector_day_rate() { // initialPledge equal to undeclaredPenalty guarantees expected reward is greater let initial_pledge = undeclared_penalty(); - let day_reward = &initial_pledge / INITIAL_PLEDGE_FACTOR; - let old_day_reward = 2 * &day_reward; + let day_reward = initial_pledge.div_floor(INITIAL_PLEDGE_FACTOR); + let old_day_reward = &day_reward * 2; let twenty_day_reward = &day_reward * INITIAL_PLEDGE_FACTOR; let old_sector_age_in_days = 20; let old_sector_age = old_sector_age_in_days * EPOCHS_IN_DAY; @@ -209,10 +209,10 @@ fn charges_for_replaced_sector_at_replaced_sector_day_rate() { // use low power, so termination fee exceeds SP let power = BigInt::from(1); - let old_penalty = (&old_day_reward * old_sector_age_in_days * &*TERMINATION_REWARD_FACTOR_NUM) - / &*TERMINATION_REWARD_FACTOR_DENOM; - let new_penalty = (&day_reward * replacement_age_in_days * &*TERMINATION_REWARD_FACTOR_NUM) - / &*TERMINATION_REWARD_FACTOR_DENOM; + let old_penalty = (&old_day_reward * old_sector_age_in_days * TERMINATION_REWARD_FACTOR_NUM) + .div_floor(TERMINATION_REWARD_FACTOR_DENOM); + let new_penalty = (&day_reward * replacement_age_in_days * TERMINATION_REWARD_FACTOR_NUM) + .div_floor(TERMINATION_REWARD_FACTOR_DENOM); let expected_fee = &twenty_day_reward + old_penalty + new_penalty; let fee = pledge_penalty_for_termination( diff --git a/actors/miner/tests/policy_test.rs b/actors/miner/tests/policy_test.rs index 5d3cdf964..a5b0ec6e3 100644 --- a/actors/miner/tests/policy_test.rs +++ b/actors/miner/tests/policy_test.rs @@ -6,13 +6,12 @@ use fil_actor_miner::{ use fil_actors_runtime::{EPOCHS_IN_DAY, SECONDS_IN_DAY}; use fvm_shared::bigint::{BigInt, Zero}; use fvm_shared::clock::ChainEpoch; -use fvm_shared::econ::TokenAmount; use fvm_shared::sector::SectorSize; #[test] fn quality_is_independent_of_size_and_duration() { // Quality of space with no deals. This doesn't depend on either the sector size or duration. - let empty_quality = TokenAmount::from(1 << SECTOR_QUALITY_PRECISION); + let empty_quality = BigInt::from(1 << SECTOR_QUALITY_PRECISION); // Quality space filled with non-verified deals. let deal_quality = &empty_quality * (DEAL_WEIGHT_MULTIPLIER.clone() / QUALITY_BASE_MULTIPLIER.clone()); @@ -56,7 +55,7 @@ fn quality_is_independent_of_size_and_duration() { #[test] fn quality_scales_with_verified_weight_proportion() { // Quality of space with no deals. This doesn't depend on either the sector size or duration. - let empty_quality = TokenAmount::from(1 << SECTOR_QUALITY_PRECISION); + let empty_quality = BigInt::from(1 << SECTOR_QUALITY_PRECISION); // Quality space filled with verified deals. let verified_quality = &empty_quality * (VERIFIED_DEAL_WEIGHT_MULTIPLIER.clone() / QUALITY_BASE_MULTIPLIER.clone()); diff --git a/actors/miner/tests/precommit_deposit_and_initial_pledge_positive_test.rs b/actors/miner/tests/precommit_deposit_and_initial_pledge_positive_test.rs index d7e269240..2e0f2f062 100644 --- a/actors/miner/tests/precommit_deposit_and_initial_pledge_positive_test.rs +++ b/actors/miner/tests/precommit_deposit_and_initial_pledge_positive_test.rs @@ -17,7 +17,11 @@ my_const!(qa_sector_power, StoragePower, StoragePower::from(1u64 << 36)); my_const!(network_qa_power, StoragePower, StoragePower::from(1u64 << 10)); my_const!(baseline_power, StoragePower, network_qa_power()); my_const!(power_rate_of_change, StoragePower, StoragePower::from(1u64 << 10)); -my_const!(reward_estimate, FilterEstimate, FilterEstimate::new(epoch_target_reward(), zero())); +my_const!( + reward_estimate, + FilterEstimate, + FilterEstimate::new(epoch_target_reward().atto().clone(), zero()) +); my_const!( power_estimate, FilterEstimate, @@ -35,12 +39,12 @@ fn initial_pledge_clamped_at_one_attofil() { &power_estimate(), &circulating_supply(), ); - assert_eq!(TokenAmount::from(1), initial_pledge); + assert_eq!(TokenAmount::from_atto(1), initial_pledge); } #[test] fn precommit_deposit_is_clamped_at_one_attofil() { let precommit_deposit = pre_commit_deposit_for_power(&reward_estimate(), &power_estimate(), &qa_sector_power()); - assert_eq!(TokenAmount::from(1), precommit_deposit); + assert_eq!(TokenAmount::from_atto(1), precommit_deposit); } diff --git a/actors/miner/tests/precommitted_sector_stores.rs b/actors/miner/tests/precommitted_sector_stores.rs index 16a66f1fe..b8d0fb4de 100644 --- a/actors/miner/tests/precommitted_sector_stores.rs +++ b/actors/miner/tests/precommitted_sector_stores.rs @@ -15,16 +15,20 @@ use state_harness::*; fn put_get_and_delete() { let mut h = StateHarness::new(0); - let pc1 = new_pre_commit_on_chain(1, make_sealed_cid("1".as_bytes()), TokenAmount::from(1), 1); + let pc1 = + new_pre_commit_on_chain(1, make_sealed_cid("1".as_bytes()), TokenAmount::from_atto(1), 1); h.put_precommitted_sectors(vec![pc1.clone()]).unwrap(); assert_eq!(pc1, h.get_precommit(1)); - let pc2 = new_pre_commit_on_chain(2, make_sealed_cid("2".as_bytes()), TokenAmount::from(1), 1); + let pc2 = + new_pre_commit_on_chain(2, make_sealed_cid("2".as_bytes()), TokenAmount::from_atto(1), 1); h.put_precommitted_sectors(vec![pc2.clone()]).unwrap(); assert_eq!(pc2, h.get_precommit(2)); - let pc3 = new_pre_commit_on_chain(3, make_sealed_cid("2".as_bytes()), TokenAmount::from(1), 1); - let pc4 = new_pre_commit_on_chain(4, make_sealed_cid("2".as_bytes()), TokenAmount::from(1), 1); + let pc3 = + new_pre_commit_on_chain(3, make_sealed_cid("2".as_bytes()), TokenAmount::from_atto(1), 1); + let pc4 = + new_pre_commit_on_chain(4, make_sealed_cid("2".as_bytes()), TokenAmount::from_atto(1), 1); h.put_precommitted_sectors(vec![pc3.clone(), pc4.clone()]).unwrap(); assert_eq!(pc3, h.get_precommit(3)); assert_eq!(pc4, h.get_precommit(4)); @@ -50,14 +54,16 @@ fn has_nonexistent_value_returns_false() { fn duplicate_put_rejected() { let mut h = StateHarness::new(0); - let pc1 = new_pre_commit_on_chain(1, make_sealed_cid("1".as_bytes()), TokenAmount::from(1), 1); + let pc1 = + new_pre_commit_on_chain(1, make_sealed_cid("1".as_bytes()), TokenAmount::from_atto(1), 1); // In sequence assert!(h.put_precommitted_sectors(vec![pc1.clone()]).is_ok()); assert!(h.put_precommitted_sectors(vec![pc1]).is_err()); // In batch - let pc2 = new_pre_commit_on_chain(2, make_sealed_cid("2".as_bytes()), TokenAmount::from(1), 1); + let pc2 = + new_pre_commit_on_chain(2, make_sealed_cid("2".as_bytes()), TokenAmount::from_atto(1), 1); assert!(h.put_precommitted_sectors(vec![pc2.clone(), pc2]).is_err()); } diff --git a/actors/miner/tests/prove_commit.rs b/actors/miner/tests/prove_commit.rs index ea5be8f5a..f0932af4a 100644 --- a/actors/miner/tests/prove_commit.rs +++ b/actors/miner/tests/prove_commit.rs @@ -23,14 +23,13 @@ const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; const VERIFIED_DEAL_WEIGHT_MULTIPLIER: u64 = 100; const QUALITY_BASE_MULTIPLIER: u64 = 10; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; const PERIOD_OFFSET: ChainEpoch = 100; #[test] fn prove_single_sector() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); let precommit_epoch = PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); @@ -88,7 +87,7 @@ fn prove_single_sector() { // run prove commit logic rt.set_epoch(prove_commit_epoch); - rt.balance.replace(TokenAmount::from(1000) * 1e18 as u64); + rt.balance.replace(TokenAmount::from_whole(1000)); let sector = h .prove_commit_sector_and_confirm( &mut rt, @@ -176,7 +175,7 @@ fn prove_single_sector() { fn prove_sectors_from_batch_pre_commit() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); let precommit_epoch = PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); @@ -255,7 +254,7 @@ fn prove_sectors_from_batch_pre_commit() { &mut rt, PreCommitSectorBatchParams { sectors }, &conf, - TokenAmount::zero(), + &TokenAmount::zero(), ); rt.set_epoch(prove_commit_epoch); @@ -355,7 +354,7 @@ fn prove_sectors_from_batch_pre_commit() { fn invalid_proof_rejected() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); let precommit_epoch = PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); @@ -433,7 +432,7 @@ fn invalid_proof_rejected() { ); rt.reset(); - rt.balance.replace(TokenAmount::from(1_000) * 1e18 as u64); + rt.balance.replace(TokenAmount::from_whole(1_000)); let prove_commit = h.make_prove_commit_params(sector_no); h.prove_commit_sector_and_confirm( @@ -472,14 +471,14 @@ fn invalid_proof_rejected() { fn prove_commit_aborts_if_pledge_requirement_not_met() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); // Set the circulating supply high and expected reward low in order to coerce // pledge requirements (BR + share of money supply, but capped at 1FIL) // to exceed pre-commit deposit (BR only). - rt.set_circulating_supply(TokenAmount::from(100_000_000) * 1e18 as u64); + rt.set_circulating_supply(TokenAmount::from_whole(100_000_000)); h.epoch_reward_smooth = FilterEstimate::new(BigInt::from(1e15 as u64), BigInt::zero()); // prove one sector to establish collateral and locked funds @@ -532,7 +531,7 @@ fn prove_commit_aborts_if_pledge_requirement_not_met() { fn drop_invalid_prove_commit_while_processing_valid_one() { let mut h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); @@ -572,7 +571,7 @@ fn drop_invalid_prove_commit_while_processing_valid_one() { fn prove_commit_just_after_period_start_permits_post() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); // Epoch PERIOD_OFFSET+1 should be at the beginning of the miner's proving period so there will be time to commit // and PoSt a sector. @@ -593,7 +592,7 @@ fn prove_commit_just_after_period_start_permits_post() { fn sector_with_non_positive_lifetime_is_skipped_in_confirmation() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); let precommit_epoch = PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); @@ -638,7 +637,7 @@ fn sector_with_non_positive_lifetime_is_skipped_in_confirmation() { fn verify_proof_does_not_vest_funds() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.balance.replace(TokenAmount::from(BIG_BALANCE)); + rt.balance.replace(BIG_BALANCE.clone()); let precommit_epoch = PERIOD_OFFSET + 1; rt.set_epoch(precommit_epoch); @@ -662,14 +661,14 @@ fn verify_proof_does_not_vest_funds() { .add_locked_funds( &rt.store, rt.epoch, - &TokenAmount::from(1000), + &TokenAmount::from_atto(1000), &VestSpec { initial_delay: 0, vest_period: 1, step_duration: 1, quantization: 1 }, ) .unwrap(); rt.replace_state(&st); rt.set_epoch(precommit_epoch + rt.policy.pre_commit_challenge_delay + 1); - rt.balance.replace(TokenAmount::from(1000) * 1e18 as u64); + rt.balance.replace(TokenAmount::from_whole(1000)); let mut prove_commit = h.make_prove_commit_params(sector_no); prove_commit.proof.resize(192, 0); diff --git a/actors/miner/tests/repay_debt_test.rs b/actors/miner/tests/repay_debt_test.rs index eae450d24..a1b60c791 100644 --- a/actors/miner/tests/repay_debt_test.rs +++ b/actors/miner/tests/repay_debt_test.rs @@ -8,8 +8,8 @@ use num_traits::Zero; fn repay_debt_in_priority_order() { let mut h = StateHarness::new(0); - let current_balance = TokenAmount::from(300u16); - let fee = TokenAmount::from(1000); + let current_balance = TokenAmount::from_atto(300u16); + let fee = TokenAmount::from_atto(1000); h.st.apply_penalty(&fee).unwrap(); assert_eq!(h.st.fee_debt, fee); @@ -23,7 +23,7 @@ fn repay_debt_in_priority_order() { assert_eq!(expected_debt, h.st.fee_debt); let current_balance = TokenAmount::zero(); - let fee = TokenAmount::from(2050); + let fee = TokenAmount::from_atto(2050); h.st.apply_penalty(&fee).unwrap(); h.st.repay_partial_debt_in_priority_order(&h.store, 33, ¤t_balance).unwrap(); diff --git a/actors/miner/tests/repay_debts.rs b/actors/miner/tests/repay_debts.rs index 9f5269300..d4dba48cd 100644 --- a/actors/miner/tests/repay_debts.rs +++ b/actors/miner/tests/repay_debts.rs @@ -6,7 +6,6 @@ use fvm_shared::econ::TokenAmount; mod util; use util::*; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; const PERIOD_OFFSET: ChainEpoch = 100; #[test] @@ -17,7 +16,7 @@ fn repay_with_no_available_funds_does_nothing() { // introduce fee debt let mut st = h.get_state(&rt); - let fee_debt: TokenAmount = 4 * TokenAmount::from(BIG_BALANCE); + let fee_debt: TokenAmount = 4 * &*BIG_BALANCE; st.fee_debt = fee_debt.clone(); rt.replace_state(&st); @@ -37,7 +36,7 @@ fn pay_debt_entirely_from_balance() { // introduce fee debt let mut st = h.get_state(&rt); - let fee_debt: TokenAmount = 4 * TokenAmount::from(BIG_BALANCE); + let fee_debt: TokenAmount = 4 * &*BIG_BALANCE; st.fee_debt = fee_debt.clone(); rt.replace_state(&st); @@ -57,15 +56,15 @@ fn partially_repay_debt() { // introduce fee debt let mut st = h.get_state(&rt); - let fee_debt: TokenAmount = 4 * TokenAmount::from(BIG_BALANCE); + let fee_debt: TokenAmount = 4 * &*BIG_BALANCE; st.fee_debt = fee_debt.clone(); rt.replace_state(&st); - let debt_to_repay = 3 * (&fee_debt / 4); + let debt_to_repay = 3 * (&fee_debt.div_floor(4)); h.repay_debts(&mut rt, &debt_to_repay, &TokenAmount::zero(), &debt_to_repay).unwrap(); let st = h.get_state(&rt); - assert_eq!(&fee_debt / 4, st.fee_debt); + assert_eq!(fee_debt.div_floor(4), st.fee_debt); h.check_state(&rt); } @@ -75,7 +74,7 @@ fn pay_debt_partially_from_vested_funds() { let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - let reward_amount: TokenAmount = 4 * TokenAmount::from(BIG_BALANCE); + let reward_amount: TokenAmount = 4 * &*BIG_BALANCE; let (amount_locked, _) = locked_reward_from_reward(reward_amount.clone()); rt.set_balance(amount_locked.clone()); h.apply_rewards(&mut rt, reward_amount, TokenAmount::zero()); @@ -83,15 +82,15 @@ fn pay_debt_partially_from_vested_funds() { // introduce fee debt let mut st = h.get_state(&rt); - st.fee_debt = 4 * TokenAmount::from(BIG_BALANCE); + st.fee_debt = 4 * &*BIG_BALANCE; rt.replace_state(&st); // send 1 FIL and repay all debt from vesting funds and balance h.repay_debts( &mut rt, - &TokenAmount::from(BIG_BALANCE), // send 1 FIL - &amount_locked, // 3 FIL comes from vesting funds - &TokenAmount::from(BIG_BALANCE), // 1 FIL sent from balance + &*BIG_BALANCE, // send 1 FIL + &amount_locked, // 3 FIL comes from vesting funds + &*BIG_BALANCE, // 1 FIL sent from balance ) .unwrap(); diff --git a/actors/miner/tests/report_consensus_fault.rs b/actors/miner/tests/report_consensus_fault.rs index 75f27e940..23df39e3f 100644 --- a/actors/miner/tests/report_consensus_fault.rs +++ b/actors/miner/tests/report_consensus_fault.rs @@ -4,20 +4,19 @@ use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::{ConsensusFault, ConsensusFaultType}; -use fvm_shared::econ::TokenAmount; + use fvm_shared::error::ExitCode; mod util; use util::*; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; const PERIOD_OFFSET: ChainEpoch = 100; fn setup() -> (ActorHarness, MockRuntime) { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); (h, rt) } diff --git a/actors/miner/tests/sector_assignment.rs b/actors/miner/tests/sector_assignment.rs index c012b42b1..ae9374123 100644 --- a/actors/miner/tests/sector_assignment.rs +++ b/actors/miner/tests/sector_assignment.rs @@ -5,8 +5,9 @@ use cid::Cid; use fil_actor_miner::{power_for_sectors, Deadline, PoStPartition, PowerPair, SectorOnChainInfo}; use fil_actors_runtime::{runtime::Policy, test_utils::make_sealed_cid}; use fvm_ipld_bitfield::{BitField, UnvalidatedBitField}; +use fvm_shared::bigint::BigInt; use fvm_shared::clock::ChainEpoch; -use fvm_shared::econ::TokenAmount; + use fvm_shared::sector::RegisteredSealProof; use fvm_shared::sector::SectorNumber; @@ -20,7 +21,7 @@ use state_harness::*; fn new_sector_on_chain_info( sector_number: SectorNumber, sealed_cid: Cid, - weight: TokenAmount, + weight: BigInt, activation: ChainEpoch, ) -> SectorOnChainInfo { SectorOnChainInfo { @@ -36,6 +37,7 @@ fn new_sector_on_chain_info( } mod sector_assignment { + use super::*; #[test] @@ -53,7 +55,7 @@ mod sector_assignment { new_sector_on_chain_info( i as SectorNumber, make_sealed_cid("{i}".as_bytes()), - TokenAmount::from(1u8), + BigInt::from(1u8), 0, ) }) diff --git a/actors/miner/tests/terminate_sectors_test.rs b/actors/miner/tests/terminate_sectors_test.rs index 2a37345e3..c751381e9 100644 --- a/actors/miner/tests/terminate_sectors_test.rs +++ b/actors/miner/tests/terminate_sectors_test.rs @@ -11,7 +11,7 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::{econ::TokenAmount, error::ExitCode}; mod util; -use fvm_shared::bigint::BigInt; + use num_traits::Zero; use util::*; @@ -23,7 +23,7 @@ fn setup() -> (ActorHarness, MockRuntime) { let h = ActorHarness::new(period_offset); let mut rt = h.new_runtime(); h.construct_and_verify(&mut rt); - rt.balance.replace(TokenAmount::from(big_balance)); + rt.balance.replace(TokenAmount::from_atto(big_balance)); rt.set_epoch(precommit_epoch); (h, rt) @@ -42,7 +42,7 @@ fn removes_sector_with_correct_accounting() { // A miner will pay the minimum of termination fee and locked funds. Add some locked funds to ensure // correct fee calculation is used. - h.apply_rewards(&mut rt, BIG_REWARDS.into(), BigInt::zero()); + h.apply_rewards(&mut rt, BIG_REWARDS.clone(), TokenAmount::zero()); let state: State = rt.get_state(); let initial_locked_funds = state.locked_funds; @@ -68,7 +68,7 @@ fn removes_sector_with_correct_accounting() { &h.epoch_qa_power_smooth, §or_power, &h.epoch_reward_smooth, - &BigInt::zero(), + &TokenAmount::zero(), 0, ); diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 1ef221164..ec64b417c 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -47,7 +47,6 @@ use fvm_ipld_encoding::de::Deserialize; use fvm_ipld_encoding::ser::Serialize; use fvm_ipld_encoding::{BytesDe, Cbor, CborStore, RawBytes}; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::BigIntSer; use fvm_shared::bigint::BigInt; use fvm_shared::clock::QuantSpec; use fvm_shared::clock::{ChainEpoch, NO_QUANTIZATION}; @@ -66,9 +65,9 @@ use fvm_shared::{HAMT_BIT_WIDTH, METHOD_SEND}; use cid::Cid; use itertools::Itertools; +use lazy_static::lazy_static; use multihash::derive::Multihash; use multihash::MultihashDigest; -use num_traits::sign::Signed; use fil_actor_miner::testing::{ check_deadline_state_invariants, check_state_invariants, DeadlineStateSummary, @@ -81,14 +80,18 @@ const RECEIVER_ID: u64 = 1000; pub type SectorsMap = BTreeMap; -// A reward amount for use in tests where the vesting amount wants to be large enough to cover penalties. -#[allow(dead_code)] -pub const BIG_REWARDS: u128 = 10u128.pow(24); - // an expriration ~10 days greater than effective min expiration taking into account 30 days max between pre and prove commit #[allow(dead_code)] pub const DEFAULT_SECTOR_EXPIRATION: u64 = 220; +lazy_static! { + pub static ref BIG_BALANCE: TokenAmount = TokenAmount::from_whole(1_000_000); + pub static ref ONE_PERCENT_BALANCE: TokenAmount = BIG_BALANCE.div_floor(100); + + // A reward amount for use in tests where the vesting amount wants to be large enough to cover penalties. + pub static ref BIG_REWARDS: TokenAmount = TokenAmount::from_whole(1_000); +} + #[allow(dead_code)] pub fn setup() -> (ActorHarness, MockRuntime) { let mut rt = MockRuntime::default(); @@ -130,7 +133,7 @@ impl ActorHarness { let control_addrs = vec![Address::new_id(999), Address::new_id(998), Address::new_id(997)]; let worker_key = new_bls_addr(0); let receiver = Address::new_id(RECEIVER_ID); - let rwd = TokenAmount::from(10_000_000_000_000_000_000i128); + let rwd = TokenAmount::from_whole(10); let pwr = StoragePower::from(1i128 << 50); let proof_type = RegisteredSealProof::StackedDRG32GiBV1P1; @@ -149,12 +152,12 @@ impl ActorHarness { period_offset: proving_period_offset, next_sector_no: 0, - network_pledge: rwd.clone() * TokenAmount::from(1000), + network_pledge: rwd.clone() * 1000, network_raw_power: pwr.clone(), network_qa_power: pwr.clone(), baseline_power: pwr.clone(), - epoch_reward_smooth: FilterEstimate::new(rwd, BigInt::from(0)), + epoch_reward_smooth: FilterEstimate::new(rwd.atto().clone(), BigInt::from(0)), epoch_qa_power_smooth: FilterEstimate::new(pwr, BigInt::from(0)), } } @@ -214,7 +217,7 @@ impl ActorHarness { self.worker, AccountMethod::PubkeyAddress as u64, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(self.worker_key).unwrap(), ExitCode::OK, ); @@ -450,7 +453,7 @@ impl ActorHarness { rt: &mut MockRuntime, params: PreCommitSectorBatchParams, conf: &PreCommitBatchConfig, - base_fee: TokenAmount, + base_fee: &TokenAmount, ) -> Result { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); rt.expect_validate_caller_addr(self.caller_addrs()); @@ -498,7 +501,7 @@ impl ActorHarness { *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::VerifyDealsForActivation as u64, RawBytes::serialize(vdparams).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(vdreturn).unwrap(), ExitCode::OK, ); @@ -506,9 +509,9 @@ impl ActorHarness { let state = self.get_state(rt); // burn networkFee - if state.fee_debt > TokenAmount::from(0u8) || params.sectors.len() > 1 { + if state.fee_debt > TokenAmount::zero() || params.sectors.len() > 1 { let expected_network_fee = - aggregate_pre_commit_network_fee(params.sectors.len() as i64, &base_fee); + aggregate_pre_commit_network_fee(params.sectors.len() as i64, base_fee); let expected_burn = expected_network_fee + state.fee_debt; rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, @@ -531,7 +534,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(cron_params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -549,7 +552,7 @@ impl ActorHarness { rt: &mut MockRuntime, params: PreCommitSectorBatchParams, conf: &PreCommitBatchConfig, - base_fee: TokenAmount, + base_fee: &TokenAmount, ) -> Vec { let result = self.pre_commit_sector_batch(rt, params.clone(), conf, base_fee).unwrap(); @@ -589,7 +592,7 @@ impl ActorHarness { *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::VerifyDealsForActivation as u64, RawBytes::serialize(vdparams).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(vdreturn).unwrap(), ExitCode::OK, ); @@ -597,7 +600,7 @@ impl ActorHarness { // in the original test the else branch does some redundant checks which we can omit. let state = self.get_state(rt); - if state.fee_debt > TokenAmount::from(0u8) { + if state.fee_debt > TokenAmount::zero() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -619,7 +622,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(cron_params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -676,7 +679,7 @@ impl ActorHarness { *REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(current_reward).unwrap(), ExitCode::OK, ); @@ -684,7 +687,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::CurrentTotalPower as u64, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(current_power).unwrap(), ExitCode::OK, ); @@ -724,7 +727,7 @@ impl ActorHarness { *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ComputeDataCommitment as u64, RawBytes::serialize(cdc_params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(cdc_ret).unwrap(), ExitCode::OK, ); @@ -758,7 +761,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::SubmitPoRepForBulkVerify as u64, RawBytes::serialize(seal).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -778,7 +781,7 @@ impl ActorHarness { config: ProveCommitConfig, precommits: Vec, params: ProveCommitAggregateParams, - base_fee: BigInt, + base_fee: &TokenAmount, ) -> Result<(), ActorError> { // recieve call to ComputeDataCommittments let mut comm_ds = Vec::new(); @@ -798,7 +801,7 @@ impl ActorHarness { *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ComputeDataCommitment as u64, RawBytes::serialize(cdc_params)?, - BigInt::zero(), + TokenAmount::zero(), RawBytes::serialize(cdc_ret)?, ExitCode::OK, ); @@ -849,8 +852,8 @@ impl ActorHarness { self.confirm_sector_proofs_valid_internal(rt, config, &precommits); // burn network fee - let expected_fee = aggregate_prove_commit_network_fee(precommits.len() as i64, &base_fee); - assert!(expected_fee > BigInt::zero()); + let expected_fee = aggregate_prove_commit_network_fee(precommits.len() as i64, base_fee); + assert!(expected_fee.is_positive()); rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -930,7 +933,7 @@ impl ActorHarness { *STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ActivateDeals as u64, RawBytes::serialize(params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), exit, ); @@ -940,7 +943,7 @@ impl ActorHarness { } if !valid_pcs.is_empty() { - let mut expected_pledge = TokenAmount::from(0u8); + let mut expected_pledge = TokenAmount::zero(); let mut expected_qa_power = BigInt::from(0); let mut expected_raw_power = BigInt::from(0); @@ -979,12 +982,12 @@ impl ActorHarness { } } - if expected_pledge != TokenAmount::from(0u8) { + if expected_pledge != TokenAmount::zero() { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&expected_pledge)).unwrap(), - TokenAmount::from(0u8), + RawBytes::serialize(&expected_pledge).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1056,20 +1059,20 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); } - let mut penalty_total = TokenAmount::from(0u8); - let mut pledge_delta = TokenAmount::from(0u8); + let mut penalty_total = TokenAmount::zero(); + let mut pledge_delta = TokenAmount::zero(); penalty_total += cfg.continued_faults_penalty.clone(); penalty_total += cfg.repaid_fee_debt.clone(); penalty_total += cfg.expired_precommit_penalty.clone(); - if penalty_total != TokenAmount::from(0u8) { + if penalty_total != TokenAmount::zero() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -1093,12 +1096,12 @@ impl ActorHarness { pledge_delta += cfg.expired_sectors_pledge_delta; pledge_delta -= immediately_vesting_funds(rt, &state); - if pledge_delta != TokenAmount::from(0u8) { + if pledge_delta != TokenAmount::zero() { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&pledge_delta)).unwrap(), - TokenAmount::from(0u8), + RawBytes::serialize(&pledge_delta).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1111,7 +1114,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1229,7 +1232,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1337,7 +1340,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1372,8 +1375,8 @@ impl ActorHarness { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&expected_pledge_delta)).unwrap(), - TokenAmount::from(0u8), + RawBytes::serialize(&expected_pledge_delta).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1441,13 +1444,13 @@ impl ActorHarness { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&pledge_delta)).unwrap(), - TokenAmount::from(0u8), + RawBytes::serialize(&pledge_delta).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - if penalty > TokenAmount::from(0u8) { + if penalty > TokenAmount::zero() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -1601,7 +1604,7 @@ impl ActorHarness { *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1627,7 +1630,7 @@ impl ActorHarness { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); rt.expect_validate_caller_addr(self.caller_addrs()); - if expected_debt_repaid > TokenAmount::from(0u8) { + if expected_debt_repaid > TokenAmount::zero() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, @@ -1745,11 +1748,11 @@ impl ActorHarness { *REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(current_reward).unwrap(), ExitCode::OK, ); - let this_epoch_reward = self.epoch_reward_smooth.estimate(); + let this_epoch_reward = TokenAmount::from_atto(self.epoch_reward_smooth.estimate()); let penalty_total = consensus_fault_penalty(this_epoch_reward.clone()); let reward_total = reward_for_consensus_slash_report(&this_epoch_reward); rt.expect_send( @@ -1838,7 +1841,7 @@ impl ActorHarness { self.expect_query_network_info(rt); - let mut pledge_delta = BigInt::zero(); + let mut pledge_delta = TokenAmount::zero(); if expected_fee.is_positive() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, @@ -1860,8 +1863,8 @@ impl ActorHarness { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, UPDATE_PLEDGE_TOTAL_METHOD, - RawBytes::serialize(BigIntSer(&pledge_delta)).unwrap(), - BigInt::zero(), + RawBytes::serialize(&pledge_delta).unwrap(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1956,7 +1959,7 @@ impl ActorHarness { rt.expect_send( *STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, - RawBytes::serialize(BigIntSer(&pledge_delta)).unwrap(), + RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, @@ -2300,11 +2303,11 @@ impl CronConfig { expected_enrollment: 0, detected_faults_power_delta: None, expired_sectors_power_delta: None, - expired_sectors_pledge_delta: TokenAmount::from(0u8), - continued_faults_penalty: TokenAmount::from(0u8), - expired_precommit_penalty: TokenAmount::from(0u8), - repaid_fee_debt: TokenAmount::from(0u8), - penalty_from_unlocked: TokenAmount::from(0u8), + expired_sectors_pledge_delta: TokenAmount::zero(), + continued_faults_penalty: TokenAmount::zero(), + expired_precommit_penalty: TokenAmount::zero(), + repaid_fee_debt: TokenAmount::zero(), + penalty_from_unlocked: TokenAmount::zero(), } } @@ -2381,7 +2384,7 @@ enum MhCode { fn immediately_vesting_funds(rt: &MockRuntime, state: &State) -> TokenAmount { let vesting = rt.store.get_cbor::(&state.vesting_funds).unwrap().unwrap(); - let mut sum = TokenAmount::from(0u8); + let mut sum = TokenAmount::zero(); for vf in vesting.funds { if vf.epoch < rt.epoch { sum += vf.amount; @@ -2511,7 +2514,7 @@ pub fn test_sector( sector_number, deal_weight: DealWeight::from(deal_weight), verified_deal_weight: DealWeight::from(verified_deal_weight), - initial_pledge: TokenAmount::from(pledge), + initial_pledge: TokenAmount::from_atto(pledge), sealed_cid: make_sealed_cid(format!("commR-{sector_number}").as_bytes()), ..Default::default() } diff --git a/actors/miner/tests/vesting_add_locked_funds.rs b/actors/miner/tests/vesting_add_locked_funds.rs index 463fdcaf2..b585772e5 100644 --- a/actors/miner/tests/vesting_add_locked_funds.rs +++ b/actors/miner/tests/vesting_add_locked_funds.rs @@ -11,7 +11,7 @@ fn locked_funds_increases_with_sequential_calls() { let vspec = VestSpec { initial_delay: 0, vest_period: 1, step_duration: 1, quantization: 1 }; let vest_start = 10; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); assert_eq!(vest_sum, h.st.locked_funds); @@ -26,7 +26,7 @@ fn vests_when_quantize_step_duration_and_vesting_period_are_coprime() { let vspec = VestSpec { initial_delay: 0, vest_period: 27, step_duration: 5, quantization: 7 }; let vest_start = 10; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); assert_eq!(vest_sum, h.st.locked_funds); @@ -35,19 +35,19 @@ fn vests_when_quantize_step_duration_and_vesting_period_are_coprime() { let amount_vested = h.unlock_vested_funds(e).unwrap(); match e { 22 => { - assert_eq!(TokenAmount::from(40), amount_vested); + assert_eq!(TokenAmount::from_atto(40), amount_vested); } 29 => { - assert_eq!(TokenAmount::from(26), amount_vested); + assert_eq!(TokenAmount::from_atto(26), amount_vested); } 36 => { - assert_eq!(TokenAmount::from(26), amount_vested); + assert_eq!(TokenAmount::from_atto(26), amount_vested); } 43 => { - assert_eq!(TokenAmount::from(8), amount_vested); + assert_eq!(TokenAmount::from_atto(8), amount_vested); } _ => { - assert_eq!(TokenAmount::from(0), amount_vested); + assert_eq!(TokenAmount::zero(), amount_vested); } } total_vested += amount_vested; diff --git a/actors/miner/tests/vesting_add_locked_funds_table.rs b/actors/miner/tests/vesting_add_locked_funds_table.rs index b66c7eae6..e4b1c26de 100644 --- a/actors/miner/tests/vesting_add_locked_funds_table.rs +++ b/actors/miner/tests/vesting_add_locked_funds_table.rs @@ -1,5 +1,4 @@ use fil_actor_miner::VestSpec; -use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -150,18 +149,18 @@ fn test_vesting() { let vest_start = tc.period_start + vest_start_delay; - h.st.add_locked_funds(&h.store, vest_start, &TokenAmount::from(vest_sum), &tc.vspec) + h.st.add_locked_funds(&h.store, vest_start, &TokenAmount::from_atto(vest_sum), &tc.vspec) .unwrap(); - assert_eq!(TokenAmount::from(vest_sum), h.st.locked_funds); + assert_eq!(TokenAmount::from_atto(vest_sum), h.st.locked_funds); let mut total_vested = 0; for (e, &v) in tc.vepocs.iter().enumerate() { assert_eq!( - TokenAmount::from(v), + TokenAmount::from_atto(v), h.st.unlock_vested_funds(&h.store, vest_start + e as ChainEpoch).unwrap() ); total_vested += v; - assert_eq!(TokenAmount::from(vest_sum - total_vested), h.st.locked_funds); + assert_eq!(TokenAmount::from_atto(vest_sum - total_vested), h.st.locked_funds); } assert_eq!(vest_sum, total_vested); diff --git a/actors/miner/tests/vesting_unvested_funds.rs b/actors/miner/tests/vesting_unvested_funds.rs index 0bb51ab64..6cc1cae64 100644 --- a/actors/miner/tests/vesting_unvested_funds.rs +++ b/actors/miner/tests/vesting_unvested_funds.rs @@ -13,27 +13,27 @@ fn unlock_unvested_funds_leaving_bucket_with_non_zero_tokens() { let vspec = VestSpec { initial_delay: 0, vest_period: 5, step_duration: 1, quantization: 1 }; let vest_start = 100; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); - let amount_unlocked = h.unlock_unvested_funds(vest_start, &TokenAmount::from(39)).unwrap(); - assert_eq!(TokenAmount::from(39), amount_unlocked); + let amount_unlocked = h.unlock_unvested_funds(vest_start, &TokenAmount::from_atto(39)).unwrap(); + assert_eq!(TokenAmount::from_atto(39), amount_unlocked); // no vested funds available to unlock until strictly after first vesting epoch - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start).unwrap()); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 1).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 1).unwrap()); // expected to be zero due to unlocking of UNvested funds - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 2).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 2).unwrap()); // expected to be partially unlocked already du to unlocking of UNvested funds - assert_eq!(TokenAmount::from(1), h.unlock_vested_funds(vest_start + 3).unwrap()); + assert_eq!(TokenAmount::from_atto(1), h.unlock_vested_funds(vest_start + 3).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 4).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 5).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 6).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 4).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 5).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 6).unwrap()); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 7).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 7).unwrap()); assert!(h.st.locked_funds.is_zero()); assert!(h.vesting_funds_store_empty()) @@ -45,25 +45,25 @@ fn unlock_unvested_funds_leaving_bucket_with_zero_tokens() { let vspec = VestSpec { initial_delay: 0, vest_period: 5, step_duration: 1, quantization: 1 }; let vest_start = 100; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); - let amount_unlocked = h.unlock_unvested_funds(vest_start, &TokenAmount::from(40)).unwrap(); - assert_eq!(TokenAmount::from(40), amount_unlocked); + let amount_unlocked = h.unlock_unvested_funds(vest_start, &TokenAmount::from_atto(40)).unwrap(); + assert_eq!(TokenAmount::from_atto(40), amount_unlocked); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start).unwrap()); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 1).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 1).unwrap()); // expected to be zero due to unlocking of UNvested funds - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 2).unwrap()); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 3).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 2).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 3).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 4).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 5).unwrap()); - assert_eq!(TokenAmount::from(20), h.unlock_vested_funds(vest_start + 6).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 4).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 5).unwrap()); + assert_eq!(TokenAmount::from_atto(20), h.unlock_vested_funds(vest_start + 6).unwrap()); - assert_eq!(TokenAmount::from(0), h.unlock_vested_funds(vest_start + 7).unwrap()); + assert_eq!(TokenAmount::zero(), h.unlock_vested_funds(vest_start + 7).unwrap()); assert!(h.st.locked_funds.is_zero()); assert!(h.vesting_funds_store_empty()) @@ -75,7 +75,7 @@ fn unlock_all_unvested_funds() { let vspec = VestSpec { initial_delay: 0, vest_period: 5, step_duration: 1, quantization: 1 }; let vest_start = 10; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); let unvested_funds = h.unlock_unvested_funds(vest_start, &vest_sum).unwrap(); @@ -91,9 +91,9 @@ fn unlock_unvested_funds_value_greater_than_locked_funds() { let vspec = VestSpec { initial_delay: 0, vest_period: 5, step_duration: 1, quantization: 1 }; let vest_start = 10; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); - let unvested_funds = h.unlock_unvested_funds(vest_start, &TokenAmount::from(200)).unwrap(); + let unvested_funds = h.unlock_unvested_funds(vest_start, &TokenAmount::from_atto(200)).unwrap(); assert_eq!(vest_sum, unvested_funds); assert!(h.st.locked_funds.is_zero()); @@ -106,14 +106,14 @@ fn unlock_unvested_funds_when_there_are_vested_funds_in_the_table() { let vspec = VestSpec { initial_delay: 0, vest_period: 50, step_duration: 1, quantization: 1 }; let vest_start = 10; - let vest_sum = TokenAmount::from(100); + let vest_sum = TokenAmount::from_atto(100); // will lock funds from epochs 11 to 60 h.add_locked_funds(vest_start, &vest_sum, &vspec).unwrap(); // unlock funds from epochs 30 to 60 let new_epoch = 30; - let target = TokenAmount::from(60); + let target = TokenAmount::from_atto(60); let remaining = &vest_sum - ⌖ let unvested_funds = h.unlock_unvested_funds(new_epoch, &target).unwrap(); assert_eq!(target, unvested_funds); diff --git a/actors/miner/tests/withdraw_balance.rs b/actors/miner/tests/withdraw_balance.rs index 224e2b108..1ee426b65 100644 --- a/actors/miner/tests/withdraw_balance.rs +++ b/actors/miner/tests/withdraw_balance.rs @@ -3,29 +3,21 @@ use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use std::ops::Deref; mod util; use util::*; -const BIG_BALANCE: u128 = 1_000_000_000_000_000_000_000_000u128; -const ONE_PERCENT_BALANCE: u128 = BIG_BALANCE / 100; const PERIOD_OFFSET: ChainEpoch = 100; #[test] fn happy_path_withdraws_funds() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); - h.withdraw_funds( - &mut rt, - &TokenAmount::from(ONE_PERCENT_BALANCE), - &TokenAmount::from(ONE_PERCENT_BALANCE), - &TokenAmount::zero(), - ) - .unwrap(); + h.withdraw_funds(&mut rt, &ONE_PERCENT_BALANCE, &ONE_PERCENT_BALANCE, &TokenAmount::zero()) + .unwrap(); h.check_state(&rt); } @@ -33,19 +25,19 @@ fn happy_path_withdraws_funds() { fn fails_if_miner_cant_repay_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let mut st = h.get_state(&rt); - st.fee_debt = rt.balance.borrow().deref() + TokenAmount::from(1e18 as u64); + st.fee_debt = &*rt.balance.borrow() + TokenAmount::from_whole(1); rt.replace_state(&st); expect_abort_contains_message( ExitCode::USR_INSUFFICIENT_FUNDS, "unlocked balance can not repay fee debt", h.withdraw_funds( &mut rt, - &TokenAmount::from(ONE_PERCENT_BALANCE), - &TokenAmount::from(ONE_PERCENT_BALANCE), + &*ONE_PERCENT_BALANCE, + &*ONE_PERCENT_BALANCE, &TokenAmount::zero(), ), ); @@ -56,11 +48,11 @@ fn fails_if_miner_cant_repay_fee_debt() { fn withdraw_only_what_we_can_after_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); let mut rt = h.new_runtime(); - rt.set_balance(TokenAmount::from(BIG_BALANCE)); + rt.set_balance(BIG_BALANCE.clone()); h.construct_and_verify(&mut rt); let mut st = h.get_state(&rt); - let fee_debt = TokenAmount::from(BIG_BALANCE - ONE_PERCENT_BALANCE); + let fee_debt = &*BIG_BALANCE - &*ONE_PERCENT_BALANCE; st.fee_debt = fee_debt.clone(); rt.replace_state(&st); diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 2a32d5e3e..e53d0f796 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" @@ -29,5 +29,6 @@ fvm_ipld_encoding = "0.2.2" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } +lazy_static = "1.4.0" [features] fil-actor = [] diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 726db39e7..b9749ebe0 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -12,12 +12,12 @@ use fil_actors_runtime::{ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::Sign; + use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, Signed}; +use num_traits::{FromPrimitive, Zero}; pub use self::state::*; pub use self::types::*; @@ -110,7 +110,7 @@ impl Actor { signers: resolved_signers, num_approvals_threshold: params.num_approvals_threshold, pending_txs: empty_root, - initial_balance: TokenAmount::from(0), + initial_balance: TokenAmount::zero(), next_tx_id: Default::default(), start_epoch: Default::default(), unlock_duration: Default::default(), @@ -137,7 +137,7 @@ impl Actor { rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; let proposer: Address = rt.message().caller(); - if params.value.sign() == Sign::Minus { + if params.value.is_negative() { return Err(actor_error!( illegal_argument, "proposed value must be non-negative, was {}", diff --git a/actors/multisig/src/state.rs b/actors/multisig/src/state.rs index 4e9360aa8..67d387503 100644 --- a/actors/multisig/src/state.rs +++ b/actors/multisig/src/state.rs @@ -7,7 +7,8 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; use fvm_shared::address::Address; -use fvm_shared::bigint::{bigint_ser, Integer}; +use fvm_shared::bigint::BigInt; +use fvm_shared::bigint::Integer; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use indexmap::IndexMap; @@ -25,7 +26,6 @@ pub struct State { pub next_tx_id: TxnID, // Linear unlock - #[serde(with = "bigint_ser")] pub initial_balance: TokenAmount, pub start_epoch: ChainEpoch, pub unlock_duration: ChainEpoch, @@ -54,7 +54,7 @@ impl State { /// Returns amount locked in multisig contract pub fn amount_locked(&self, elapsed_epoch: ChainEpoch) -> TokenAmount { if elapsed_epoch >= self.unlock_duration { - return TokenAmount::from(0); + return TokenAmount::zero(); } if elapsed_epoch <= 0 { return self.initial_balance.clone(); @@ -64,9 +64,9 @@ impl State { // locked = ceil(InitialBalance * remainingLockDuration / UnlockDuration) let numerator: TokenAmount = &self.initial_balance * remaining_lock_duration; - let denominator = TokenAmount::from(self.unlock_duration); + let denominator = BigInt::from(self.unlock_duration); - numerator.div_ceil(&denominator) + TokenAmount::from_atto(numerator.atto().div_ceil(&denominator)) } /// Iterates all pending transactions and removes an address from each list of approvals, @@ -111,7 +111,7 @@ impl State { amount_to_spend: &TokenAmount, curr_epoch: ChainEpoch, ) -> anyhow::Result<()> { - if amount_to_spend < &0.into() { + if amount_to_spend.is_negative() { return Err(anyhow!("amount to spend {} less than zero", amount_to_spend)); } if &balance < amount_to_spend { diff --git a/actors/multisig/src/testing.rs b/actors/multisig/src/testing.rs index 3f0359fea..58dc328dc 100644 --- a/actors/multisig/src/testing.rs +++ b/actors/multisig/src/testing.rs @@ -5,7 +5,6 @@ use fil_actors_runtime::{Map, MessageAccumulator}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::address::Address; use integer_encoding::VarInt; -use num_traits::Zero; use crate::{State, Transaction, TxnID, SIGNERS_MAX}; diff --git a/actors/multisig/src/types.rs b/actors/multisig/src/types.rs index b4ba99669..51ed78992 100644 --- a/actors/multisig/src/types.rs +++ b/actors/multisig/src/types.rs @@ -7,7 +7,7 @@ use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{serde_bytes, Cbor, RawBytes}; use fvm_ipld_hamt::BytesKey; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; + use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -40,7 +40,6 @@ impl Display for TxnID { #[derive(Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct Transaction { pub to: Address, - #[serde(with = "bigint_ser")] pub value: TokenAmount, pub method: MethodNum, pub params: RawBytes, @@ -58,7 +57,6 @@ pub struct Transaction { pub struct ProposalHashData<'a> { pub requester: Option<&'a Address>, pub to: &'a Address, - #[serde(with = "bigint_ser")] pub value: &'a TokenAmount, pub method: &'a MethodNum, pub params: &'a RawBytes, @@ -78,7 +76,6 @@ pub struct ConstructorParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ProposeParams { pub to: Address, - #[serde(with = "bigint_ser")] pub value: TokenAmount, pub method: MethodNum, pub params: RawBytes, @@ -164,6 +161,5 @@ pub struct ChangeNumApprovalsThresholdParams { pub struct LockBalanceParams { pub start_epoch: ChainEpoch, pub unlock_duration: ChainEpoch, - #[serde(with = "bigint_ser")] pub amount: TokenAmount, } diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 645c81824..9a6d00c57 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -10,7 +10,7 @@ use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; -use fvm_shared::bigint::bigint_ser; + use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -54,7 +54,7 @@ mod constructor_tests { start_epoch: 100, }; - rt.set_received(TokenAmount::from(100u8)); + rt.set_received(TokenAmount::from_atto(100u8)); rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); let ret = rt.call::( @@ -67,7 +67,7 @@ mod constructor_tests { let st: State = rt.get_state(); assert_eq!(params.signers, st.signers); assert_eq!(params.num_approvals_threshold, st.num_approvals_threshold); - assert_eq!(TokenAmount::from(100u8), st.initial_balance); + assert_eq!(TokenAmount::from_atto(100u8), st.initial_balance); assert_eq!(200, st.unlock_duration); assert_eq!(100, st.start_epoch); h.assert_transactions(&rt, vec![]); @@ -284,6 +284,7 @@ mod constructor_tests { #[cfg(test)] mod vesting_tests { use super::*; + use lazy_static::lazy_static; const MSIG: Address = Address::new_id(1000); const ANNE: Address = Address::new_id(101); @@ -293,15 +294,18 @@ mod vesting_tests { const UNLOCK_DURATION: ChainEpoch = 10; const START_EPOCH: ChainEpoch = 0; - const MSIG_INITIAL_BALANCE: u8 = 100; + + lazy_static! { + static ref MSIG_INITIAL_BALANCE: TokenAmount = TokenAmount::from_atto(100); + } #[test] fn happy_path_full_vesting() { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - rt.set_balance(TokenAmount::from(MSIG_INITIAL_BALANCE)); - rt.set_received(TokenAmount::from(MSIG_INITIAL_BALANCE)); + rt.set_balance(MSIG_INITIAL_BALANCE.clone()); + rt.set_received(MSIG_INITIAL_BALANCE.clone()); h.construct_and_verify(&mut rt, 2, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); rt.set_received(TokenAmount::zero()); @@ -310,7 +314,7 @@ mod vesting_tests { let proposal_hash = h.propose_ok( &mut rt, DARLENE, - TokenAmount::from(MSIG_INITIAL_BALANCE), + MSIG_INITIAL_BALANCE.clone(), METHOD_SEND, RawBytes::default(), ); @@ -326,7 +330,7 @@ mod vesting_tests { DARLENE, METHOD_SEND, RawBytes::default(), - TokenAmount::from(MSIG_INITIAL_BALANCE), + MSIG_INITIAL_BALANCE.clone(), RawBytes::default(), ExitCode::OK, ); @@ -341,8 +345,8 @@ mod vesting_tests { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - rt.set_balance(TokenAmount::from(MSIG_INITIAL_BALANCE)); - rt.set_received(TokenAmount::from(MSIG_INITIAL_BALANCE)); + rt.set_balance(MSIG_INITIAL_BALANCE.clone()); + rt.set_received(MSIG_INITIAL_BALANCE.clone()); h.construct_and_verify(&mut rt, 2, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); rt.set_received(TokenAmount::zero()); @@ -350,7 +354,7 @@ mod vesting_tests { let proposal_hash = h.propose_ok( &mut rt, DARLENE, - TokenAmount::from(MSIG_INITIAL_BALANCE / 2), + MSIG_INITIAL_BALANCE.div_floor(2), METHOD_SEND, RawBytes::default(), ); @@ -360,7 +364,7 @@ mod vesting_tests { DARLENE, METHOD_SEND, RawBytes::default(), - TokenAmount::from(MSIG_INITIAL_BALANCE / 2), + MSIG_INITIAL_BALANCE.div_floor(2), RawBytes::default(), ExitCode::OK, ); @@ -374,8 +378,8 @@ mod vesting_tests { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - rt.set_balance(TokenAmount::from(MSIG_INITIAL_BALANCE)); - rt.set_received(TokenAmount::from(MSIG_INITIAL_BALANCE)); + rt.set_balance(MSIG_INITIAL_BALANCE.clone()); + rt.set_received(MSIG_INITIAL_BALANCE.clone()); h.construct_and_verify(&mut rt, 1, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); rt.set_received(TokenAmount::zero()); @@ -385,14 +389,14 @@ mod vesting_tests { h.propose( &mut rt, DARLENE, - TokenAmount::from(MSIG_INITIAL_BALANCE), + MSIG_INITIAL_BALANCE.clone(), METHOD_SEND, RawBytes::default(), ), ); rt.reset(); rt.set_epoch(START_EPOCH + UNLOCK_DURATION / 10); - let amount_out = TokenAmount::from(MSIG_INITIAL_BALANCE / 10); + let amount_out = MSIG_INITIAL_BALANCE.div_floor(10); rt.expect_send( DARLENE, METHOD_SEND, @@ -411,8 +415,8 @@ mod vesting_tests { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - rt.set_balance(TokenAmount::from(MSIG_INITIAL_BALANCE)); - rt.set_received(TokenAmount::from(MSIG_INITIAL_BALANCE)); + rt.set_balance(MSIG_INITIAL_BALANCE.clone()); + rt.set_received(MSIG_INITIAL_BALANCE.clone()); h.construct_and_verify(&mut rt, 2, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); rt.set_received(TokenAmount::zero()); @@ -420,7 +424,7 @@ mod vesting_tests { let proposal_hash = h.propose_ok( &mut rt, DARLENE, - TokenAmount::from(MSIG_INITIAL_BALANCE / 2), + MSIG_INITIAL_BALANCE.div_floor(2), METHOD_SEND, RawBytes::default(), ); @@ -435,8 +439,8 @@ mod vesting_tests { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - let locked_balance = TokenAmount::from(UNLOCK_DURATION - 1); // balance < duration - let one = TokenAmount::from(1u8); + let locked_balance = TokenAmount::from_atto(UNLOCK_DURATION - 1); // balance < duration + let one = TokenAmount::from_atto(1u8); rt.set_balance(locked_balance.clone()); rt.set_received(locked_balance.clone()); h.construct_and_verify(&mut rt, 1, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); @@ -510,8 +514,8 @@ mod vesting_tests { let mut rt = construct_runtime(MSIG); let h = util::ActorHarness::new(); - rt.set_balance(TokenAmount::from(MSIG_INITIAL_BALANCE)); - rt.set_received(TokenAmount::from(MSIG_INITIAL_BALANCE)); + rt.set_balance(MSIG_INITIAL_BALANCE.clone()); + rt.set_received(MSIG_INITIAL_BALANCE.clone()); h.construct_and_verify(&mut rt, 2, UNLOCK_DURATION, START_EPOCH, vec![ANNE, BOB, CHARLIE]); rt.set_received(TokenAmount::zero()); @@ -534,11 +538,12 @@ mod vesting_tests { h.construct_and_verify(&mut rt, 1, 0, START_EPOCH, vec![ANNE]); rt.set_caller(*MULTISIG_ACTOR_CODE_ID, MSIG); - rt.set_balance(TokenAmount::from(10u8)); - rt.set_received(TokenAmount::from(10u8)); + rt.set_balance(TokenAmount::from_atto(10u8)); + rt.set_received(TokenAmount::from_atto(10u8)); // lock up funds the actor doesn't have yet - h.lock_balance(&mut rt, START_EPOCH, UNLOCK_DURATION, TokenAmount::from(10u8)).unwrap(); + h.lock_balance(&mut rt, START_EPOCH, UNLOCK_DURATION, TokenAmount::from_atto(10u8)) + .unwrap(); // make a tx that transfers no value let send_amount = TokenAmount::zero(); @@ -554,7 +559,7 @@ mod vesting_tests { h.propose_ok(&mut rt, BOB, send_amount, METHOD_SEND, RawBytes::default()); // verify that sending any value is prevented - let send_amount = TokenAmount::from(1u8); + let send_amount = TokenAmount::from_atto(1u8); expect_abort( ExitCode::USR_INSUFFICIENT_FUNDS, h.propose(&mut rt, BOB, send_amount, METHOD_SEND, RawBytes::default()), @@ -578,7 +583,7 @@ fn test_simple_propose() { let start_epoch = 0; let signers = vec![anne, bob]; - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); h.propose_ok(&mut rt, chuck, send_value.clone(), METHOD_SEND, RawBytes::default()); @@ -605,12 +610,12 @@ fn test_propose_with_threshold_met() { let bob = Address::new_id(102); let chuck = Address::new_id(103); let fake_params = RawBytes::from([99u8; 3].to_vec()); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let no_unlock_duration = 0; let start_epoch = 0; let signers = vec![anne, bob]; - rt.set_balance(TokenAmount::from(10u8)); + rt.set_balance(TokenAmount::from_atto(10u8)); rt.set_received(TokenAmount::zero()); h.construct_and_verify(&mut rt, num_approvals, no_unlock_duration, start_epoch, signers); @@ -639,12 +644,12 @@ fn test_propose_with_threshold_and_non_empty_return_value() { let bob = Address::new_id(102); let chuck = Address::new_id(103); let fake_params = RawBytes::from([99u8; 3].to_vec()); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let no_unlock_duration = 0; let start_epoch = 0; let signers = vec![anne, bob]; - rt.set_balance(TokenAmount::from(20u8)); + rt.set_balance(TokenAmount::from_atto(20u8)); rt.set_received(TokenAmount::zero()); h.construct_and_verify(&mut rt, num_approvals, no_unlock_duration, start_epoch, signers); @@ -652,14 +657,13 @@ fn test_propose_with_threshold_and_non_empty_return_value() { struct FakeReturn { addr1: Address, addr2: Address, - #[serde(with = "bigint_ser")] tokens: TokenAmount, } let propose_ret = FakeReturn { addr1: Address::new_id(1), addr2: Address::new_id(2), - tokens: TokenAmount::from(77u8), + tokens: TokenAmount::from_atto(77u8), }; let inner_ret_bytes = serialize(&propose_ret, "fake proposal return value").unwrap(); let fake_method = 42u64; @@ -695,7 +699,7 @@ fn test_fail_propose_with_threshold_met_and_insufficient_balance() { let bob = Address::new_id(102); let chuck = Address::new_id(103); let fake_params = RawBytes::from([99u8; 3].to_vec()); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let no_unlock_duration = 0; let start_epoch = 0; let signers = vec![anne, bob]; @@ -725,7 +729,7 @@ fn test_fail_propose_from_non_signer() { let bob = Address::new_id(102); let chuck = Address::new_id(103); let fake_params = RawBytes::from([99u8; 3].to_vec()); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let no_unlock_duration = 0; let start_epoch = 0; let signers = vec![anne, bob]; @@ -1305,7 +1309,7 @@ mod approval_tests { let fake_params = RawBytes::from(vec![1, 2, 3, 4]); let fake_method = 42; let fake_ret = RawBytes::from(vec![4, 3, 2, 1]); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); let proposal_hash = h.propose_ok(&mut rt, chuck, send_value.clone(), fake_method, fake_params.clone()); @@ -1337,7 +1341,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1366,7 +1370,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(20u8); + let send_value = TokenAmount::from_atto(20u8); let unlock_duration = 20; let start_epoch = 10; let h = util::ActorHarness::new(); @@ -1415,9 +1419,9 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); - rt.set_balance(send_value.clone() - 1); + rt.set_balance(send_value.clone() - TokenAmount::from_atto(1)); rt.set_received(TokenAmount::zero()); h.construct_and_verify(&mut rt, 2, 0, 0, signers); @@ -1452,7 +1456,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(20u8); + let send_value = TokenAmount::from_atto(20u8); let unlock_duration = 20; let start_epoch = 10; let h = util::ActorHarness::new(); @@ -1492,7 +1496,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1526,7 +1530,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1562,7 +1566,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1601,7 +1605,7 @@ mod approval_tests { let bob = Address::new_id(102); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value); rt.set_received(TokenAmount::zero()); @@ -1624,7 +1628,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1664,7 +1668,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1704,7 +1708,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob, chuck]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1749,7 +1753,7 @@ mod approval_tests { let chuck = Address::new_id(103); let signers = vec![anne, bob]; let mut rt = construct_runtime(msig); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let h = util::ActorHarness::new(); rt.set_balance(send_value.clone()); rt.set_received(TokenAmount::zero()); @@ -1808,7 +1812,7 @@ mod cancel_tests { let fake_params = RawBytes::from(vec![1, 2, 3, 4]); let fake_method = 42; - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); // anne proposes tx rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); let proposal_hash = h.propose_ok(&mut rt, chuck, send_value, fake_method, fake_params); @@ -1828,7 +1832,7 @@ mod cancel_tests { let anne = Address::new_id(101); let bob = Address::new_id(102); let chuck = Address::new_id(103); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let mut rt = construct_runtime(msig); let h = util::ActorHarness::new(); @@ -1856,7 +1860,7 @@ mod cancel_tests { let anne = Address::new_id(101); let bob = Address::new_id(102); let chuck = Address::new_id(103); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let mut rt = construct_runtime(msig); let h = util::ActorHarness::new(); @@ -1898,7 +1902,7 @@ mod cancel_tests { let anne = Address::new_id(101); let bob = Address::new_id(102); let chuck = Address::new_id(103); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let mut rt = construct_runtime(msig); let h = util::ActorHarness::new(); @@ -1939,7 +1943,7 @@ mod cancel_tests { let anne = Address::new_id(101); let bob = Address::new_id(102); let chuck = Address::new_id(103); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let mut rt = construct_runtime(msig); let h = util::ActorHarness::new(); @@ -1981,7 +1985,7 @@ mod cancel_tests { let anne = Address::new_id(101); let bob = Address::new_id(102); let chuck = Address::new_id(103); - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); let num_approvers = 3; let mut rt = construct_runtime(msig); @@ -2120,7 +2124,7 @@ mod change_threshold_tests { // anne proposes tx id 0 let fake_method = 42; - let send_value = TokenAmount::from(10u8); + let send_value = TokenAmount::from_atto(10u8); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); let proposal_hash = h.propose_ok(&mut rt, chuck, send_value.clone(), fake_method, RawBytes::default()); @@ -2166,13 +2170,13 @@ mod lock_balance_tests { // some time later, initialize vesting rt.set_epoch(200); let vest_start = 0; - let lock_amount = TokenAmount::from(100_000u32); + let lock_amount = TokenAmount::from_atto(100_000u32); let vest_duration = 1000; rt.set_caller(*MULTISIG_ACTOR_CODE_ID, msig); h.lock_balance(&mut rt, vest_start, vest_duration, lock_amount.clone()).unwrap(); rt.set_epoch(300); - let vested = TokenAmount::from(30_000); + let vested = TokenAmount::from_atto(30_000); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); // Fail to spend balance the multisig doesn't have @@ -2189,7 +2193,7 @@ mod lock_balance_tests { h.propose( &mut rt, bob, - vested.clone() + TokenAmount::from(1), + vested.clone() + TokenAmount::from_atto(1), METHOD_SEND, RawBytes::default(), ), @@ -2212,13 +2216,13 @@ mod lock_balance_tests { rt.set_balance(lock_amount - vested); expect_abort( ExitCode::USR_INSUFFICIENT_FUNDS, - h.propose(&mut rt, bob, TokenAmount::from(1), METHOD_SEND, RawBytes::default()), + h.propose(&mut rt, bob, TokenAmount::from_atto(1), METHOD_SEND, RawBytes::default()), ); rt.reset(); // later can spend the rest rt.set_epoch(vest_start + vest_duration); - let rested = TokenAmount::from(70_000u32); + let rested = TokenAmount::from_atto(70_000u32); rt.expect_send( bob, METHOD_SEND, @@ -2246,7 +2250,7 @@ mod lock_balance_tests { // some time later initialize vesting rt.set_epoch(200); let vest_start = 1000; - let lock_amount = TokenAmount::from(100_000); + let lock_amount = TokenAmount::from_atto(100_000); let vest_duration = 1000; rt.set_caller(*MULTISIG_ACTOR_CODE_ID, msig); h.lock_balance(&mut rt, vest_start, vest_duration, lock_amount.clone()).unwrap(); @@ -2254,28 +2258,28 @@ mod lock_balance_tests { // oversupply the wallet allow spending the oversupply rt.set_epoch(300); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, anne); - rt.set_balance(lock_amount.clone() + TokenAmount::from(1)); + rt.set_balance(lock_amount.clone() + TokenAmount::from_atto(1)); rt.expect_send( bob, METHOD_SEND, RawBytes::default(), - TokenAmount::from(1), + TokenAmount::from_atto(1), RawBytes::default(), ExitCode::OK, ); - h.propose_ok(&mut rt, bob, TokenAmount::from(1), METHOD_SEND, RawBytes::default()); + h.propose_ok(&mut rt, bob, TokenAmount::from_atto(1), METHOD_SEND, RawBytes::default()); // fail to spend locked funds before vesting starts rt.set_balance(lock_amount.clone()); expect_abort( ExitCode::USR_INSUFFICIENT_FUNDS, - h.propose(&mut rt, bob, TokenAmount::from(1), METHOD_SEND, RawBytes::default()), + h.propose(&mut rt, bob, TokenAmount::from_atto(1), METHOD_SEND, RawBytes::default()), ); rt.reset(); // can spend partially vested amount rt.set_epoch(vest_start + 200); - let expect_vested = TokenAmount::from(20_000); + let expect_vested = TokenAmount::from_atto(20_000); rt.expect_send( bob, METHOD_SEND, @@ -2290,12 +2294,12 @@ mod lock_balance_tests { rt.set_balance(lock_amount - expect_vested); expect_abort( ExitCode::USR_INSUFFICIENT_FUNDS, - h.propose(&mut rt, bob, TokenAmount::from(1), METHOD_SEND, RawBytes::default()), + h.propose(&mut rt, bob, TokenAmount::from_atto(1), METHOD_SEND, RawBytes::default()), ); // later, can spend the rest rt.set_epoch(vest_start + vest_duration); - let rested = TokenAmount::from(80_000); + let rested = TokenAmount::from_atto(80_000); rt.expect_send( bob, METHOD_SEND, @@ -2322,7 +2326,7 @@ mod lock_balance_tests { // initialize vesting from zero let vest_start = 0; - let lock_amount = TokenAmount::from(100_000); + let lock_amount = TokenAmount::from_atto(100_000); let vest_duration = 1000; rt.set_caller(*MULTISIG_ACTOR_CODE_ID, msig); h.lock_balance(&mut rt, vest_start, vest_duration, lock_amount.clone()).unwrap(); @@ -2342,7 +2346,12 @@ mod lock_balance_tests { // can't change locked amount expect_abort( ExitCode::USR_FORBIDDEN, - h.lock_balance(&mut rt, vest_start, vest_duration, lock_amount - TokenAmount::from(1)), + h.lock_balance( + &mut rt, + vest_start, + vest_duration, + lock_amount - TokenAmount::from_atto(1), + ), ); rt.reset(); check_state(&rt); @@ -2381,7 +2390,7 @@ mod lock_balance_tests { h.construct_and_verify(&mut rt, 1, 0, 0, vec![anne]); let vest_start = 0_i64; - let lock_amount = TokenAmount::from(100_000u32); + let lock_amount = TokenAmount::from_atto(100_000u32); let vest_duration = 1000_i64; // Disallow negative duration but allow negative start epoch @@ -2394,7 +2403,7 @@ mod lock_balance_tests { // Disallow negative amount expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.lock_balance(&mut rt, vest_start, vest_duration, TokenAmount::from(-1i32)), + h.lock_balance(&mut rt, vest_start, vest_duration, TokenAmount::from_atto(-1i32)), ); check_state(&rt); } diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index db9cbcfb3..31092f923 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 7ccde472f..cd16267fd 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -9,12 +9,12 @@ use fil_actors_runtime::{ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::{BigInt, Sign}; + use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use num_derive::FromPrimitive; -use num_traits::FromPrimitive; +use num_traits::{FromPrimitive, Zero}; pub use self::state::{LaneState, Merge, State}; pub use self::types::*; @@ -163,7 +163,7 @@ impl Actor { return Err(actor_error!(illegal_argument; "this voucher has expired")); } - if sv.amount.sign() == Sign::Minus { + if sv.amount.is_negative() { return Err(actor_error!(illegal_argument; "voucher amount must be non-negative, was {}", sv.amount)); } @@ -176,7 +176,7 @@ impl Actor { } if let Some(extra) = &sv.extra { - rt.send(&extra.actor, extra.method, extra.data.clone(), TokenAmount::from(0u8)) + rt.send(&extra.actor, extra.method, extra.data.clone(), TokenAmount::zero()) .map_err(|e| e.wrap("spend voucher verification failed"))?; } @@ -203,7 +203,7 @@ impl Actor { // The next section actually calculates the payment amounts to update // the payment channel state // 1. (optional) sum already redeemed value of all merging lanes - let mut redeemed_from_others = BigInt::default(); + let mut redeemed_from_others = TokenAmount::zero(); for merge in sv.merges { if merge.lane == sv.lane { return Err(actor_error!(illegal_argument; @@ -242,7 +242,7 @@ impl Actor { // 4. check operation validity let new_send_balance = balance_delta + &st.to_send; - if new_send_balance < TokenAmount::from(0) { + if new_send_balance < TokenAmount::zero() { return Err(actor_error!(illegal_argument; "voucher would leave channel balance negative")); } diff --git a/actors/paych/src/state.rs b/actors/paych/src/state.rs index aeeb10dda..c67a02d26 100644 --- a/actors/paych/src/state.rs +++ b/actors/paych/src/state.rs @@ -5,7 +5,7 @@ use cid::Cid; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; use fvm_shared::address::Address; -use fvm_shared::bigint::{bigint_ser, BigInt}; + use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -19,7 +19,6 @@ pub struct State { /// Recipient of payouts from channel. pub to: Address, /// Amount successfully redeemed through the payment channel, paid out on `Collect`. - #[serde(with = "bigint_ser")] pub to_send: TokenAmount, /// Height at which the channel can be collected. pub settling_at: ChainEpoch, @@ -46,8 +45,7 @@ impl State { /// as well as the amount it has already redeemed. #[derive(Default, Clone, PartialEq, Eq, Debug, Serialize_tuple, Deserialize_tuple)] pub struct LaneState { - #[serde(with = "bigint_ser")] - pub redeemed: BigInt, + pub redeemed: TokenAmount, pub nonce: u64, } diff --git a/actors/paych/src/testing.rs b/actors/paych/src/testing.rs index b3b760084..79e3b965d 100644 --- a/actors/paych/src/testing.rs +++ b/actors/paych/src/testing.rs @@ -3,7 +3,7 @@ use fil_actors_runtime::MessageAccumulator; use fvm_ipld_amt::Amt; use fvm_ipld_blockstore::Blockstore; use fvm_shared::{address::Protocol, econ::TokenAmount}; -use num_traits::{Signed, Zero}; +use num_traits::Zero; use crate::{LaneState, State}; diff --git a/actors/paych/src/types.rs b/actors/paych/src/types.rs index d5c79ecb7..f09d9e47e 100644 --- a/actors/paych/src/types.rs +++ b/actors/paych/src/types.rs @@ -5,9 +5,9 @@ use fil_actors_runtime::network::EPOCHS_IN_HOUR; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{serde_bytes, to_vec, Error, RawBytes}; use fvm_shared::address::Address; -use fvm_shared::bigint::{bigint_ser, BigInt}; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; +use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; use super::Merge; @@ -49,8 +49,7 @@ pub struct SignedVoucher { /// Set by `from` to prevent redemption of stale vouchers on a lane pub nonce: u64, /// Amount voucher can be redeemed for - #[serde(with = "bigint_ser")] - pub amount: BigInt, + pub amount: TokenAmount, /// (optional) Can extend channel min_settle_height if needed pub min_settle_height: ChainEpoch, @@ -74,8 +73,7 @@ impl SignedVoucher { pub extra: &'a Option, pub lane: u64, pub nonce: u64, - #[serde(with = "bigint_ser")] - pub amount: &'a BigInt, + pub amount: &'a TokenAmount, pub min_settle_height: ChainEpoch, pub merges: &'a [Merge], pub signature: (), diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index ba7694736..769c6ec8b 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -18,12 +18,12 @@ use fil_actors_runtime::INIT_ACTOR_ADDR; use fvm_ipld_amt::Amt; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::BigInt; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::METHOD_CONSTRUCTOR; +use num_traits::Zero; const PAYCH_ID: u64 = 100; const PAYER_ID: u64 = 102; @@ -33,7 +33,7 @@ struct LaneParams { epoch_num: ChainEpoch, from: Address, to: Address, - amt: BigInt, + amt: TokenAmount, lane: u64, nonce: u64, } @@ -69,6 +69,7 @@ fn check_state(rt: &MockRuntime) { mod paych_constructor { use fvm_shared::METHOD_CONSTRUCTOR; use fvm_shared::METHOD_SEND; + use num_traits::Zero; use super::*; @@ -218,7 +219,7 @@ mod paych_constructor { non_id_addr, METHOD_SEND, Default::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), Default::default(), ExitCode::OK, ); @@ -255,7 +256,7 @@ mod paych_constructor { non_id_addr, METHOD_SEND, Default::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), Default::default(), ExitCode::OK, ); @@ -324,7 +325,7 @@ mod create_lane_tests { let paych_addr = Address::new_id(PAYCH_ADDR); let payer_addr = Address::new_id(PAYER_ADDR); let payee_addr = Address::new_id(PAYEE_ADDR); - let paych_balance = TokenAmount::from(PAYCH_BALANCE); + let paych_balance = TokenAmount::from_atto(PAYCH_BALANCE); let paych_non_id = Address::new_bls(&[201; fvm_shared::address::BLS_PUB_LEN]).unwrap(); let sig = Option::Some(Signature::new_bls("doesn't matter".as_bytes().to_vec())); @@ -428,7 +429,7 @@ mod create_lane_tests { secret_pre_image: test_case.secret_preimage.clone(), lane: test_case.lane, nonce: test_case.nonce, - amount: BigInt::from(test_case.amt), + amount: TokenAmount::from_atto(test_case.amt), signature: test_case.sig.clone(), channel_addr: test_case.payment_channel, extra: Default::default(), @@ -492,7 +493,7 @@ mod update_channel_state_redeem { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, payee_addr); rt.expect_validate_caller_addr(vec![state.from, state.to]); - sv.amount = BigInt::from(9); + sv.amount = TokenAmount::from_atto(9); let payer_addr = Address::new_id(PAYER_ID); @@ -510,11 +511,11 @@ mod update_channel_state_redeem { ); rt.verify(); - let exp_ls = LaneState { redeemed: BigInt::from(9), nonce: 2 }; + let exp_ls = LaneState { redeemed: TokenAmount::from_atto(9), nonce: 2 }; let exp_state = PState { from: state.from, to: state.to, - to_send: TokenAmount::from(9), + to_send: TokenAmount::from_atto(9), settling_at: state.settling_at, min_settle_height: state.min_settle_height, lane_states: construct_lane_state_amt(&rt, vec![exp_ls]), @@ -532,7 +533,7 @@ mod update_channel_state_redeem { rt.expect_validate_caller_addr(vec![state.from, state.to]); let initial_amount = state.to_send; - sv.amount = BigInt::from(9); + sv.amount = TokenAmount::from_atto(9); sv.lane = 1; let ls_to_update: LaneState = get_lane_state(&rt, &state.lane_states, sv.lane); @@ -574,7 +575,7 @@ mod update_channel_state_redeem { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, payee_addr); rt.expect_validate_caller_addr(vec![state.from, state.to]); - sv.amount = BigInt::from(9); + sv.amount = TokenAmount::from_atto(9); sv.nonce = 1; let payer_addr = Address::new_id(PAYER_ID); @@ -711,7 +712,7 @@ mod merge_tests { let num_lanes = 2; let (mut rt, mut sv, state) = construct_runtime(num_lanes); - rt.set_balance(TokenAmount::from(tc.balance as u64)); + rt.set_balance(TokenAmount::from_atto(tc.balance)); sv.lane = 0; sv.nonce = tc.voucher; @@ -741,7 +742,7 @@ mod merge_tests { sv.lane = MAX_LANE + 1; sv.nonce += 1; - sv.amount = BigInt::from(100); + sv.amount = TokenAmount::from_atto(100); failure_end(&mut rt, sv, ExitCode::USR_ILLEGAL_ARGUMENT); } } @@ -775,7 +776,7 @@ mod update_channel_state_extra { other_addr, Method::UpdateChannelState as u64, fake_params, - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), exit_code, ); @@ -1124,8 +1125,8 @@ fn require_create_channel_with_lanes(num_lanes: u64) -> (MockRuntime, SignedVouc let paych_addr = Address::new_id(100); let payer_addr = Address::new_id(PAYER_ID); let payee_addr = Address::new_id(PAYEE_ID); - let balance = TokenAmount::from(100_000); - let received = TokenAmount::from(0); + let balance = TokenAmount::from_atto(100_000); + let received = TokenAmount::zero(); let curr_epoch = 2; let mut actor_code_cids = HashMap::default(); @@ -1151,7 +1152,7 @@ fn require_create_channel_with_lanes(num_lanes: u64) -> (MockRuntime, SignedVouc epoch_num: curr_epoch, from: payer_addr, to: payee_addr, - amt: (BigInt::from(i) + 1), + amt: (TokenAmount::from_atto(i + 1)), lane: i as u64, nonce: i + 1, }; diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index cecf9506d..ced852ff9 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 91a9f4127..b421e67b3 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -15,7 +15,7 @@ use fil_actors_runtime::{ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::{BigIntDe, BigIntSer}; +use fvm_shared::bigint::bigint_ser::BigIntSer; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::reward::ThisEpochRewardReturn; @@ -23,7 +23,7 @@ use fvm_shared::sector::SealVerifyInfo; use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; use log::{debug, error}; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, Signed, Zero}; +use num_traits::{FromPrimitive, Zero}; pub use self::policy::*; pub use self::state::*; @@ -282,7 +282,7 @@ impl Actor { &REWARD_ACTOR_ADDR, ext::reward::UPDATE_NETWORK_KPI, this_epoch_raw_byte_power?, - TokenAmount::from(0_u32), + TokenAmount::zero(), ) .map_err(|e| e.wrap("failed to update network KPI with reward actor"))?; @@ -686,7 +686,7 @@ impl ActorCode for Actor { Ok(RawBytes::default()) } Some(Method::UpdatePledgeTotal) => { - let BigIntDe(param) = cbor::deserialize_params(params)?; + let param: TokenAmount = cbor::deserialize_params(params)?; Self::update_pledge_total(rt, param)?; Ok(RawBytes::default()) } diff --git a/actors/power/src/state.rs b/actors/power/src/state.rs index 1203dfacc..3f5e8503d 100644 --- a/actors/power/src/state.rs +++ b/actors/power/src/state.rs @@ -46,14 +46,12 @@ pub struct State { pub total_quality_adj_power: StoragePower, #[serde(with = "bigint_ser")] pub total_qa_bytes_committed: StoragePower, - #[serde(with = "bigint_ser")] pub total_pledge_collateral: TokenAmount, #[serde(with = "bigint_ser")] pub this_epoch_raw_byte_power: StoragePower, #[serde(with = "bigint_ser")] pub this_epoch_quality_adj_power: StoragePower, - #[serde(with = "bigint_ser")] pub this_epoch_pledge_collateral: TokenAmount, pub this_epoch_qa_power_smoothed: FilterEstimate, diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 99c0e8890..521be8f83 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -62,7 +62,6 @@ pub struct CurrentTotalPowerReturn { pub raw_byte_power: StoragePower, #[serde(with = "bigint_ser")] pub quality_adj_power: StoragePower, - #[serde(with = "bigint_ser")] pub pledge_collateral: TokenAmount, pub quality_adj_power_smoothed: FilterEstimate, } diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 51bd66486..24c1b7024 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -22,6 +22,7 @@ use fvm_ipld_hamt::Error; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntDe; use fvm_shared::bigint::bigint_ser::BigIntSer; +use fvm_shared::bigint::BigInt; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -71,13 +72,13 @@ pub fn new_runtime() -> MockRuntime { } pub fn new_harness() -> Harness { - let rwd = TokenAmount::from(10) * TokenAmount::from(10_i128.pow(18)); + let rwd = TokenAmount::from_whole(10); Harness { miner_seq: 0, seal_proof: RegisteredSealProof::StackedDRG32GiBV1P1, window_post_proof: RegisteredPoStProof::StackedDRGWindow32GiBV1, this_epoch_baseline_power: StoragePower::from(1i64 << 50), - this_epoch_reward_smoothed: FilterEstimate::new(rwd, TokenAmount::zero()), + this_epoch_reward_smoothed: FilterEstimate::new(rwd.atto().clone(), BigInt::zero()), } } @@ -196,7 +197,7 @@ impl Harness { peer, vec![], self.window_post_proof, - &TokenAmount::from(0), + &TokenAmount::zero(), ) } @@ -289,7 +290,7 @@ impl Harness { rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); rt.call::( Method::UpdatePledgeTotal as MethodNum, - &RawBytes::serialize(BigIntDe(delta.clone())).unwrap(), + &RawBytes::serialize(delta).unwrap(), ) .unwrap(); rt.verify(); @@ -380,7 +381,7 @@ impl Harness { *REWARD_ACTOR_ADDR, ThisEpochReward as u64, RawBytes::default(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::serialize(current_reward).unwrap(), ExitCode::OK, ); diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index f6ecb037d..9b754a064 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -48,7 +48,7 @@ fn create_miner() { peer, multiaddrs, RegisteredPoStProof::StackedDRGWindow32GiBV1, - &TokenAmount::from(10), + &TokenAmount::from_atto(10), ) .unwrap(); @@ -119,8 +119,8 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { // owner send CreateMiner to Actor rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *OWNER); - rt.value_received = TokenAmount::from(10); - rt.set_balance(TokenAmount::from(10)); + rt.value_received = TokenAmount::from_atto(10); + rt.set_balance(TokenAmount::from_atto(10)); rt.expect_validate_caller_type(CALLER_TYPES_SIGNABLE.to_vec()); let message_params = ExecParams { @@ -140,7 +140,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { *INIT_ACTOR_ADDR, EXEC_METHOD, RawBytes::serialize(message_params).unwrap(), - TokenAmount::from(10), + TokenAmount::from_atto(10), RawBytes::default(), ExitCode::SYS_INSUFFICIENT_FUNDS, ); @@ -234,9 +234,9 @@ fn power_and_pledge_accounted_below_threshold() { // Add power and pledge for miner2 h.update_claimed_power(&mut rt, MINER2, small_power_unit, small_power_unit); - h.update_pledge_total(&mut rt, MINER1, &TokenAmount::from(1_000_000)); + h.update_pledge_total(&mut rt, MINER1, &TokenAmount::from_atto(1_000_000)); h.expect_total_power_eager(&mut rt, small_power_unit_x2, small_power_unit_x3); - h.expect_total_pledge_eager(&mut rt, &TokenAmount::from(1_000_000)); + h.expect_total_pledge_eager(&mut rt, &TokenAmount::from_atto(1_000_000)); rt.verify(); @@ -251,9 +251,9 @@ fn power_and_pledge_accounted_below_threshold() { // Subtract power and some pledge for miner2 h.update_claimed_power(&mut rt, MINER2, &small_power_unit.neg(), &small_power_unit.neg()); - h.update_pledge_total(&mut rt, MINER2, &TokenAmount::from(100_000).neg()); + h.update_pledge_total(&mut rt, MINER2, &TokenAmount::from_atto(100_000).neg()); h.expect_total_power_eager(&mut rt, small_power_unit, small_power_unit_x2); - h.expect_total_pledge_eager(&mut rt, &TokenAmount::from(900_000)); + h.expect_total_pledge_eager(&mut rt, &TokenAmount::from_atto(900_000)); let claim2 = h.get_claim(&rt, &MINER2).unwrap(); assert!(claim2.raw_byte_power.is_zero()); @@ -604,7 +604,7 @@ fn given_no_miner_claim_update_pledge_total_should_abort() { "unknown miner", rt.call::( Method::UpdatePledgeTotal as u64, - &RawBytes::serialize(BigIntSer(&TokenAmount::from(1_000_000))).unwrap(), + &RawBytes::serialize(&TokenAmount::from_atto(1_000_000)).unwrap(), ), ); @@ -679,7 +679,7 @@ mod cron_tests { let expected_power: BigInt = power_unit * 4u8; - let delta = TokenAmount::from(1u8); + let delta = TokenAmount::from_atto(1u8); h.update_pledge_total(&mut rt, miner1, &delta); h.on_epoch_tick_end(&mut rt, 0, &expected_power, Vec::new(), Vec::new()); @@ -751,7 +751,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -779,7 +779,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -818,7 +818,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -895,7 +895,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -972,7 +972,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -999,7 +999,7 @@ mod cron_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1233,7 +1233,7 @@ mod cron_batch_proof_verifies_tests { cs.miner, CONFIRM_SECTOR_PROOFS_VALID_METHOD, RawBytes::serialize(params).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1245,7 +1245,7 @@ mod cron_batch_proof_verifies_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), - TokenAmount::from(0u8), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -1281,7 +1281,7 @@ mod cron_batch_proof_verifies_tests { *REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), - BigInt::zero(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index c90863c67..9db5a2e01 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/reward/src/ext.rs b/actors/reward/src/ext.rs index f3bb1073a..c9cad048f 100644 --- a/actors/reward/src/ext.rs +++ b/actors/reward/src/ext.rs @@ -1,5 +1,4 @@ use fvm_ipld_encoding::tuple::*; -use fvm_shared::bigint::bigint_ser; use fvm_shared::econ::TokenAmount; pub mod miner { @@ -9,9 +8,7 @@ pub mod miner { #[derive(Debug, Serialize_tuple, Deserialize_tuple)] pub struct ApplyRewardParams { - #[serde(with = "bigint_ser")] pub reward: TokenAmount, - #[serde(with = "bigint_ser")] pub penalty: TokenAmount, } } diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index 5852f87eb..21db9bdf6 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -10,13 +10,12 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntDe; -use fvm_shared::bigint::{Integer, Sign}; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::StoragePower; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use log::{error, warn}; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, Signed}; +use num_traits::FromPrimitive; pub use self::logic::*; pub use self::state::{Reward, State, VestingFunction}; @@ -92,10 +91,10 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; let prior_balance = rt.current_balance(); - if params.penalty.sign() == Sign::Minus { + if params.penalty.is_negative() { return Err(actor_error!(illegal_argument, "negative penalty {}", params.penalty)); } - if params.gas_reward.sign() == Sign::Minus { + if params.gas_reward.is_negative() { return Err(actor_error!( illegal_argument, "negative gas reward {}", @@ -121,8 +120,8 @@ impl Actor { let penalty: TokenAmount = ¶ms.penalty * PENALTY_MULTIPLIER; let total_reward = rt.transaction(|st: &mut State, rt| { - let mut block_reward: TokenAmount = (&st.this_epoch_reward * params.win_count) - .div_floor(&TokenAmount::from(EXPECTED_LEADERS_PER_EPOCH)); + let mut block_reward: TokenAmount = + (&st.this_epoch_reward * params.win_count).div_rem(EXPECTED_LEADERS_PER_EPOCH).0; let mut total_reward = ¶ms.gas_reward + &block_reward; let curr_balance = rt.current_balance(); if total_reward > curr_balance { diff --git a/actors/reward/src/logic.rs b/actors/reward/src/logic.rs index ad5b5d1cb..f1ed89d26 100644 --- a/actors/reward/src/logic.rs +++ b/actors/reward/src/logic.rs @@ -8,7 +8,6 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::math::PRECISION; use fvm_shared::sector::StoragePower; -use fvm_shared::FILECOIN_PRECISION; use lazy_static::lazy_static; use super::expneg::expneg; @@ -29,9 +28,9 @@ lazy_static! { ((BASELINE_INITIAL_VALUE.clone() << (2*PRECISION)) / &*BASELINE_EXPONENT) >> PRECISION; /// 330M for mainnet - pub(super) static ref SIMPLE_TOTAL: BigInt = BigInt::from(330_000_000) * FILECOIN_PRECISION; + pub(super) static ref SIMPLE_TOTAL: TokenAmount = TokenAmount::from_whole(330_000_000); /// 770M for mainnet - pub(super) static ref BASELINE_TOTAL: BigInt = BigInt::from(770_000_000) * FILECOIN_PRECISION; + pub(super) static ref BASELINE_TOTAL: TokenAmount = TokenAmount::from_whole(770_000_000); /// expLamSubOne = e^lambda - 1 /// for Q.128: int(expLamSubOne * 2^128) static ref EXP_LAM_SUB_ONE: BigInt = BigInt::from(37396273494747879394193016954629u128); @@ -76,19 +75,19 @@ pub(crate) fn compute_reward( epoch: ChainEpoch, prev_theta: BigInt, curr_theta: BigInt, - simple_total: &BigInt, - baseline_total: &BigInt, + simple_total: &TokenAmount, + baseline_total: &TokenAmount, ) -> TokenAmount { - let mut simple_reward = simple_total * &*EXP_LAM_SUB_ONE; + let mut simple_reward = simple_total.atto() * &*EXP_LAM_SUB_ONE; let epoch_lam = &*LAMBDA * epoch; simple_reward *= expneg(&epoch_lam); simple_reward >>= PRECISION; - let baseline_reward = compute_baseline_supply(curr_theta, baseline_total) - - compute_baseline_supply(prev_theta, baseline_total); + let baseline_reward = compute_baseline_supply(curr_theta, baseline_total.atto()) + - compute_baseline_supply(prev_theta, baseline_total.atto()); - (simple_reward + baseline_reward) >> PRECISION + TokenAmount::from_atto((simple_reward + baseline_reward) >> PRECISION) } /// Computes baseline supply based on theta in Q.128 format. @@ -194,7 +193,7 @@ mod tests { let prev_theta_str = &prev_theta.to_string(); let theta_str = &theta.to_string(); - let reward_str = &reward.to_string(); + let reward_str = &reward.atto().to_string(); b.push_str(prev_theta_str); b.push(','); b.push_str(theta_str); @@ -229,7 +228,7 @@ mod tests { ); let x_str = &x.to_string(); - let reward_str = &reward.to_string(); + let reward_str = &reward.atto().to_string(); b.push_str(x_str); b.push(','); b.push_str(reward_str); diff --git a/actors/reward/src/state.rs b/actors/reward/src/state.rs index 2ed75ea47..de04c6ace 100644 --- a/actors/reward/src/state.rs +++ b/actors/reward/src/state.rs @@ -4,7 +4,7 @@ use fvm_ipld_encoding::repr::*; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; -use fvm_shared::bigint::{bigint_ser, Integer}; +use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::{ChainEpoch, EPOCH_UNDEFINED}; use fvm_shared::econ::TokenAmount; @@ -17,9 +17,9 @@ use super::logic::*; lazy_static! { /// 36.266260308195979333 FIL - pub static ref INITIAL_REWARD_POSITION_ESTIMATE: TokenAmount = TokenAmount::from(36266260308195979333u128); + pub static ref INITIAL_REWARD_POSITION_ESTIMATE: TokenAmount = TokenAmount::from_atto(36266260308195979333u128); /// -1.0982489*10^-7 FIL per epoch. Change of simple minted tokens between epochs 0 and 1. - pub static ref INITIAL_REWARD_VELOCITY_ESTIMATE: TokenAmount = TokenAmount::from(-109897758509i64); + pub static ref INITIAL_REWARD_VELOCITY_ESTIMATE: TokenAmount = TokenAmount::from_atto(-109897758509i64); } /// Reward actor state @@ -48,7 +48,6 @@ pub struct State { /// The reward to be paid in per WinCount to block producers. /// The actual reward total paid out depends on the number of winners in any round. /// This value is recomputed every non-null epoch and used in the next non-null epoch. - #[serde(with = "bigint_ser")] pub this_epoch_reward: TokenAmount, /// Smoothed `this_epoch_reward`. pub this_epoch_reward_smoothed: FilterEstimate, @@ -61,7 +60,6 @@ pub struct State { pub epoch: ChainEpoch, // TotalStoragePowerReward tracks the total FIL awarded to block miners - #[serde(with = "bigint_ser")] pub total_storage_power_reward: TokenAmount, // Simple and Baseline totals are constants used for computing rewards. @@ -69,9 +67,7 @@ pub struct State { // in a way that depended on the history leading immediately up to the // migration fixing the value. These values can be moved from state back // into a code constant in a subsequent upgrade. - #[serde(with = "bigint_ser")] pub simple_total: TokenAmount, - #[serde(with = "bigint_ser")] pub baseline_total: TokenAmount, } @@ -82,8 +78,8 @@ impl State { this_epoch_baseline_power: INIT_BASELINE_POWER.clone(), epoch: EPOCH_UNDEFINED, this_epoch_reward_smoothed: FilterEstimate::new( - INITIAL_REWARD_POSITION_ESTIMATE.clone(), - INITIAL_REWARD_VELOCITY_ESTIMATE.clone(), + INITIAL_REWARD_POSITION_ESTIMATE.atto().clone(), + INITIAL_REWARD_VELOCITY_ESTIMATE.atto().clone(), ), simple_total: SIMPLE_TOTAL.clone(), baseline_total: BASELINE_TOTAL.clone(), @@ -141,7 +137,7 @@ impl State { let filter_reward = AlphaBetaFilter::load(&self.this_epoch_reward_smoothed, &DEFAULT_ALPHA, &DEFAULT_BETA); self.this_epoch_reward_smoothed = - filter_reward.next_estimate(&self.this_epoch_reward, delta); + filter_reward.next_estimate(self.this_epoch_reward.atto(), delta); } pub fn into_total_storage_power_reward(self) -> TokenAmount { @@ -164,9 +160,7 @@ pub struct Reward { pub vesting_function: VestingFunction, pub start_epoch: ChainEpoch, pub end_epoch: ChainEpoch, - #[serde(with = "bigint_ser")] pub value: TokenAmount, - #[serde(with = "bigint_ser")] pub amount_withdrawn: TokenAmount, } @@ -180,8 +174,7 @@ impl Reward { if elapsed >= vest_duration { self.value.clone() } else { - (self.value.clone() * elapsed as u64) - .div_floor(&TokenAmount::from(vest_duration)) + (self.value.clone() * elapsed as u64).div_rem(vest_duration).0 } } } diff --git a/actors/reward/src/testing.rs b/actors/reward/src/testing.rs index d4d8f69ea..cfddba441 100644 --- a/actors/reward/src/testing.rs +++ b/actors/reward/src/testing.rs @@ -1,6 +1,5 @@ use crate::{baseline_power_from_prev, State}; use fil_actors_runtime::MessageAccumulator; -use fvm_shared::bigint::BigInt; use fvm_shared::{clock::ChainEpoch, econ::TokenAmount}; use num_traits::Signed; @@ -14,8 +13,7 @@ pub fn check_state_invariants( ) -> (StateSummary, MessageAccumulator) { let acc = MessageAccumulator::default(); - let fil = 10u64.pow(18); - let storage_mining_allocation_check = BigInt::from(1_100_000_000) * fil; + let storage_mining_allocation_check = TokenAmount::from_whole(1_100_000_000); // Can't assert equality because anyone can send funds to reward actor (and already have on mainnet) acc.require( diff --git a/actors/reward/src/types.rs b/actors/reward/src/types.rs index 42b733a77..c5db7e2e3 100644 --- a/actors/reward/src/types.rs +++ b/actors/reward/src/types.rs @@ -3,15 +3,12 @@ use fvm_ipld_encoding::tuple::*; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; use fvm_shared::econ::TokenAmount; #[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct AwardBlockRewardParams { pub miner: Address, - #[serde(with = "bigint_ser")] pub penalty: TokenAmount, - #[serde(with = "bigint_ser")] pub gas_reward: TokenAmount, pub win_count: i64, } diff --git a/actors/reward/tests/reward_actor_test.rs b/actors/reward/tests/reward_actor_test.rs index b000c2855..f8ed63937 100644 --- a/actors/reward/tests/reward_actor_test.rs +++ b/actors/reward/tests/reward_actor_test.rs @@ -24,11 +24,13 @@ use num_traits::FromPrimitive; lazy_static! { static ref EPOCH_ZERO_REWARD: TokenAmount = - TokenAmount::from_i128(36_266_264_293_777_134_739).unwrap(); + TokenAmount::from_atto(36_266_264_293_777_134_739i128); static ref WINNER: Address = Address::new_id(1000); } mod construction_tests { + use num::Zero; + use super::*; #[test] @@ -53,7 +55,7 @@ mod construction_tests { let state: State = rt.get_state(); assert_eq!(ChainEpoch::from(0), state.epoch); assert_eq!(start_realized_power, state.cumsum_realized); - assert_ne!(TokenAmount::from(0), state.this_epoch_reward); + assert_ne!(TokenAmount::zero(), state.this_epoch_reward); } #[test] @@ -78,6 +80,7 @@ mod test_award_block_reward { use fvm_shared::error::ExitCode; use fvm_shared::sector::StoragePower; use fvm_shared::METHOD_SEND; + use num::Zero; use super::*; @@ -85,17 +88,17 @@ mod test_award_block_reward { fn rejects_gas_reward_exceeding_balance() { let mut rt = construct_and_verify(&StoragePower::default()); - rt.set_balance(TokenAmount::from(9)); + rt.set_balance(TokenAmount::from_atto(9)); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); assert_eq!( ExitCode::USR_ILLEGAL_STATE, award_block_reward( &mut rt, *WINNER, - TokenAmount::from(0), - TokenAmount::from(10), + TokenAmount::zero(), + TokenAmount::from_atto(10), 1, - TokenAmount::from(0) + TokenAmount::zero() ) .unwrap_err() .exit_code() @@ -105,7 +108,7 @@ mod test_award_block_reward { #[test] fn rejects_negative_penalty_or_reward() { let mut rt = construct_and_verify(&StoragePower::default()); - rt.set_balance(TokenAmount::from(10_i128.pow(18))); + rt.set_balance(TokenAmount::from_whole(1)); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); let reward_penalty_pairs = [(-1, 0), (0, -1)]; @@ -116,10 +119,10 @@ mod test_award_block_reward { award_block_reward( &mut rt, *WINNER, - TokenAmount::from(*penalty), - TokenAmount::from(*reward), + TokenAmount::from_atto(*penalty), + TokenAmount::from_atto(*reward), 1, - TokenAmount::from(0) + TokenAmount::zero() ) .unwrap_err() .exit_code() @@ -131,16 +134,16 @@ mod test_award_block_reward { #[test] fn rejects_zero_wincount() { let mut rt = construct_and_verify(&StoragePower::default()); - rt.set_balance(TokenAmount::from(10_i128.pow(18))); + rt.set_balance(TokenAmount::from_whole(1)); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); assert!(award_block_reward( &mut rt, *WINNER, - TokenAmount::from(0), - TokenAmount::from(0), + TokenAmount::zero(), + TokenAmount::zero(), 0, - TokenAmount::from(0) + TokenAmount::zero() ) .is_err()); rt.reset(); @@ -149,12 +152,12 @@ mod test_award_block_reward { #[test] fn pays_reward_and_tracks_penalty() { let mut rt = construct_and_verify(&StoragePower::default()); - rt.set_balance(TokenAmount::from(10_i128.pow(27))); + rt.set_balance(TokenAmount::from_whole(1_000_000_000)); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); - let penalty: TokenAmount = TokenAmount::from(100); - let gas_reward: TokenAmount = TokenAmount::from(200); + let penalty: TokenAmount = TokenAmount::from_atto(100); + let gas_reward: TokenAmount = TokenAmount::from_atto(200); let expected_reward: TokenAmount = - &*EPOCH_ZERO_REWARD / EXPECTED_LEADERS_PER_EPOCH + &gas_reward; + EPOCH_ZERO_REWARD.div_floor(EXPECTED_LEADERS_PER_EPOCH) + &gas_reward; let miner_penalty = PENALTY_MULTIPLIER * &penalty; let params = RawBytes::serialize(&ext::miner::ApplyRewardParams { reward: expected_reward.clone(), @@ -185,8 +188,8 @@ mod test_award_block_reward { let mut rt = construct_and_verify(&StoragePower::from(1)); // Total reward is a huge number, upon writing ~1e18, so 300 should be way less - let small_reward = TokenAmount::from(300); - let penalty = TokenAmount::from(100); + let small_reward = TokenAmount::from_atto(300); + let penalty = TokenAmount::from_atto(100); rt.set_balance(small_reward.clone()); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); @@ -208,7 +211,7 @@ mod test_award_block_reward { let params = AwardBlockRewardParams { miner: *WINNER, penalty, - gas_reward: TokenAmount::from(0), + gas_reward: TokenAmount::zero(), win_count: 1, }; assert!(rt @@ -225,22 +228,22 @@ mod test_award_block_reward { let mut rt = construct_and_verify(&StoragePower::from(1)); let mut state: State = rt.get_state(); - assert_eq!(TokenAmount::from(0), state.total_storage_power_reward); - state.this_epoch_reward = TokenAmount::from(5000); + assert_eq!(TokenAmount::zero(), state.total_storage_power_reward); + state.this_epoch_reward = TokenAmount::from_atto(5000); rt.replace_state(&state); - let total_payout = TokenAmount::from(3500); + let total_payout = TokenAmount::from_atto(3500); rt.set_balance(total_payout.clone()); for i in &[1000, 1000, 1000, 500] { assert!(award_block_reward( &mut rt, *WINNER, - TokenAmount::from(0), - TokenAmount::from(0), + TokenAmount::zero(), + TokenAmount::zero(), 1, - TokenAmount::from(*i) + TokenAmount::from_atto(*i) ) .is_ok()); } @@ -254,15 +257,15 @@ mod test_award_block_reward { let mut rt = construct_and_verify(&StoragePower::from(1)); let mut state: State = rt.get_state(); - assert_eq!(TokenAmount::from(0), state.total_storage_power_reward); - state.this_epoch_reward = TokenAmount::from(5000); + assert_eq!(TokenAmount::zero(), state.total_storage_power_reward); + state.this_epoch_reward = TokenAmount::from_atto(5000); rt.replace_state(&state); // enough balance to pay 3 full rewards and one partial - rt.set_balance(TokenAmount::from(3500)); + rt.set_balance(TokenAmount::from_atto(3500)); rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); - let expected_reward = TokenAmount::from(1000); - let miner_penalty = TokenAmount::from(0); + let expected_reward = TokenAmount::from_atto(1000); + let miner_penalty = TokenAmount::zero(); let params = RawBytes::serialize(&ext::miner::ApplyRewardParams { reward: expected_reward.clone(), penalty: miner_penalty, @@ -287,8 +290,8 @@ mod test_award_block_reward { let params = AwardBlockRewardParams { miner: *WINNER, - penalty: TokenAmount::from(0), - gas_reward: TokenAmount::from(0), + penalty: TokenAmount::zero(), + gas_reward: TokenAmount::zero(), win_count: 1, }; @@ -373,7 +376,7 @@ fn award_block_reward( ExitCode::OK, ); - if penalty > TokenAmount::from(0) { + if penalty.is_positive() { rt.expect_send( *BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 31b96c861..6b00c044f 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 020634e4b..73e8f0964 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 6d5777985..8a44e88ea 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,33 +8,34 @@ edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] -anyhow = "1.0.56" -base64 = "0.13.0" -blake2b_simd = "1.0" +fvm_ipld_hamt = "0.5.1" +fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +num-traits = "0.2.14" +num-derive = "0.3.3" +serde = { version = "1.0.136", features = ["derive"] } +lazy_static = "1.4.0" +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"] } -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" -fvm_ipld_hamt = "0.5.1" -fvm_sdk = { version = "2.0.0-alpha.1", optional = true } -fvm_shared = { version = "0.9.0", default-features = false } +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 } -indexmap = { version = "1.8.0", features = ["serde-1"] } -integer-encoding = { version = "3.0.3", default-features = false } -itertools = "0.10" -lazy_static = "1.4.0" -log = "0.4.14" +anyhow = "1.0.56" +fvm_sdk = { version = "3.0.0-alpha.1", optional = true } +blake2b_simd = "1.0" +fvm_ipld_blockstore = "0.1.1" +fvm_ipld_encoding = "0.2.2" multihash = { version = "0.16.1", default-features = false } -num-derive = "0.3.3" -num-traits = "0.2.14" rand = "0.8.5" -regex = "1" -serde = { version = "1.0.136", features = ["derive"] } serde_repr = "0.1.8" -thiserror = "1.0.30" -unsigned-varint = "0.7.1" +regex = "1" +itertools = "0.10" [dependencies.sha2] version = "0.10" diff --git a/runtime/src/builtin/network.rs b/runtime/src/builtin/network.rs index 13a0df1d1..37b8d61d0 100644 --- a/runtime/src/builtin/network.rs +++ b/runtime/src/builtin/network.rs @@ -10,6 +10,3 @@ pub const SECONDS_IN_YEAR: i64 = 31556925; pub const EPOCHS_IN_HOUR: i64 = SECONDS_IN_HOUR / EPOCH_DURATION_SECONDS; pub const EPOCHS_IN_DAY: i64 = SECONDS_IN_DAY / EPOCH_DURATION_SECONDS; pub const EPOCHS_IN_YEAR: i64 = SECONDS_IN_YEAR / EPOCH_DURATION_SECONDS; - -// 1 NanoFIL -pub const ONE_NANO_FIL: u64 = 10u64.pow(9); diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 4937a7900..f7d71b5fb 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -171,12 +171,11 @@ where } fn resolve_builtin_actor_type(&self, code_id: &Cid) -> Option { - fvm::actor::get_builtin_actor_type(code_id).and_then(|t| Type::from_i32(t as i32)) + fvm::actor::get_builtin_actor_type(code_id).and_then(Type::from_i32) } fn get_code_cid_for_type(&self, typ: Type) -> Cid { - let t = fvm_shared::actor::builtin::Type::from_i32(typ as i32).unwrap(); - fvm::actor::get_code_cid_for_type(t) + fvm::actor::get_code_cid_for_type(typ as i32) } fn get_randomness_from_tickets( @@ -195,7 +194,9 @@ where // // Since that behaviour changes, we may as well abort with a more appropriate exit code // explicitly. - fvm::rand::get_chain_randomness(personalization as i64, rand_epoch, entropy).map_err(|e| { + fvm::rand::get_chain_randomness(personalization as i64, rand_epoch, entropy) + .map(|v|Randomness(v.into())) + .map_err(|e| { if self.network_version() < NetworkVersion::V16 { ActorError::unchecked(ExitCode::SYS_ILLEGAL_INSTRUCTION, "failed to get chain randomness".into()) @@ -217,7 +218,9 @@ where entropy: &[u8], ) -> Result { // See note on exit codes in get_randomness_from_tickets. - fvm::rand::get_beacon_randomness(personalization as i64, rand_epoch, entropy).map_err(|e| { + fvm::rand::get_beacon_randomness(personalization as i64, rand_epoch, entropy) + .map(|v|Randomness(v.into())) + .map_err(|e| { if self.network_version() < NetworkVersion::V16 { ActorError::unchecked(ExitCode::SYS_ILLEGAL_INSTRUCTION, "failed to get chain randomness".into()) diff --git a/runtime/src/util/chaos/types.rs b/runtime/src/util/chaos/types.rs index c61bb6fb6..21829fde1 100644 --- a/runtime/src/util/chaos/types.rs +++ b/runtime/src/util/chaos/types.rs @@ -5,7 +5,6 @@ use cid::Cid; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -31,7 +30,6 @@ pub struct ResolveAddressResponse { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct SendArgs { pub to: Address, - #[serde(with = "bigint_ser")] pub value: TokenAmount, #[serde(rename = "MethodNum")] pub method: u64, @@ -62,10 +60,8 @@ pub struct AbortWithArgs { pub struct InspectRuntimeReturn { pub caller: Address, pub receiver: Address, - #[serde(with = "bigint_ser")] pub value_received: TokenAmount, pub curr_epoch: ChainEpoch, - #[serde(with = "bigint_ser")] pub current_balance: TokenAmount, pub state: State, } diff --git a/runtime/tests/alpha_beta_filter_test.rs b/runtime/tests/alpha_beta_filter_test.rs index e4a78c361..cb55369f7 100644 --- a/runtime/tests/alpha_beta_filter_test.rs +++ b/runtime/tests/alpha_beta_filter_test.rs @@ -4,6 +4,7 @@ use fil_actors_runtime::EPOCHS_IN_DAY; use fvm_shared::bigint::{BigInt, Integer}; use fvm_shared::clock::ChainEpoch; +use fvm_shared::econ::TokenAmount; use fvm_shared::math::{poly_parse, PRECISION}; use fvm_shared::sector::StoragePower; use fvm_shared::smooth::{extrapolated_cum_sum_of_ratio as ecsor, *}; @@ -148,7 +149,7 @@ fn flipped_signs() { #[test] fn values_in_range() { - let tens_of_fil = BigInt::from(50 * 10_i128.pow(18)); + let tens_of_fil = TokenAmount::from_whole(50).atto().clone(); let one_fil_per_sec = BigInt::from(25); let four_fil_per_second = BigInt::from(100); diff --git a/state/Cargo.toml b/state/Cargo.toml index d569101da..abd7b401f 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/state/src/check.rs b/state/src/check.rs index 8283f81c6..032a49b5d 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -28,14 +28,13 @@ use fvm_ipld_encoding::from_slice; use fvm_ipld_encoding::CborStore; use fvm_shared::address::Address; use fvm_shared::address::Protocol; -use fvm_shared::bigint::BigInt; + use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use num_traits::Zero; use anyhow::anyhow; use fvm_ipld_encoding::tuple::*; -use fvm_shared::bigint::bigint_ser; use fil_actor_account::testing as account; use fil_actor_cron::testing as cron; @@ -59,7 +58,6 @@ pub struct Actor { pub head: Cid, /// `call_seq_num` for the next message to be received by the actor (non-zero for accounts only) pub call_seq_num: u64, - #[serde(with = "bigint_ser")] /// Token balance of the actor pub balance: TokenAmount, } @@ -115,7 +113,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( prior_epoch: ChainEpoch, ) -> anyhow::Result { let acc = MessageAccumulator::default(); - let mut total_fil = BigInt::zero(); + let mut total_fil = TokenAmount::zero(); let mut init_summary: Option = None; let mut cron_summary: Option = None; diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 31ea10f69..4ca7cc34a 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,7 +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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "0.9.0", default-features = false } +fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index fee6c2fdb..94e65677c 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -35,7 +35,7 @@ use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; use fvm_shared::address::Payload; use fvm_shared::address::{Address, Protocol}; -use fvm_shared::bigint::{bigint_ser, BigInt, Zero}; +use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::signature::Signature; @@ -50,7 +50,6 @@ use fvm_shared::sector::{ use fvm_shared::smooth::FilterEstimate; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; -use num_traits::Signed; use regex::Regex; use serde::ser; use std::cell::{RefCell, RefMut}; @@ -131,12 +130,8 @@ impl<'bs> VM<'bs> { } pub fn new_with_singletons(store: &'bs MemoryBlockstore) -> VM<'bs> { - // funding - let fil = TokenAmount::from(1_000_000_000i32) - .checked_mul(&TokenAmount::from(1_000_000_000i32)) - .unwrap(); - let reward_total = TokenAmount::from(1_100_000_000i32).checked_mul(&fil).unwrap(); - let faucet_total = TokenAmount::from(1_000_000_000u32).checked_mul(&fil).unwrap(); + let reward_total = TokenAmount::from_whole(1_100_000_000i64); + let faucet_total = TokenAmount::from_whole(1_000_000_000i64); let v = VM::new(store).with_total_fil(&reward_total + &faucet_total); @@ -218,7 +213,7 @@ impl<'bs> VM<'bs> { .apply_message( *SYSTEM_ACTOR_ADDR, *INIT_ACTOR_ADDR, - BigInt::zero(), + TokenAmount::zero(), fil_actor_init::Method::Exec as u64, fil_actor_init::ExecParams { code_cid: *MULTISIG_ACTOR_CODE_ID, @@ -404,7 +399,7 @@ impl<'bs> VM<'bs> { originator_stable_addr: from, _originator_call_seq: call_seq, new_actor_addr_count: RefCell::new(0), - circ_supply: TokenAmount::from(1e9 as u128 * 1e18 as u128), + circ_supply: TokenAmount::from_whole(1_000_000_000), }; let msg = InternalMessage { from: from_id, @@ -486,10 +481,10 @@ impl<'bs> VM<'bs> { pub fn get_total_actor_balance( &self, store: &MemoryBlockstore, - ) -> anyhow::Result { + ) -> anyhow::Result { let state_tree = Tree::load(store, &self.checkpoint())?; - let mut total = BigInt::zero(); + let mut total = TokenAmount::zero(); state_tree.for_each(|_, actor| { total += &actor.balance.clone(); Ok(()) @@ -503,7 +498,7 @@ pub struct TopCtx { originator_stable_addr: Address, _originator_call_seq: u64, new_actor_addr_count: RefCell, - circ_supply: BigInt, + circ_supply: TokenAmount, } #[derive(Clone, Debug)] @@ -626,13 +621,13 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { // Transfer funds let mut from_actor = self.v.get_actor(self.msg.from).unwrap(); if !self.msg.value.is_zero() { - if self.msg.value.lt(&BigInt::zero()) { + if self.msg.value.is_negative() { return Err(ActorError::unchecked( ExitCode::SYS_ASSERTION_FAILED, "attempt to transfer negative value".to_string(), )); } - if from_actor.balance.lt(&self.msg.value) { + if from_actor.balance < self.msg.value { return Err(ActorError::unchecked( ExitCode::SYS_INSUFFICIENT_FUNDS, "insufficient balance to transfer".to_string(), @@ -641,7 +636,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } // Load, deduct, store from actor before loading to actor to handle self-send case - from_actor.balance = from_actor.balance.abs_sub(&self.msg.value); + from_actor.balance -= &self.msg.value; self.v.set_actor(self.msg.from, from_actor); let (mut to_actor, to_addr) = self.resolve_target(&self.msg.to)?; @@ -695,7 +690,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat "attempt to create new actor at existing address".to_string(), )); } - let a = actor(code_id, self.v.empty_obj_cid, 0, BigInt::zero()); + let a = actor(code_id, self.v.empty_obj_cid, 0, TokenAmount::zero()); self.v.set_actor(addr, a); Ok(()) } @@ -830,7 +825,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat _rand_epoch: ChainEpoch, _entropy: &[u8], ) -> Result { - Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().to_vec())) + Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().into())) } fn get_randomness_from_beacon( @@ -839,7 +834,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat _rand_epoch: ChainEpoch, _entropy: &[u8], ) -> Result { - Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().to_vec())) + Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().into())) } fn create(&mut self, obj: &C) -> Result<(), ActorError> { @@ -1039,7 +1034,6 @@ pub struct Actor { pub code: Cid, pub head: Cid, pub call_seq_num: u64, - #[serde(with = "bigint_ser")] pub balance: TokenAmount, } diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 845b50ab5..37bb756b2 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -581,9 +581,9 @@ pub fn publish_deal( label, start_epoch: deal_start, end_epoch: deal_start + deal_lifetime, - storage_price_per_epoch: TokenAmount::from((1 << 20) as u64), - provider_collateral: TokenAmount::from(2e18 as u64), - client_collateral: TokenAmount::from(1e18 as u64), + storage_price_per_epoch: TokenAmount::from_atto((1 << 20) as u64), + provider_collateral: TokenAmount::from_whole(2), + client_collateral: TokenAmount::from_whole(1), }; let publish_params = PublishStorageDealsParams { diff --git a/test_vm/tests/batch_onboarding.rs b/test_vm/tests/batch_onboarding.rs index 22b5e5919..38f5f606a 100644 --- a/test_vm/tests/batch_onboarding.rs +++ b/test_vm/tests/batch_onboarding.rs @@ -12,7 +12,6 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::bigint::{BigInt, Zero}; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{RegisteredSealProof, SectorNumber}; -use num_traits::Signed; use test_vm::util::{ advance_to_proving_deadline, apply_ok, create_accounts, create_miner, invariant_failure_patterns, precommit_sectors, prove_commit_sectors, submit_windowed_post, @@ -49,7 +48,7 @@ impl Onboarding { fn batch_onboarding() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, _) = create_miner( @@ -57,7 +56,7 @@ fn batch_onboarding() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); let mut v = v.with_epoch(200); diff --git a/test_vm/tests/commit_post_test.rs b/test_vm/tests/commit_post_test.rs index 386cb80a4..e2315a14d 100644 --- a/test_vm/tests/commit_post_test.rs +++ b/test_vm/tests/commit_post_test.rs @@ -23,7 +23,6 @@ use fvm_shared::error::ExitCode; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{PoStProof, RegisteredSealProof, SectorNumber, MAX_SECTOR_NUMBER}; use fvm_shared::METHOD_SEND; -use num_traits::sign::Signed; use test_vm::util::{ advance_by_deadline_to_epoch, advance_to_proving_deadline, apply_code, apply_ok, create_accounts, create_miner, invariant_failure_patterns, precommit_sectors, @@ -47,7 +46,7 @@ struct MinerInfo { fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, robust_addr) = create_miner( @@ -55,7 +54,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); let mut v = v.with_epoch(200); @@ -319,7 +318,7 @@ fn overdue_precommit() { let store = MemoryBlockstore::new(); let policy = &Policy::default(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let id_addr = create_miner( @@ -327,7 +326,7 @@ fn overdue_precommit() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ) .0; let mut v = v.with_epoch(200); @@ -433,7 +432,7 @@ fn overdue_precommit() { fn aggregate_bad_sector_number() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, robust_addr) = create_miner( @@ -441,7 +440,7 @@ fn aggregate_bad_sector_number() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); let mut v = v.with_epoch(200); let policy = &Policy::default(); @@ -514,7 +513,7 @@ fn aggregate_size_limits() { let oversized_batch = 820; let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, robust_addr) = create_miner( @@ -522,7 +521,7 @@ fn aggregate_size_limits() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(100_000), ); let mut v = v.with_epoch(200); let policy = &Policy::default(); @@ -647,7 +646,7 @@ fn aggregate_size_limits() { fn aggregate_bad_sender() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 2, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 2, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, robust_addr) = create_miner( @@ -655,7 +654,7 @@ fn aggregate_bad_sender() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); let mut v = v.with_epoch(200); let policy = &Policy::default(); @@ -723,7 +722,7 @@ fn aggregate_bad_sender() { fn aggregate_one_precommit_expires() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker) = (addrs[0], addrs[0]); let (id_addr, robust_addr) = create_miner( @@ -731,7 +730,7 @@ fn aggregate_one_precommit_expires() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); let mut v = v.with_epoch(200); let policy = &Policy::default(); diff --git a/test_vm/tests/extend_sectors_test.rs b/test_vm/tests/extend_sectors_test.rs index a158b7a04..e6a5dee1d 100644 --- a/test_vm/tests/extend_sectors_test.rs +++ b/test_vm/tests/extend_sectors_test.rs @@ -32,7 +32,7 @@ use test_vm::{ExpectInvocation, VM}; fn extend_sector_with_deals() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 3, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (owner, worker, verifier, verified_client) = (addrs[0], addrs[0], addrs[1], addrs[2]); let sector_number: SectorNumber = 100; @@ -46,7 +46,7 @@ fn extend_sector_with_deals() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(1_000e18 as i128), + TokenAmount::from_whole(1_000), ) .0; let mut v = v.with_epoch(200); @@ -72,7 +72,7 @@ fn extend_sector_with_deals() { ); // add market collateral for clients and miner - let mut collateral = TokenAmount::from(3e18 as i128); + let mut collateral = TokenAmount::from_whole(3); apply_ok( &v, verified_client, @@ -81,7 +81,7 @@ fn extend_sector_with_deals() { MarketMethod::AddBalance as u64, verified_client, ); - collateral = TokenAmount::from(64e18 as i128); + collateral = TokenAmount::from_whole(64); apply_ok( &v, worker, diff --git a/test_vm/tests/market_miner_withdrawal_test.rs b/test_vm/tests/market_miner_withdrawal_test.rs index 1a12a5164..632e8802d 100644 --- a/test_vm/tests/market_miner_withdrawal_test.rs +++ b/test_vm/tests/market_miner_withdrawal_test.rs @@ -7,8 +7,7 @@ use fil_actors_runtime::STORAGE_MARKET_ACTOR_ADDR; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::BigIntDe; -use fvm_shared::bigint::BigInt; + use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -27,7 +26,7 @@ mod market_tests { let store = MemoryBlockstore::new(); let (v, caller) = market_setup(&store); - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw( &v, three_fil.clone(), @@ -44,8 +43,8 @@ mod market_tests { let (v, caller) = market_setup(&store); // Add 2 FIL of collateral and attempt to withdraw 3 - let two_fil = TokenAmount::from(2) * BigInt::from(1e18 as i128); - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let two_fil = TokenAmount::from_whole(2); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw( &v, two_fil.clone(), @@ -62,7 +61,7 @@ mod market_tests { let (v, caller) = market_setup(&store); // Add 0 FIL of collateral and attempt to withdraw 3 - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw( &v, TokenAmount::zero(), @@ -83,7 +82,7 @@ mod miner_tests { let store = MemoryBlockstore::new(); let (v, _, owner, m_addr) = miner_setup(&store); - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw( &v, three_fil.clone(), @@ -99,8 +98,8 @@ mod miner_tests { let store = MemoryBlockstore::new(); let (v, _, owner, m_addr) = miner_setup(&store); - let two_fil = TokenAmount::from(2) * BigInt::from(1e18 as i128); - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let two_fil = TokenAmount::from_whole(2); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw(&v, two_fil.clone(), two_fil, three_fil, m_addr, owner); } @@ -109,7 +108,7 @@ mod miner_tests { let store = MemoryBlockstore::new(); let (v, _, owner, m_addr) = miner_setup(&store); - let three_fil = TokenAmount::from(3) * BigInt::from(1e18 as i128); + let three_fil = TokenAmount::from_whole(3); assert_add_collateral_and_withdraw( &v, TokenAmount::zero(), @@ -125,7 +124,7 @@ mod miner_tests { let store = MemoryBlockstore::new(); let (v, worker, _, m_addr) = miner_setup(&store); - let one_fil = TokenAmount::from(1) * BigInt::from(1e18 as i128); + let one_fil = TokenAmount::from_whole(1); let params = MinerWithdrawBalanceParams { amount_requested: one_fil }; apply_code( &v, @@ -163,7 +162,7 @@ fn assert_add_collateral_and_withdraw( let caller_initial_balance = c.balance; // add collateral - if collateral > BigInt::zero() { + if collateral.is_positive() { match a_type { x if x == *MINER_ACTOR_CODE_ID => { apply_ok(v, caller, escrow, collateral.clone(), METHOD_SEND, RawBytes::default()) @@ -184,12 +183,19 @@ fn assert_add_collateral_and_withdraw( assert_eq!(&caller_initial_balance - &collateral, c.balance); // attempt to withdraw withdrawal - let ret: BigIntDe = match a_type { + let withdrawn: TokenAmount = match a_type { x if x == *MINER_ACTOR_CODE_ID => { let params = MinerWithdrawBalanceParams { amount_requested: requested }; - apply_ok(v, caller, escrow, BigInt::zero(), MinerMethod::WithdrawBalance as u64, params) - .deserialize() - .unwrap() + apply_ok( + v, + caller, + escrow, + TokenAmount::zero(), + MinerMethod::WithdrawBalance as u64, + params, + ) + .deserialize() + .unwrap() } x if x == *MARKET_ACTOR_CODE_ID => { let params = @@ -198,7 +204,7 @@ fn assert_add_collateral_and_withdraw( v, caller, escrow, - BigInt::zero(), + TokenAmount::zero(), MarketMethod::WithdrawBalance as u64, params, ) @@ -207,7 +213,6 @@ fn assert_add_collateral_and_withdraw( } _ => panic!("unreachable"), }; - let withdrawn = ret.0; assert_eq!(expected_withdrawn, withdrawn); c = require_actor(v, caller); @@ -220,7 +225,7 @@ fn require_actor(v: &VM, addr: Address) -> Actor { fn market_setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Address) { let v = VM::new_with_singletons(store); - let initial_balance = BigInt::from(6) * BigInt::from(1e18 as i128); + let initial_balance = TokenAmount::from_whole(6); let addrs = create_accounts(&v, 1, initial_balance); let caller = addrs[0]; (v, caller) @@ -228,7 +233,7 @@ fn market_setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Address) { fn miner_setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Address, Address, Address) { let mut v = VM::new_with_singletons(store); - let initial_balance = BigInt::from(10_000) * BigInt::from(1e18 as i128); + let initial_balance = TokenAmount::from_whole(10_000); let addrs = create_accounts(&v, 2, initial_balance); let (worker, owner) = (addrs[0], addrs[1]); @@ -238,7 +243,7 @@ fn miner_setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Address, Address, Addres owner, worker, RegisteredPoStProof::StackedDRGWindow32GiBV1, - TokenAmount::from(0), + TokenAmount::zero(), ); (v, worker, owner, m_addr) diff --git a/test_vm/tests/multisig_test.rs b/test_vm/tests/multisig_test.rs index f176b256e..8cc5e2591 100644 --- a/test_vm/tests/multisig_test.rs +++ b/test_vm/tests/multisig_test.rs @@ -23,7 +23,7 @@ use test_vm::{ExpectInvocation, VM}; fn test_proposal_hash() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 3, TokenAmount::from(10_000e18 as u64)); + let addrs = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); let alice = addrs[0]; let bob = addrs[1]; let sys_act_start_bal = v.get_actor(*SYSTEM_ACTOR_ADDR).unwrap().balance; @@ -31,7 +31,7 @@ fn test_proposal_hash() { let msig_addr = create_msig(&v, addrs, 2); // fund msig and propose send funds to system actor - let fil_delta = TokenAmount::from(3 * 1_000_000_000_u64); // 3 nFIL + let fil_delta = TokenAmount::from_nano(3); let propose_send_sys_params = ProposeParams { to: *SYSTEM_ACTOR_ADDR, value: fil_delta.clone(), @@ -49,7 +49,7 @@ fn test_proposal_hash() { let wrong_tx = Transaction { to: *SYSTEM_ACTOR_ADDR, - value: fil_delta.clone() - 1_u64, // incorrect send amount not consistent with proposal + value: &fil_delta - TokenAmount::from_atto(1), // incorrect send amount not consistent with proposal method: METHOD_SEND, approved: vec![alice], params: RawBytes::default(), @@ -103,7 +103,7 @@ fn test_delete_self() { let test = |threshold: usize, signers: u64, remove_idx: usize| { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, signers, TokenAmount::from(10_000e18 as u64)); + let addrs = create_accounts(&v, signers, TokenAmount::from_whole(10_000)); let msig_addr = create_msig(&v, addrs.clone(), threshold as u64); @@ -175,7 +175,7 @@ fn test_delete_self() { fn swap_self_1_of_2() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 3, TokenAmount::from(10_000e18 as u64)); + let addrs = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); let (alice, bob, chuck) = (addrs[0], addrs[1], addrs[2]); let msig_addr = create_msig(&v, vec![alice, bob], 1); @@ -204,7 +204,7 @@ fn swap_self_1_of_2() { fn swap_self_2_of_3() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 4, TokenAmount::from(10_000e18 as u64)); + let addrs = create_accounts(&v, 4, TokenAmount::from_whole(10_000)); let (alice, bob, chuck, dinesh) = (addrs[0], addrs[1], addrs[2], addrs[3]); let msig_addr = create_msig(&v, vec![alice, bob, chuck], 2); @@ -289,7 +289,7 @@ fn create_msig(v: &VM, signers: Vec
, threshold: u64) -> Address { v, signers[0], *INIT_ACTOR_ADDR, - TokenAmount::from(0_u64), + TokenAmount::zero(), fil_actor_init::Method::Exec as u64, fil_actor_init::ExecParams { code_cid: *MULTISIG_ACTOR_CODE_ID, diff --git a/test_vm/tests/power_scenario_tests.rs b/test_vm/tests/power_scenario_tests.rs index 566c4d097..7512b1faa 100644 --- a/test_vm/tests/power_scenario_tests.rs +++ b/test_vm/tests/power_scenario_tests.rs @@ -15,7 +15,7 @@ use fil_actors_runtime::{ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::{BytesDe, RawBytes}; use fvm_shared::address::Address; -use fvm_shared::bigint::BigInt; + use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{RegisteredPoStProof, RegisteredSealProof}; use fvm_shared::METHOD_SEND; @@ -34,7 +34,7 @@ fn create_miner_test() { v.apply_message( TEST_FAUCET_ADDR, owner, - TokenAmount::from(10_000u32), + TokenAmount::from_atto(10_000u32), METHOD_SEND, RawBytes::default(), ) @@ -53,7 +53,7 @@ fn create_miner_test() { .apply_message( owner, *STORAGE_POWER_ACTOR_ADDR, - TokenAmount::from(1000u32), + TokenAmount::from_atto(1000u32), PowerMethod::CreateMiner as u64, params.clone(), ) @@ -105,7 +105,7 @@ fn test_cron_tick() { let store = MemoryBlockstore::new(); let mut vm = VM::new_with_singletons(&store); - let addrs = create_accounts(&vm, 1, BigInt::from(10_000u64) * BigInt::from(10u64.pow(18))); + let addrs = create_accounts(&vm, 1, TokenAmount::from_whole(10_000)); // create a miner let (id_addr, robust_addr) = create_miner( @@ -113,7 +113,7 @@ fn test_cron_tick() { addrs[0], addrs[0], RegisteredPoStProof::StackedDRGWindow32GiBV1, - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // create precommit @@ -137,7 +137,7 @@ fn test_cron_tick() { &vm, addrs[0], robust_addr, - TokenAmount::from(0u32), + TokenAmount::zero(), MinerMethod::PreCommitSector as u64, precommit_params, ); @@ -153,7 +153,7 @@ fn test_cron_tick() { &v, *CRON_ACTOR_ADDR, *STORAGE_POWER_ACTOR_ADDR, - BigInt::zero(), + TokenAmount::zero(), PowerMethod::OnEpochTickEnd as u64, RawBytes::default(), ); @@ -190,7 +190,7 @@ fn test_cron_tick() { &v, *CRON_ACTOR_ADDR, *STORAGE_POWER_ACTOR_ADDR, - BigInt::zero(), + TokenAmount::zero(), PowerMethod::OnEpochTickEnd as u64, RawBytes::default(), ); @@ -207,7 +207,7 @@ fn test_cron_tick() { to: id_addr, method: MinerMethod::OnDeferredCronEvent as u64, from: Some(*STORAGE_POWER_ACTOR_ADDR), - value: Some(BigInt::zero()), + value: Some(TokenAmount::zero()), ..Default::default() }, // expect call to reward to update kpi diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 02ad64202..0a0565364 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -35,22 +35,22 @@ struct Addrs { const DEAL_LIFETIME: ChainEpoch = 181 * EPOCHS_IN_DAY; fn token_defaults() -> (TokenAmount, TokenAmount, TokenAmount) { - let price_per_epoch = TokenAmount::from(1 << 20); - let provider_collateral = TokenAmount::from(2e18 as u128); - let client_collateral = TokenAmount::from(1e18 as u128); + let price_per_epoch = TokenAmount::from_atto(1 << 20); + let provider_collateral = TokenAmount::from_whole(2); + let client_collateral = TokenAmount::from_whole(1); (price_per_epoch, provider_collateral, client_collateral) } // create miner and client and add collateral fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 7, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 7, TokenAmount::from_whole(10_000)); let (worker, client1, client2, not_miner, cheap_client, verifier, verified_client) = (addrs[0], addrs[1], addrs[2], addrs[3], addrs[4], addrs[5], addrs[6]); let owner = worker; // setup provider - let miner_balance = TokenAmount::from(100e18 as i128); + let miner_balance = TokenAmount::from_whole(100); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let maddr = create_miner( @@ -77,7 +77,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { add_client_params, ); - let client_collateral = TokenAmount::from(100e18 as i128); + let client_collateral = TokenAmount::from_whole(100); apply_ok( &v, client1, @@ -103,7 +103,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { verified_client, ); - let miner_collateral = TokenAmount::from(100e18 as i128); + let miner_collateral = TokenAmount::from_whole(100); apply_ok( &v, worker, @@ -255,13 +255,13 @@ fn psd_not_enough_provider_lockup_for_batch() { let (mut v, a, deal_start) = setup(&store); // note different seed, different address - let cheap_worker = create_accounts_seeded(&v, 1, TokenAmount::from(10_000e18 as u128), 444)[0]; + let cheap_worker = create_accounts_seeded(&v, 1, TokenAmount::from_whole(10_000), 444)[0]; let cheap_maddr = create_miner( &mut v, cheap_worker, cheap_worker, fvm_shared::sector::RegisteredPoStProof::StackedDRGWindow32GiBV1, - TokenAmount::from(100e18 as u128), + TokenAmount::from_whole(100), ) .0; // add one deal of collateral to provider's market account diff --git a/test_vm/tests/replica_update_test.rs b/test_vm/tests/replica_update_test.rs index 8fd9d7cff..a99e36dc0 100644 --- a/test_vm/tests/replica_update_test.rs +++ b/test_vm/tests/replica_update_test.rs @@ -25,7 +25,6 @@ use fvm_shared::error::ExitCode; use fvm_shared::sector::SectorSize; use fvm_shared::sector::StoragePower; use fvm_shared::sector::{RegisteredSealProof, SectorNumber}; -use num_traits::sign::Signed; use test_vm::util::{ advance_by_deadline_to_epoch, advance_by_deadline_to_index, advance_to_proving_deadline, apply_code, apply_ok, bf_all, check_sector_active, check_sector_faulty, create_accounts, @@ -165,7 +164,7 @@ fn prove_replica_update_multi_dline() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(1_000_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, _) = create_miner( @@ -173,7 +172,7 @@ fn prove_replica_update_multi_dline() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(100_000), ); v = v.with_epoch(1440); // something offset far away from deadline 0 and 1 @@ -304,7 +303,7 @@ fn prove_replica_update_multi_dline() { fn immutable_deadline_failure() { let store = &MemoryBlockstore::new(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -312,7 +311,7 @@ fn immutable_deadline_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -355,7 +354,7 @@ fn unhealthy_sector_failure() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -363,7 +362,7 @@ fn unhealthy_sector_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -410,7 +409,7 @@ fn unhealthy_sector_failure() { fn terminated_sector_failure() { let store = &MemoryBlockstore::new(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -418,7 +417,7 @@ fn terminated_sector_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -476,7 +475,7 @@ fn bad_batch_size_failure() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -484,7 +483,7 @@ fn bad_batch_size_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -577,7 +576,7 @@ fn bad_post_upgrade_dispute() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -585,7 +584,7 @@ fn bad_post_upgrade_dispute() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -731,7 +730,7 @@ fn wrong_deadline_index_failure() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -739,7 +738,7 @@ fn wrong_deadline_index_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -788,7 +787,7 @@ fn wrong_partition_index_failure() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -796,7 +795,7 @@ fn wrong_partition_index_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -845,7 +844,7 @@ fn deal_included_in_multiple_sectors_failure() { let store = &MemoryBlockstore::new(); let policy = Policy::default(); let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, _) = create_miner( @@ -853,7 +852,7 @@ fn deal_included_in_multiple_sectors_failure() { owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // @@ -973,7 +972,7 @@ fn create_miner_and_upgrade_sector( store: &MemoryBlockstore, ) -> (VM, SectorOnChainInfo, Address, Address, u64, u64, SectorSize) { let mut v = VM::new_with_singletons(store); - let addrs = create_accounts(&v, 1, TokenAmount::from(100_000e18 as i128)); + let addrs = create_accounts(&v, 1, TokenAmount::from_whole(100_000)); let (worker, owner) = (addrs[0], addrs[0]); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; let (maddr, robust) = create_miner( @@ -981,7 +980,7 @@ fn create_miner_and_upgrade_sector( owner, worker, seal_proof.registered_window_post_proof().unwrap(), - TokenAmount::from(10_000e18 as i128), + TokenAmount::from_whole(10_000), ); // advance to have seal randomness epoch in the past @@ -1101,13 +1100,13 @@ fn create_sector( } fn create_deals( - num_deals: u128, + num_deals: u32, v: &VM, client: Address, worker: Address, maddr: Address, ) -> Vec { - let collateral = TokenAmount::from(3 * num_deals * 1e18 as u128); + let collateral = TokenAmount::from_whole(3 * num_deals as i64); apply_ok( v, client, diff --git a/test_vm/tests/terminate_test.rs b/test_vm/tests/terminate_test.rs index 93f92bf7b..36ddff82e 100644 --- a/test_vm/tests/terminate_test.rs +++ b/test_vm/tests/terminate_test.rs @@ -36,12 +36,12 @@ use test_vm::{ExpectInvocation, VM}; fn terminate_sectors() { let store = MemoryBlockstore::new(); let mut v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 4, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 4, TokenAmount::from_whole(10_000)); let (owner, verifier, unverified_client, verified_client) = (addrs[0], addrs[1], addrs[2], addrs[3]); let worker = owner; - let miner_balance = TokenAmount::from(1_000e18 as i128); + let miner_balance = TokenAmount::from_whole(1_000); let sector_number = 100; let sealed_cid = make_sealed_cid(b"s100"); let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; @@ -71,7 +71,7 @@ fn terminate_sectors() { ); // add market collateral - let collateral = TokenAmount::from(3e18 as u64); + let collateral = TokenAmount::from_whole(3); apply_ok( &v, unverified_client, @@ -89,7 +89,7 @@ fn terminate_sectors() { verified_client, ); - let miner_collateral = TokenAmount::from(64e18 as u128); + let miner_collateral = TokenAmount::from_whole(64); apply_ok( &v, worker, @@ -324,7 +324,7 @@ fn terminate_sectors() { ); // because of rounding error it's annoying to compute exact withdrawable balance which is 2.9999.. FIL // withdrawing 2 FIL proves out that the claim to 1 FIL per deal (2 deals for this client) is removed at termination - let withdrawal = TokenAmount::from(2e18 as u64); + let withdrawal = TokenAmount::from_whole(2); apply_ok( &v, verified_client, @@ -359,8 +359,8 @@ fn terminate_sectors() { // miner add 64 balance. Each of 3 deals required 2 FIL collateral, so provider collateral should have been // slashed by 6 FIL. Miner's remaining market balance should be 64 - 6 + payment, where payment is for storage // before the slash and should be << 1 FIL. Actual amount withdrawn should be between 58 and 59 FIL. - assert!(TokenAmount::from(58e18 as u128) < value_withdrawn); - assert!(TokenAmount::from(59e18 as u128) > value_withdrawn); + assert!(TokenAmount::from_whole(58) < value_withdrawn); + assert!(TokenAmount::from_whole(59) > value_withdrawn); v.expect_state_invariants( &[invariant_failure_patterns::REWARD_STATE_EPOCH_MISMATCH.to_owned()], diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 0c3b0081d..1e17fe49c 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -6,6 +6,7 @@ use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; +use num_traits::Zero; use test_vm::util::pk_addrs_from; use test_vm::{actor, FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR, VM}; @@ -17,13 +18,14 @@ fn state_control() { let addr2 = Address::new_id(2222); // set actor - let a1 = actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from(10u8)); + let a1 = + actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from_atto(10u8)); v.set_actor(addr1, a1.clone()); let out = v.get_actor(addr1).unwrap(); assert_eq!(out, a1); let check = v.checkpoint(); - let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from(1u8)); + let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from_atto(1u8)); v.set_actor(addr2, a2.clone()); assert_eq!(v.get_actor(addr2).unwrap(), a2); // rollback removes a2 but not a1 @@ -63,53 +65,53 @@ fn test_sent() { v.apply_message( TEST_FAUCET_ADDR, addr1, - TokenAmount::from(42u8), + TokenAmount::from_atto(42u8), METHOD_SEND, RawBytes::default(), ) .unwrap(); let expect_id_addr1 = Address::new_id(FIRST_TEST_USER_ADDR); - assert_account_actor(0, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); + assert_account_actor(0, TokenAmount::from_atto(42u8), addr1, &v, expect_id_addr1); // send from this account actor to another uninit account actor let addr2 = Address::new_bls(&[2; fvm_shared::address::BLS_PUB_LEN]).unwrap(); - v.apply_message(addr1, addr2, TokenAmount::from(41u8), METHOD_SEND, RawBytes::default()) + v.apply_message(addr1, addr2, TokenAmount::from_atto(41u8), METHOD_SEND, RawBytes::default()) .unwrap(); let expect_id_addr2 = Address::new_id(FIRST_TEST_USER_ADDR + 1); - assert_account_actor(0, TokenAmount::from(41u8), addr2, &v, expect_id_addr2); + assert_account_actor(0, TokenAmount::from_atto(41u8), addr2, &v, expect_id_addr2); // send between two initialized account actors - v.apply_message(addr2, addr1, TokenAmount::from(41u8), METHOD_SEND, RawBytes::default()) + v.apply_message(addr2, addr1, TokenAmount::from_atto(41u8), METHOD_SEND, RawBytes::default()) .unwrap(); - assert_account_actor(1, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); - assert_account_actor(1, TokenAmount::from(0u8), addr2, &v, expect_id_addr2); + assert_account_actor(1, TokenAmount::from_atto(42u8), addr1, &v, expect_id_addr1); + assert_account_actor(1, TokenAmount::zero(), addr2, &v, expect_id_addr2); // self send is noop - v.apply_message(addr1, addr1, TokenAmount::from(1u8), METHOD_SEND, RawBytes::default()) + v.apply_message(addr1, addr1, TokenAmount::from_atto(1u8), METHOD_SEND, RawBytes::default()) .unwrap(); - assert_account_actor(2, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); + assert_account_actor(2, TokenAmount::from_atto(42u8), addr1, &v, expect_id_addr1); // fail with insufficient funds let mres = v - .apply_message(addr2, addr1, TokenAmount::from(1u8), METHOD_SEND, RawBytes::default()) + .apply_message(addr2, addr1, TokenAmount::from_atto(1u8), METHOD_SEND, RawBytes::default()) .unwrap(); assert_eq!(ExitCode::SYS_INSUFFICIENT_FUNDS, mres.code); - assert_account_actor(2, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); - assert_account_actor(2, TokenAmount::from(0u8), addr2, &v, expect_id_addr2); + assert_account_actor(2, TokenAmount::from_atto(42u8), addr1, &v, expect_id_addr1); + assert_account_actor(2, TokenAmount::zero(), addr2, &v, expect_id_addr2); // fail to send to non existent id actor (vm doesn't create those on send) let mres = v .apply_message( addr1, Address::new_id(88), - TokenAmount::from(1u8), + TokenAmount::from_atto(1u8), METHOD_SEND, RawBytes::default(), ) .unwrap(); assert_eq!(ExitCode::SYS_INVALID_RECEIVER, mres.code); - assert_account_actor(3, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); - assert_account_actor(2, TokenAmount::from(0u8), addr2, &v, expect_id_addr2); + assert_account_actor(3, TokenAmount::from_atto(42u8), addr1, &v, expect_id_addr1); + assert_account_actor(2, TokenAmount::zero(), addr2, &v, expect_id_addr2); v.assert_state_invariants(); } diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 70f95654d..c32607197 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -22,7 +22,7 @@ use test_vm::{ExpectInvocation, TEST_VERIFREG_ROOT_ADDR, VM}; fn remove_datacap_simple_successful_path() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); - let addrs = create_accounts(&v, 4, TokenAmount::from(10_000e18 as i128)); + let addrs = create_accounts(&v, 4, TokenAmount::from_whole(10_000)); let (verifier1, verifier2, verified_client) = (addrs[0], addrs[1], addrs[2]); let verifier1_id_addr = v.normalize_address(&verifier1).unwrap(); From 10fae60e3822b19f1b01406f61f587a338eb4010 Mon Sep 17 00:00:00 2001 From: raulk Date: Thu, 1 Sep 2022 15:50:59 +0100 Subject: [PATCH 029/339] EVM runtime: implement some contextual opcodes + EVM Address type (#586) * refactor: move opcode handlers to appropriate modules. * implement BASEFEE opcode handler. * implement SELFBALANCE opcode handler. * EVM runtime: add an initial Address type that performs ID address masking. * EVM runtime: convert EVM errors to ActorErrors. * EVM runtime: commented selfdestruct. * EVM runtime: implement DIFFICULTY, NUMBER, COINBASE opcodes. * EVM runtime: add conversion from actor ID to masked address. Make CALLER, ADDRESS, ORIGIN use masked ID addresses. * remove TODO. * add comment. * fix clippy. * adjust test for ID address flag. --- actors/evm/src/interpreter/address.rs | 121 ++++++++++++++++++ actors/evm/src/interpreter/execution.rs | 24 ++-- .../src/interpreter/instructions/context.rs | 57 +++++---- .../evm/src/interpreter/instructions/ext.rs | 35 +++++ .../src/interpreter/instructions/lifecycle.rs | 36 ++++++ .../src/interpreter/instructions/memory.rs | 11 +- .../evm/src/interpreter/instructions/mod.rs | 3 + .../evm/src/interpreter/instructions/state.rs | 24 ++++ .../src/interpreter/instructions/storage.rs | 48 ------- .../evm/src/interpreter/instructions/table.rs | 0 actors/evm/src/interpreter/mod.rs | 1 + actors/evm/src/interpreter/output.rs | 10 ++ actors/evm/src/interpreter/uints.rs | 3 +- actors/evm/tests/basic.rs | 1 + 14 files changed, 277 insertions(+), 97 deletions(-) create mode 100644 actors/evm/src/interpreter/address.rs create mode 100644 actors/evm/src/interpreter/instructions/ext.rs create mode 100644 actors/evm/src/interpreter/instructions/lifecycle.rs create mode 100644 actors/evm/src/interpreter/instructions/state.rs delete mode 100644 actors/evm/src/interpreter/instructions/table.rs diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs new file mode 100644 index 000000000..cda664c94 --- /dev/null +++ b/actors/evm/src/interpreter/address.rs @@ -0,0 +1,121 @@ +use crate::interpreter::{H160, H256}; +use crate::U256; +use fvm_shared::address::Address as FilecoinAddress; + +/// A Filecoin address as represented in the FEVM runtime (also called EVM-form). +/// +/// TODO this type will eventually handle f4 address detection. +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Address(H160); + +impl From for Address { + fn from(v: U256) -> Self { + Self(H256(v.into()).into()) + } +} + +impl Address { + /// Expect a Filecoin address type containing an ID address, and return an address in EVM-form. + pub fn from_id_address(addr: &FilecoinAddress) -> Option
{ + addr.id().ok().map(Address::from_id) + } + + /// Returns an EVM-form ID address from actor ID. + pub fn from_id(id: u64) -> Address { + let mut bytes = [0u8; 20]; + bytes[0] = 0xff; + bytes[12..].copy_from_slice(&id.to_be_bytes()); + Address(bytes.try_into().unwrap()) + } + + /// Interpret the hash 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_address(&self) -> Option { + let val = &self.0 .0; + if (val[0] != 0xff) || !val[1..12].iter().all(|&byte| byte == 0) { + return None; + } + Some(FilecoinAddress::new_id(u64::from_be_bytes(val[12..].try_into().unwrap()))) + } + + /// Returns this Address as an EVM word. + pub fn as_evm_word(&self) -> U256 { + U256::from(&self.0[..]) + } +} + +#[cfg(test)] +mod tests { + use crate::interpreter::address::Address; + use crate::U256; + use fvm_shared::address::Address as FilecoinAddress; + + const TYPE_PADDING: &[u8] = &[0; 12]; // padding preceding H160 (12 bytes) + 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 = Address::from(U256::from(evm_bytes.as_slice())); + assert_eq!( + evm_addr.as_id_address(), + $expectation + ); + + // test inverse conversion, if a valid ID address was supplied + if let Some(fil_addr) = $expectation { + assert_eq!(Address::from_id_address(&fil_addr), Some(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(FilecoinAddress::new_id(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(FilecoinAddress::new_id(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/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index a831f5c33..8fa0e293d 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -268,7 +268,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } BALANCE(m) { - storage::balance(m.runtime, m.system)?; + state::balance(m.runtime, m.system)?; Ok(ControlFlow::Continue) } @@ -318,12 +318,12 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } EXTCODESIZE(m) { - storage::extcodesize(m.runtime, m.system)?; + ext::extcodesize(m.runtime, m.system)?; Ok(ControlFlow::Continue) } EXTCODECOPY(m) { - memory::extcodecopy(m.runtime, m.system)?; + ext::extcodecopy(m.runtime, m.system)?; Ok(ControlFlow::Continue) } @@ -338,7 +338,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } EXTCODEHASH(m) { - storage::extcodehash(m.runtime, m.system)?; + ext::extcodehash(m.runtime, m.system)?; Ok(ControlFlow::Continue) } @@ -348,7 +348,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } COINBASE(m) { - context::coinbase(m.runtime, m.system)?; + context::coinbase(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -358,12 +358,12 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } NUMBER(m) { - context::block_number(m.runtime, m.system)?; + context::block_number(m.runtime, m.system); Ok(ControlFlow::Continue) } DIFFICULTY(m) { - context::difficulty(m.runtime, m.system)?; + context::difficulty(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -378,12 +378,12 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } SELFBALANCE(m) { - storage::selfbalance(m.runtime, m.system)?; + state::selfbalance(m.runtime, m.system); Ok(ControlFlow::Continue) } BASEFEE(m) { - context::base_fee(m.runtime, m.system)?; + context::base_fee(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -797,7 +797,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } CREATE(m) { - storage::create(m.runtime, m.system, false)?; + lifecycle::create(m.runtime, m.system, false)?; Ok(ControlFlow::Continue) } @@ -822,7 +822,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } CREATE2(m) { - storage::create(m.runtime, m.system, true)?; + lifecycle::create(m.runtime, m.system, true)?; Ok(ControlFlow::Continue) } @@ -842,7 +842,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } SELFDESTRUCT(m) { - storage::selfdestruct(m.runtime, m.system)?; + lifecycle::selfdestruct(m.runtime, m.system)?; Ok(ControlFlow::Continue) } } diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 8a7bb64f8..3bde29c60 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,3 +1,4 @@ +use crate::interpreter::address::Address; use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, @@ -9,7 +10,7 @@ pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!() + todo!("requires the client passing down the inclusion tipset hash") } #[inline] @@ -17,39 +18,43 @@ pub fn caller<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.message().caller().id().unwrap())) + let id = platform.rt.message().caller().id().unwrap(); + state.stack.push(Address::from_id(id).as_evm_word()) } #[inline] -pub fn call_value<'r, BS: Blockstore, RT: Runtime>( +pub fn address<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(&platform.rt.message().value_received())); + let id = platform.rt.message().receiver().id().unwrap(); + state.stack.push(Address::from_id(id).as_evm_word()) } #[inline] -pub fn address<'r, BS: Blockstore, RT: Runtime>( +pub fn origin<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.message().receiver().id().unwrap())) + let id = platform.rt.message().origin().id().unwrap(); + state.stack.push(Address::from_id(id).as_evm_word()) } #[inline] -pub fn origin<'r, BS: Blockstore, RT: Runtime>( +pub fn call_value<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.message().origin().id().unwrap())) + state.stack.push(U256::from(&platform.rt.message().value_received())); } #[inline] pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, + state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() +) { + // TODO do we want to return the zero ID address, or just a plain 0? + state.stack.push(U256::zero()) } #[inline] @@ -57,7 +62,7 @@ pub fn gas_price<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!() + todo!("should return priority fee (needs syscall) + basefee") } #[inline] @@ -65,23 +70,23 @@ pub fn timestamp<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!() + todo!("should return the timestamp from the block header (requires syscall and FFI change)") } #[inline] pub fn block_number<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.curr_epoch())) } #[inline] pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, + state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() +) { + state.stack.push(U256::zero()) } #[inline] @@ -89,7 +94,7 @@ pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!() + todo!("requires a syscall") } #[inline] @@ -97,13 +102,13 @@ pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!() + todo!("requires chain ID registration and configuration in the client") } #[inline] pub fn base_fee<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(&platform.rt.base_fee())) } diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs new file mode 100644 index 000000000..85c9df959 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -0,0 +1,35 @@ +use { + crate::interpreter::{ExecutionState, StatusCode, System}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + // TODO + // 1. call actor::get_actor_code_cid + // 2. check that it matches our code CID (it's an EVM actor) + // 3. call GetEvmBytecode method, returns the CID of the EVM bytecode block + // 4. open the block + // 5. return the length + todo!() +} + +pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + // TODO + + todo!(); +} + +pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!(); +} diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs new file mode 100644 index 000000000..c09fe6066 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -0,0 +1,36 @@ +use { + crate::interpreter::{ExecutionState, StatusCode, System}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn create<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, + _create2: bool, +) -> Result<(), StatusCode> { + todo!() +} + +#[inline] +pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + // Commented due to horrible borrow checker issues that stem from owning a HAMT during + // the entire execution inside the System. The HAMT needs to be flushed and dropped when + // create_actor, delete_actor, and send are called. All other methods taking a &mut self + // on the Runtime should not require &mut. + // + // let beneficiary_addr = Address::from(state.stack.pop()); + // + // if let Some(id_addr) = beneficiary_addr.as_id_address() { + // platform.rt.delete_actor(&id_addr)?; + // } else { + // todo!("no support for non-ID addresses") + // } + // + // Ok(()) + todo!() +} diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 9aee8bd1a..f914e7936 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -1,9 +1,7 @@ #!allow[clippy::result-unit-err] use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, - fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, + crate::interpreter::{ExecutionState, StatusCode, U256}, std::num::NonZeroUsize, }; @@ -114,10 +112,3 @@ pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { pub fn msize(state: &mut ExecutionState) { state.stack.push(u64::try_from(state.memory.len()).unwrap().into()); } - -pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!(); -} diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 4955e5da9..fa818f3f6 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -4,8 +4,11 @@ pub mod boolean; pub mod call; pub mod context; pub mod control; +pub mod ext; pub mod hash; +pub mod lifecycle; pub mod log; pub mod memory; pub mod stack; +pub mod state; pub mod storage; diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs new file mode 100644 index 000000000..6fe8c4c4c --- /dev/null +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -0,0 +1,24 @@ +use crate::U256; +use { + crate::interpreter::{ExecutionState, StatusCode, System}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, +}; + +#[inline] +pub fn balance<'r, BS: Blockstore, RT: Runtime>( + _state: &mut ExecutionState, + _platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + todo!("requires syscall") +} + +#[inline] +pub fn selfbalance<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + // Returns native FIL balance of the receiver. Value precision is identical to Ethereum, so + // no conversion needed (atto, 1e18). + state.stack.push(U256::from(&platform.rt.current_balance())) +} diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index be3007c3e..aa5198a09 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -33,51 +33,3 @@ pub fn sstore<'r, BS: Blockstore, RT: Runtime>( platform.set_storage(location, opt_value)?; Ok(()) } - -#[inline] -pub fn balance<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() -} - -#[inline] -pub fn selfbalance<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() -} - -#[inline] -pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() -} - -pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!(); -} - -#[inline] -pub fn create<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, - _create2: bool, -) -> Result<(), StatusCode> { - todo!() -} - -#[inline] -pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!() -} diff --git a/actors/evm/src/interpreter/instructions/table.rs b/actors/evm/src/interpreter/instructions/table.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 68250f0e0..b3bd885a4 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -1,3 +1,4 @@ +pub mod address; pub mod bytecode; pub mod execution; pub mod instructions; diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index c628c076e..df82a7a39 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,3 +1,4 @@ +use fil_actors_runtime::ActorError; use { bytes::Bytes, fvm_ipld_encoding::Cbor, @@ -107,4 +108,13 @@ pub enum StatusCode { InternalError(String), } +// Map ActorError to a generic internal error status code. +// +// TODO need to figure out error handling. +impl From for StatusCode { + fn from(ae: ActorError) -> Self { + Self::InternalError(String::from(ae.msg())) + } +} + impl Cbor for StatusCode {} diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 5a4dc7803..db355d85c 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -4,7 +4,7 @@ #![allow(clippy::ptr_offset_with_cast, clippy::assign_op_pattern)] use { - fixed_hash::construct_fixed_hash, + fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}, fvm_shared::econ::TokenAmount, impl_serde::{impl_fixed_hash_serde, impl_uint_serde}, std::cmp::Ordering, @@ -16,6 +16,7 @@ construct_uint! { pub struct U512(8); } // used for addmod and mulmod opcodes construct_fixed_hash! { pub struct H160(20); } // ethereum address construct_fixed_hash! { pub struct H256(32); } // Keccak256 +impl_fixed_hash_conversions!(H256, H160); impl From<&TokenAmount> for U256 { fn from(amount: &TokenAmount) -> U256 { diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index efe213b96..68b9f1941 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -57,6 +57,7 @@ fn basic_contract_construction_and_invocation() { 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); From 6310cdd5a1a6299367b748b5613362e642e2ae61 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 2 Sep 2022 10:05:35 -0700 Subject: [PATCH 030/339] EVM Precompiles (#564) * add precompiles from old branc evm-precompiles * move precompiles file, add blake2 dep with blake2f * wip call fn, use precompile error type * use u256 instead of raw bytes (cleans up code) * remove Message struct as is no longer needed * attempt getting input & output memory regions * dont use custom error type, pass in just memory instead of full execution state for memory functions * write output back into smart contract remove old TODOs * add blake2f precompile * edit TODOs * rustfmt * add modxp precompile & remove gas limit/accouting for precompiles * add expairing, use custom error type, remove gas accounting, replace unsafe code * cleanup unused things and rename precompile error variant * use fork for blakef2, organize precompiles in file * move static call type to enum * add warnings and use different slicing method * rustfmt * fix errors from merge * use InvalidMemAccess again instead of OutOfGas * lock ref-fvm to specific hash to avoid Token struct changes * go back to normal EVM precompile addresssing, unlock ref-fvm version hash * add blake2 and ec ops precompile tests * fix precompile test errors issues * Update precompiles - use methods on runtime for syscalls instead of importing SDK - fix broken tests - fix some EC methods that would expect empty bytes instead of error * implement hash and recover pubkey in tests * import sepc consts * rustfmt & clippy * Update actors/evm/src/interpreter/precompiles.rs Co-authored-by: Steven Allen Co-authored-by: Steven Allen --- Cargo.lock | 44 +- actors/evm/Cargo.toml | 3 +- actors/evm/src/interpreter/execution.rs | 10 +- .../evm/src/interpreter/instructions/call.rs | 80 ++- .../src/interpreter/instructions/control.rs | 6 +- .../evm/src/interpreter/instructions/hash.rs | 2 +- .../src/interpreter/instructions/memory.rs | 25 +- actors/evm/src/interpreter/message.rs | 73 -- actors/evm/src/interpreter/mod.rs | 3 +- actors/evm/src/interpreter/precompiles.rs | 638 ++++++++++++++++++ actors/evm/src/interpreter/system.rs | 8 +- actors/evm/src/interpreter/uints.rs | 2 +- .../tests/miner_actor_test_construction.rs | 2 +- actors/miner/tests/util.rs | 9 +- runtime/Cargo.toml | 1 + runtime/src/runtime/fvm.rs | 17 +- runtime/src/runtime/mod.rs | 14 +- runtime/src/test_utils.rs | 61 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 31 +- 20 files changed, 898 insertions(+), 133 deletions(-) delete mode 100644 actors/evm/src/interpreter/message.rs create mode 100644 actors/evm/src/interpreter/precompiles.rs diff --git a/Cargo.lock b/Cargo.lock index 88dd61b73..e0ca83840 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -447,9 +447,9 @@ dependencies = [ [[package]] name = "cs_serde_bytes" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b5b4802671350eaced846f9fa82233c214b5d358ea85289b81c3aed58a0c6c" +checksum = "5fc673ddabf48214550526b068dc28065a75f05e21e452880095247c635b1d91" dependencies = [ "serde", ] @@ -669,7 +669,6 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_sdk", "fvm_shared", "hex", "hex-literal", @@ -677,6 +676,7 @@ dependencies = [ "libsecp256k1", "log", "multihash", + "near-blake2", "num-derive", "num-traits", "rlp", @@ -685,6 +685,7 @@ dependencies = [ "sha3", "strum", "strum_macros", + "substrate-bn", "uint", ] @@ -886,6 +887,7 @@ dependencies = [ "integer-encoding", "itertools", "lazy_static", + "libsecp256k1", "log", "multihash", "num-derive", @@ -1395,6 +1397,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" @@ -1526,6 +1531,16 @@ dependencies = [ "synstructure", ] +[[package]] +name = "near-blake2" +version = "0.9.1" +source = "git+https://github.com/filecoin-project/near-blake2.git#47a58e5061ba6d6c1132f535188b7fde2f67bcb2" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "num" version = "0.4.0" @@ -1965,14 +1980,20 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2004,6 +2025,19 @@ dependencies = [ "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" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 174be0f69..75ac68026 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,6 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_sdk = { version = "3.0.0-alpha.1" } fvm_shared = { version = "3.0.0-alpha.1", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } @@ -39,6 +38,8 @@ fixed-hash = { version = "0.7.0", default-features = false } impl-serde = { version = "0.3.2", default-features = false } arrayvec = { version = "0.7.2", features = ["serde"] } hex = "0.4.3" +substrate-bn = "0.6.0" +near-blake2 = {version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git"} [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 8fa0e293d..e08e8534b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -3,8 +3,8 @@ use { super::instructions::*, super::opcode::OpCode, - super::CallKind, super::StatusCode, + crate::interpreter::instructions::call::CallKind, crate::interpreter::instructions::log::*, crate::interpreter::instructions::stack::*, crate::interpreter::memory::Memory, @@ -802,12 +802,12 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } CALL(m) { - call::call(m.runtime, m.system, CallKind::Call, false)?; + call::call(m.runtime, m.system, CallKind::Call)?; Ok(ControlFlow::Continue) } CALLCODE(m) { - call::call(m.runtime, m.system, CallKind::CallCode, false)?; + call::call(m.runtime, m.system, CallKind::CallCode)?; Ok(ControlFlow::Continue) } @@ -817,7 +817,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } DELEGATECALL(m) { - call::call(m.runtime, m.system, CallKind::DelegateCall, false)?; + call::call(m.runtime, m.system, CallKind::DelegateCall)?; Ok(ControlFlow::Continue) } @@ -827,7 +827,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } STATICCALL(m) { - call::call(m.runtime, m.system, CallKind::Call, true)?; + call::call(m.runtime, m.system, CallKind::StaticCall)?; Ok(ControlFlow::Continue) } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 809385025..ffc553064 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,10 +1,27 @@ use { - super::memory::get_memory_region, crate::interpreter::output::StatusCode, - crate::interpreter::stack::Stack, crate::interpreter::CallKind, - crate::interpreter::ExecutionState, crate::interpreter::System, crate::interpreter::U256, - fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, + super::memory::get_memory_region, + crate::interpreter::instructions::memory::MemoryRegion, + crate::interpreter::output::StatusCode, + crate::interpreter::precompiles, + crate::interpreter::stack::Stack, + crate::interpreter::ExecutionState, + crate::interpreter::System, + crate::interpreter::{H160, U256}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, }; +/// The kind of call-like instruction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CallKind { + Call, + DelegateCall, + StaticCall, + CallCode, + Create, + Create2 { salt: U256 }, +} + #[inline] pub fn calldataload(state: &mut ExecutionState) { let index = state.stack.pop(); @@ -36,8 +53,8 @@ pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = - get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, mem_index, size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = ®ion { let input_len = U256::from(state.input_data.len()); @@ -69,8 +86,8 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = - get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, mem_index, size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; if let Some(region) = region { let src = core::cmp::min(U256::from(code.len()), input_index).as_usize(); @@ -91,10 +108,51 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod #[inline] pub fn call<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, _kind: CallKind, - _is_static: bool, ) -> Result<(), StatusCode> { + let ExecutionState { stack, memory, .. } = state; + let rt = platform.rt; + + let _gas = stack.pop(); // EVM gas is not used in FVM + let dst: H160 = crate::interpreter::uints::_u256_to_address(stack.pop()); + let input_offset = stack.pop(); + let input_size = stack.pop(); + let output_offset = stack.pop(); + let output_size = stack.pop(); + + // XXX do we need this? + stack.push(U256::zero()); // Assume failure. + + // TODO Errs + let input_region = get_memory_region(memory, input_offset, input_size).unwrap(); + let output_region = get_memory_region(memory, output_offset, output_size).unwrap(); + + let output = { + // ref to memory is dropped after calling so we can mutate it on output later + let input_data = input_region + .map(|MemoryRegion { offset, size }| &memory[offset..][..size.get()]) + .unwrap_or_default(); + + let output = if precompiles::Precompiles::::is_precompile(&dst) { + precompiles::Precompiles::call_precompile(rt, dst, input_data) + } else { + todo!() + }; + + output.unwrap() + }; + + let output_data = output_region + .map(|MemoryRegion { offset, size }| { + &mut memory[offset..][..size.get()] // would like to use get for this to err instead of panic + }) + .unwrap_or_default(); + + // TODO errs + output_data.get_mut(..output.len()).unwrap().copy_from_slice(&output); + + // TODO do things after writing into output todo!(); } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 6b323449f..176718c8b 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -9,7 +9,7 @@ pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { let offset = *state.stack.get(0); let size = *state.stack.get(1); - if let Some(region) = super::memory::get_memory_region(state, offset, size) + if let Some(region) = super::memory::get_memory_region(&mut state.memory, offset, size) .map_err(|_| StatusCode::InvalidMemoryAccess)? { state.output_data = @@ -30,8 +30,8 @@ pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = - get_memory_region(state, mem_index, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, mem_index, size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; if input_index > U256::from(state.return_data.len()) { return Err(StatusCode::InvalidMemoryAccess); diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 25a6ab5f0..732bc6d35 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -10,7 +10,7 @@ pub fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(state, index, size) // + let region = get_memory_region(&mut state.memory, index, size) // .map_err(|_| StatusCode::InvalidMemoryAccess)?; state.stack.push(U256::from_big_endian(&*Keccak256::digest(if let Some(region) = region { diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index f914e7936..08ce43d09 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -1,6 +1,7 @@ #!allow[clippy::result-unit-err] use { + crate::interpreter::memory::Memory, crate::interpreter::{ExecutionState, StatusCode, U256}, std::num::NonZeroUsize, }; @@ -21,15 +22,15 @@ pub fn num_words(size_in_bytes: usize) -> usize { } #[inline] -fn grow_memory(state: &mut ExecutionState, new_size: usize) -> Result<(), ()> { +fn grow_memory(mem: &mut Memory, new_size: usize) -> Result<(), ()> { let new_words = num_words(new_size); - state.memory.grow((new_words * WORD_SIZE) as usize); + mem.grow((new_words * WORD_SIZE) as usize); Ok(()) } #[inline] fn get_memory_region_u64( - state: &mut ExecutionState, + mem: &mut Memory, offset: U256, size: NonZeroUsize, ) -> Result { @@ -38,9 +39,9 @@ fn get_memory_region_u64( } let new_size = offset.as_usize() + size.get(); - let current_size = state.memory.len(); + let current_size = mem.len(); if new_size > current_size { - grow_memory(state, new_size)?; + grow_memory(mem, new_size)?; } Ok(MemoryRegion { offset: offset.as_usize(), size }) @@ -49,7 +50,7 @@ fn get_memory_region_u64( #[inline] #[allow(clippy::result_unit_err)] pub fn get_memory_region( - state: &mut ExecutionState, + state: &mut Memory, offset: U256, size: U256, ) -> Result, ()> { @@ -68,8 +69,9 @@ pub fn get_memory_region( pub fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); - let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = + get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); @@ -83,8 +85,9 @@ pub fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); let value = state.stack.pop(); - let region = get_memory_region_u64(state, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = + get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; let mut bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut bytes); @@ -98,7 +101,7 @@ pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); let value = state.stack.pop(); - let region = get_memory_region_u64(state, index, NonZeroUsize::new(1).unwrap()) + let region = get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(1).unwrap()) .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = (value.low_u32() & 0xff) as u8; diff --git a/actors/evm/src/interpreter/message.rs b/actors/evm/src/interpreter/message.rs deleted file mode 100644 index 200cdc84e..000000000 --- a/actors/evm/src/interpreter/message.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![allow(dead_code)] - -use { - crate::interpreter::transaction::TransactionAction, - crate::interpreter::SignedTransaction, - crate::interpreter::{H160, U256}, - bytes::Bytes, - fil_actors_runtime::ActorError, - std::fmt::Debug, -}; - -/// The kind of call-like instruction. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CallKind { - Call, - DelegateCall, - CallCode, - Create, - Create2 { salt: U256 }, -} - -/// The message describing an EVM call, -/// including a zero-depth call from transaction origin. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Message { - /// The kind of the call. For zero-depth calls `CallKind::Call` SHOULD be - /// used. - pub kind: CallKind, - - /// Static call mode. - pub is_static: bool, - - /// The call depth. - pub depth: i32, - - /// The amount of gas for message execution. - pub gas: i64, - - /// The destination (recipient) of the message. - pub recipient: H160, - - /// The sender of the message. - pub sender: H160, - - /// Message input data. - pub input_data: Bytes, - - /// The amount of Ether transferred with the message. - pub value: U256, -} - -impl TryFrom for Message { - type Error = ActorError; - - fn try_from(tx: SignedTransaction) -> Result { - Ok(Message { - kind: match tx.transaction.action() { - TransactionAction::Call(_) => CallKind::Call, - TransactionAction::Create => CallKind::Create, - }, - is_static: false, - depth: 0, - gas: tx.transaction.gas_limit() as i64, - recipient: match tx.transaction.action() { - TransactionAction::Call(addr) => addr, - TransactionAction::Create => H160::zero(), - }, - sender: tx.sender_address()?, - input_data: tx.transaction.input(), - value: tx.transaction.value(), - }) - } -} diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index b3bd885a4..3892ccd44 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -3,9 +3,9 @@ pub mod bytecode; pub mod execution; pub mod instructions; pub mod memory; -pub mod message; pub mod opcode; pub mod output; +pub mod precompiles; pub mod stack; pub mod system; pub mod transaction; @@ -14,7 +14,6 @@ pub mod uints; pub use { bytecode::Bytecode, execution::{execute, ExecutionState}, - message::{CallKind, Message}, output::{Output, StatusCode}, system::System, transaction::{ diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs new file mode 100644 index 000000000..f1bdeaac6 --- /dev/null +++ b/actors/evm/src/interpreter/precompiles.rs @@ -0,0 +1,638 @@ +use std::{marker::PhantomData, ops::Mul}; + +use super::{H160, U256}; +use fil_actors_runtime::runtime::{Primitives, Runtime}; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::{ + bigint::BigUint, + crypto::{ + hash::SupportedHashes, + signature::{SECP_PUB_LEN, 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 uint::byteorder::{ByteOrder, LE}; + +#[derive(Debug, Eq, PartialEq)] +pub enum PrecompileError { + EcErr, + IncorrectInputSize, +} + +pub type PrecompileFn = fn(&RT, &[u8]) -> PrecompileResult; +pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec + +/// Generates a list of precompile smart contracts, index + 1 is the address (another option is to make an enum) +const fn gen_precompiles() -> [PrecompileFn; 9] { + [ + ec_recover, // ecrecover 0x01 + sha256, // SHA256 (Keccak) 0x02 + ripemd160, // ripemd160 0x03 + identity, // identity 0x04 + modexp, // modexp 0x05 + ec_add, // ecAdd 0x06 + ec_mul, // ecMul 0x07 + ec_pairing, // ecPairing 0x08 + blake2f, // blake2f 0x09 + ] +} + +pub struct Precompiles(PhantomData, PhantomData); + +impl> Precompiles { + const PRECOMPILES: [PrecompileFn; 9] = gen_precompiles(); + const MAX_PRECOMPILE: H160 = { + let mut bytes = [0u8; 20]; + bytes[0] = Self::PRECOMPILES.len() as u8; + H160(bytes) + }; + + pub fn call_precompile(runtime: &RT, precompile_addr: H160, input: &[u8]) -> PrecompileResult { + Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](runtime, input) + } + + pub fn is_precompile(addr: &H160) -> bool { + !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE + } +} + +/// read 32 bytes (u256) from buffer or error +fn read_u256(buf: &[u8], start: usize) -> Result { + let slice = buf.get(start..start + 32).ok_or(PrecompileError::IncorrectInputSize)?; + Ok(U256::from_big_endian(slice)) +} + +/// read 32 bytes (u256) from buffer, pass in exit reason that is desired +/// returns 0 if failed to read +fn read_u256_infalliable(buf: &[u8], start: usize) -> U256 { + let slice = buf.get(start..start + 32).unwrap_or(&[0u8; 32]); + U256::from_big_endian(slice) +} + +fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { + let mut buf = [0u8; 128]; + buf[..input.len().min(128)].copy_from_slice(&input[..input.len().min(128)]); + + let mut hash = [0u8; SECP_SIG_MESSAGE_HASH_SIZE]; + let mut sig = [0u8; SECP_SIG_LEN]; + + hash.copy_from_slice(&input[..32]); + sig.copy_from_slice(&input[64..]); // TODO this assumes input is exactly 65 bytes which would panic if incorrect + + // recovery byte means a single byte value is 32 bytes long, sad + if input[32..63] != [0u8; 31] || !matches!(input[63], 23 | 28) { + return Ok(Vec::new()); + } + sig[64] = input[63] - 27; + + let recovered = rt.recover_secp_public_key(&hash, &sig).unwrap_or([0u8; SECP_PUB_LEN]); + + Ok(recovered.to_vec()) +} + +fn sha256(rt: &RT, input: &[u8]) -> PrecompileResult { + Ok(rt.hash(SupportedHashes::Keccak256, input)) +} + +fn ripemd160(rt: &RT, input: &[u8]) -> PrecompileResult { + Ok(rt.hash(SupportedHashes::Ripemd160, input)) +} + +fn identity(_: &RT, input: &[u8]) -> PrecompileResult { + Ok(Vec::from(input)) +} + +// https://eips.ethereum.org/EIPS/eip-198 +fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { + let base_len = read_u256(input, 0)?.as_usize(); + let exponent_len = read_u256(input, 32)?.as_usize(); + let mod_len = read_u256(input, 64)?.as_usize(); + + if mod_len == 0 { + return Ok(Vec::new()); + } + + // TODO bounds checking + let mut start = 96; + let base = BigUint::from_bytes_be(&input[start..start + base_len]); + start += base_len; + let exponent = BigUint::from_bytes_be(&input[start..start + exponent_len]); + start += exponent_len; + let modulus = BigUint::from_bytes_be(&input[start..start + mod_len]); + + let mut output = if modulus.is_zero() || modulus.is_one() { + BigUint::zero().to_bytes_be() + } else { + base.modpow(&exponent, &modulus).to_bytes_be() + }; + + if output.len() < mod_len { + let mut ret = Vec::with_capacity(mod_len); + ret.extend(core::iter::repeat(0).take(mod_len - output.len())); + ret.extend_from_slice(&output); + output = ret; + } + + Ok(output) +} + +/// converts 2 byte arrays (U256) into a point on a field +/// exits with OutOfGas for any failed operation +fn uint_to_point(x: U256, y: U256) -> Result { + let x = Fq::from_u256(x.0.into()).map_err(|_| PrecompileError::EcErr)?; + let y = Fq::from_u256(y.0.into()).map_err(|_| PrecompileError::EcErr)?; + + Ok(if x.is_zero() && y.is_zero() { + G1::zero() + } else { + AffineG1::new(x, y).map_err(|_| PrecompileError::EcErr)?.into() + }) +} + +/// add 2 points together on `alt_bn128` +fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { + let x1 = read_u256_infalliable(input, 0); + let y1 = read_u256_infalliable(input, 32); + let point1 = uint_to_point(x1, y1)?; + + let x2 = read_u256_infalliable(input, 64); + let y2 = read_u256_infalliable(input, 96); + let point2 = uint_to_point(x2, y2)?; + + let output = AffineG1::from_jacobian(point1 + point2).map_or(vec![0; 64], |sum| { + let mut output = vec![0; 64]; + sum.x().to_big_endian(&mut output[..32]).unwrap(); + sum.y().to_big_endian(&mut output[32..]).unwrap(); + output + }); + + Ok(output) +} + +/// multiply a scalar and a point on `alt_bn128` +fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { + let x = read_u256_infalliable(input, 0); + let y = read_u256_infalliable(input, 32); + let point = uint_to_point(x, y)?; + + let scalar = if let Some(scalar) = input.get(64..96) { + Fr::from_slice(scalar).map_err(|_| PrecompileError::EcErr)? + } else { + return Ok(vec![0; 64]); + }; + + AffineG1::from_jacobian(point.mul(scalar)).ok_or(PrecompileError::EcErr).map(|product| { + let mut output = vec![0; 64]; + product.x().to_big_endian(&mut output[..32]).unwrap(); + product.y().to_big_endian(&mut output[32..]).unwrap(); + output + }) +} + +fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { + fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { + let x1 = read_u256(input, 0)?; + let y1 = read_u256(input, 32)?; + + let y2 = read_u256(input, 64)?; + let x2 = read_u256(input, 96)?; + let y3 = read_u256(input, 128)?; + let x3 = read_u256(input, 160)?; + + // TODO errs + let ax = Fq::from_u256(x1.0.into()).unwrap(); + let ay = Fq::from_u256(y1.0.into()).unwrap(); + + let twisted_ax = Fq::from_u256(x2.0.into()).unwrap(); + let twisted_ay = Fq::from_u256(y2.0.into()).unwrap(); + let twisted_bx = Fq::from_u256(x3.0.into()).unwrap(); + let twisted_by = Fq::from_u256(y3.0.into()).unwrap(); + + let twisted_a = Fq2::new(twisted_ax, twisted_ay); + let twisted_b = Fq2::new(twisted_bx, twisted_by); + + let twisted = { + if twisted_a.is_zero() && twisted_b.is_zero() { + G2::zero() + } else { + AffineG2::new(twisted_a, twisted_b).unwrap().into() + } + }; + + let a = { + if ax.is_zero() && ay.is_zero() { + substrate_bn::G1::zero() + } else { + AffineG1::new(ax, ay).unwrap().into() + } + }; + + Ok((a, twisted)) + } + + const GROUP_BYTE_LEN: usize = 192; + + 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 output = if accumulated == Gt::one() { U256::one() } else { U256::zero() }; + let mut buf = [0u8; 32]; + output.to_big_endian(&mut buf); + Ok(buf.to_vec()) +} + +// https://eips.ethereum.org/EIPS/eip-152 +fn blake2f(_: &RT, input: &[u8]) -> PrecompileResult { + if input.len() != 213 { + return Err(PrecompileError::IncorrectInputSize); + } + let mut hasher = near_blake2::VarBlake2b::default(); + + let mut rounds = [0u8; 4]; + + // 4 bytes + let mut start = 0; + 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 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 + }; + + hasher.blake2_f(rounds, h, m, t, f); + let output = hasher.output().to_vec(); + Ok(output) +} + +#[cfg(test)] +mod tests { + use super::*; + use fil_actors_runtime::test_utils::MockRuntime; + use hex_literal::hex; + + // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs + mod bn { + use super::MockRuntime; + use crate::interpreter::precompiles::{ec_add, ec_mul, PrecompileError}; + + #[test] + fn bn128_add() { + let rt = MockRuntime::default(); + + let input = hex::decode( + "\ + 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ + 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\ + 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ + 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + ) + .unwrap(); + let expected = hex::decode( + "\ + 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ + 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + ) + .unwrap(); + let res = ec_add(&rt, &input).unwrap(); + assert_eq!(res, expected); + // zero sum test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_add(&rt, &input).unwrap(); + assert_eq!(res, expected); + + // no input test + let input = []; + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_add(&rt, &input).unwrap(); + assert_eq!(res, expected); + // point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + let res = ec_add(&rt, &input); + assert!(matches!(res, Err(PrecompileError::EcErr))); + } + + #[test] + fn bn128_mul() { + let rt = MockRuntime::default(); + + let input = hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(); + let expected = hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + ) + .unwrap(); + let res = ec_mul(&rt, &input).unwrap(); + assert_eq!(res, expected); + + // out of gas test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0200000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_mul(&rt, &input); + assert_eq!(res, Err(PrecompileError::EcErr)); + + // no input test + let input = [0u8; 0]; + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_mul(&rt, &input).unwrap(); + assert_eq!(res, expected); + // point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 0f00000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_mul(&rt, &input); + assert!(matches!(res, Err(PrecompileError::EcErr))); + } + + // #[test] + // fn bn128_pair() { + // 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 = + // Bn128Pair::::run(&input, 260_000, &new_context(), false).unwrap().output; + // 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 = Bn128Pair::::run(&input, 259_999, &new_context(), false); + // assert!(matches!(res, Err(Return::OutOfGas))); + // // no input test + // let input = [0u8; 0]; + // let expected = + // hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + // .unwrap(); + // let res = + // Bn128Pair::::run(&input, 260_000, &new_context(), false).unwrap().output; + // assert_eq!(res, expected); + // // point not on curve fail + // let input = hex::decode( + // "\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111", + // ) + // .unwrap(); + // let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); + // assert!(matches!(res, Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_A"))))); + // // invalid input length + // let input = hex::decode( + // "\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 1111111111111111111111111111111111111111111111111111111111111111\ + // 111111111111111111111111111111\ + // ", + // ) + // .unwrap(); + // let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); + // assert!(matches!(res, Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_LEN",))))); + // } + } + + // https://eips.ethereum.org/EIPS/eip-152#test-cases + #[test] + fn blake2() { + use super::blake2f; + let rt = MockRuntime::default(); + + // // 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_eq!(blake2f(&rt, &[]), Err(PrecompileError::IncorrectInputSize)); + + // T1 too small + let input = &hex!( + "00000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + + // T2 too large + let input = &hex!( + "000000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + + // T3 final block indicator invalid + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + + // outputs + + // T4 + let expected = &hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + let input = &hex!( + "00000000" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + + // T5 + let expected = &hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + + // T6 + let expected = &hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "00" + ); + assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + + // T7 + let expected = &hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); + let input = &hex!( + "00000001" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + + // T8 + // NOTE: + // original test case ran ffffffff rounds of blake2b + // with an output of fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615 + // I ran this sucessfully 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_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + } +} diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 9bce708fa..64548b50e 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use { - crate::interpreter::{Message, StatusCode, U256}, + crate::interpreter::{StatusCode, U256}, cid::Cid, fil_actors_runtime::{runtime::Runtime, ActorError}, fvm_ipld_blockstore::Blockstore, @@ -23,12 +23,6 @@ pub enum StorageStatus { Deleted, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Call<'a> { - Call(&'a Message), - Create(&'a Message), -} - /// Platform Abstraction Layer /// that bridges the FVM world to EVM world pub struct System<'r, BS: Blockstore, RT: Runtime> { diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index db355d85c..cafb73258 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -120,7 +120,7 @@ pub fn u128_words_to_u256(high: u128, low: u128) -> U256 { } #[inline] -fn _u256_to_address(v: U256) -> H160 { +pub(crate) fn _u256_to_address(v: U256) -> H160 { let mut bytes = [0u8; 32]; v.to_big_endian(&mut bytes); H160::from_slice(&bytes) diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 9f8c770cd..96926bed3 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -46,7 +46,7 @@ fn prepare_env() -> TestEnv { env.rt.actor_code_cids.insert(env.worker, *ACCOUNT_ACTOR_CODE_ID); env.rt.actor_code_cids.insert(env.control_addrs[0], *ACCOUNT_ACTOR_CODE_ID); env.rt.actor_code_cids.insert(env.control_addrs[1], *ACCOUNT_ACTOR_CODE_ID); - env.rt.hash_func = Box::new(blake2b_256); + env.rt.hash_func = Box::new(hash); env.rt.caller = *INIT_ACTOR_ADDR; env.rt.caller_type = *INIT_ACTOR_CODE_ID; env diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index ec64b417c..baaad55a6 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -52,6 +52,7 @@ use fvm_shared::clock::QuantSpec; use fvm_shared::clock::{ChainEpoch, NO_QUANTIZATION}; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; use fvm_shared::consensus::ConsensusFault; +use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::deal::DealID; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -2490,13 +2491,13 @@ where // Returns a fake hashing function that always arranges the first 8 bytes of the digest to be the binary // encoding of a target uint64. -fn fixed_hasher(offset: ChainEpoch) -> Box [u8; 32]> { - let hash = move |_: &[u8]| -> [u8; 32] { - let mut result = [0u8; 32]; +fn fixed_hasher(offset: ChainEpoch) -> Box ([u8; 64], usize)> { + let hash = move |_, _: &[u8]| -> ([u8; 64], usize) { + let mut result = [0u8; 64]; for (i, item) in result.iter_mut().enumerate().take(8) { *item = ((offset >> (8 * (7 - i))) & 0xff) as u8; } - result + (result, 32) }; Box::new(hash) } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 8a44e88ea..8383eab80 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -36,6 +36,7 @@ rand = "0.8.5" serde_repr = "0.1.8" regex = "1" itertools = "0.10" +libsecp256k1 = { version = "0.7.1"} [dependencies.sha2] version = "0.10" diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index f7d71b5fb..9160e23e2 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -7,7 +7,9 @@ use fvm_sdk as fvm; use fvm_sdk::NO_DATA_BLOCK_ID; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; -use fvm_shared::crypto::signature::Signature; +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::error::{ErrorNumber, ExitCode}; use fvm_shared::piece::PieceInfo; @@ -426,6 +428,19 @@ where 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: fvm_shared::crypto::hash::SupportedHashes, data: &[u8]) -> Vec { + fvm::crypto::hash(hasher, data) + } + + fn recover_secp_public_key( + &self, + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { + fvm::crypto::recover_secp_public_key(hash, signature) + .map_err(|e| anyhow!("failed to recover pubkey; exit code: {}", e)) + } } #[cfg(not(feature = "fake-proofs"))] diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 6b0faabd5..1970b7116 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -7,7 +7,10 @@ use fvm_ipld_encoding::{Cbor, RawBytes}; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; -use fvm_shared::crypto::signature::Signature; +use fvm_shared::crypto::hash::SupportedHashes; +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::piece::PieceInfo; use fvm_shared::randomness::Randomness; @@ -184,6 +187,9 @@ pub trait Primitives { /// Hashes input data using blake2b with 256 bit output. fn hash_blake2b(&self, data: &[u8]) -> [u8; 32]; + /// Hashes input data using a supported hash function. + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec; + /// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes. fn compute_unsealed_sector_cid( &self, @@ -199,6 +205,12 @@ pub trait Primitives { plaintext: &[u8], ) -> Result<(), anyhow::Error>; + fn recover_secp_public_key( + &self, + 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>; } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 4375df332..3f551a9f9 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -16,7 +16,10 @@ use fvm_shared::address::{Address, Protocol}; use fvm_shared::clock::ChainEpoch; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; use fvm_shared::consensus::ConsensusFault; -use fvm_shared::crypto::signature::Signature; +use fvm_shared::crypto::hash::SupportedHashes; +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::error::ExitCode; use fvm_shared::piece::PieceInfo; @@ -39,6 +42,7 @@ use crate::runtime::{ Verifier, }; use crate::{actor_error, ActorError}; +use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; lazy_static! { pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system"); @@ -116,7 +120,14 @@ pub struct MockRuntime { pub origin: Address, pub value_received: TokenAmount, #[allow(clippy::type_complexity)] - pub hash_func: Box [u8; 32]>, + pub hash_func: Box ([u8; 64], usize)>, + #[allow(clippy::type_complexity)] + pub recover_pubkey_fn: Box< + dyn Fn( + &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], ()>, + >, pub network_version: NetworkVersion, // Actor State @@ -262,7 +273,8 @@ impl Default for MockRuntime { caller_type: Default::default(), origin: Address::new_id(0), value_received: Default::default(), - hash_func: Box::new(blake2b_256), + hash_func: Box::new(hash), + recover_pubkey_fn: Box::new(recover_secp_public_key), network_version: NetworkVersion::V0, state: Default::default(), balance: Default::default(), @@ -1068,8 +1080,17 @@ impl Primitives for MockRuntime { } fn hash_blake2b(&self, data: &[u8]) -> [u8; 32] { - (*self.hash_func)(data) + let (digest, _) = (*self.hash_func)(SupportedHashes::Blake2b256, data); + let mut ret = [0u8; 32]; + ret.copy_from_slice(&digest[..32]); + ret } + + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { + let (digest, len) = (*self.hash_func)(hasher, data); + Vec::from(&digest[..len]) + } + fn compute_unsealed_sector_cid( &self, reg: RegisteredSealProof, @@ -1101,6 +1122,14 @@ impl Primitives for MockRuntime { 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], + signature: &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { + (*self.recover_pubkey_fn)(hash, signature).map_err(|_| anyhow!("failed to recover pubkey.")) + } } impl Verifier for MockRuntime { @@ -1248,6 +1277,30 @@ pub fn blake2b_256(data: &[u8]) -> [u8; 32] { .unwrap() } +pub fn hash(hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + let hasher = Code::try_from(hasher as u64).unwrap(); + let (_, digest, written) = hasher.digest(data).into_inner(); + (digest, written as usize) +} + +#[allow(clippy::result_unit_err)] +pub fn recover_secp_public_key( + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], +) -> Result<[u8; SECP_PUB_LEN], ()> { + // generate types to recover key from + let rec_id = RecoveryId::parse(signature[64]).map_err(|_| ())?; + let message = Message::parse(hash); + + // Signature value without recovery byte + let mut s = [0u8; 64]; + s.copy_from_slice(signature[..64].as_ref()); + + // generate Signature + let sig = EcsdaSignature::parse_standard(&s).map_err(|_| ())?; + Ok(recover(&message, &sig, &rec_id).map_err(|_| ())?.serialize()) +} + // multihash library doesn't support poseidon hashing, so we fake it #[derive(Clone, Copy, Debug, PartialEq, Eq, Multihash)] #[mh(alloc_size = 64)] diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 4ca7cc34a..7ad2d77ae 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -44,11 +44,11 @@ anyhow = "1.0.56" bimap = { version = "0.6.2" } blake2b_simd = "1.0" integer-encoding = { version = "3.0.3", default-features = false } +multihash = { version = "0.16.3" } regex = "1" [dev-dependencies] cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } -multihash = { version = "0.16.1", default-features = false } [features] m2-native = [] diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 94e65677c..24fc3ca2e 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -38,7 +38,10 @@ use fvm_shared::address::{Address, Protocol}; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; -use fvm_shared::crypto::signature::Signature; +use fvm_shared::crypto::hash::SupportedHashes; +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::error::ExitCode; use fvm_shared::piece::PieceInfo; @@ -50,6 +53,7 @@ use fvm_shared::sector::{ use fvm_shared::smooth::FilterEstimate; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use multihash::MultihashDigest; use regex::Regex; use serde::ser; use std::cell::{RefCell, RefMut}; @@ -947,6 +951,19 @@ impl Primitives for VM<'_> { ) -> Result { Ok(make_piece_cid(b"unsealed from itest vm")) } + + 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).to_bytes() + } + + fn recover_secp_public_key( + &self, + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { + recover_secp_public_key(hash, signature).map_err(|_| anyhow!("failed to recover pubkey")) + } } impl Primitives for InvocationCtx<'_, '_> { @@ -975,6 +992,18 @@ impl Primitives for InvocationCtx<'_, '_> { 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) + } + + fn recover_secp_public_key( + &self, + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { + self.v.recover_secp_public_key(hash, signature) + } } impl Verifier for InvocationCtx<'_, '_> { From 970ed6a2531c90eecb8660201125b9e1e7ce4910 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Sep 2022 12:44:22 -0700 Subject: [PATCH 031/339] chore: update sdk (#598) We renamed hash to hash_owned to allow for a hash_into method. --- Cargo.lock | 4 ++-- runtime/Cargo.toml | 2 +- runtime/src/runtime/fvm.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0ca83840..00a1f36db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1177,9 +1177,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.1" +version = "3.0.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977dc7824079d057d149ea9b42ac50072928f5f37f941e07860eb2432f62fa1f" +checksum = "19f90eee234e07d5879e174167700a14bfcf2b87965fdacf94e98871a37126db" dependencies = [ "cid", "fvm_ipld_encoding", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 8383eab80..1ff0a7eb2 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -27,7 +27,7 @@ thiserror = "1.0.30" getrandom = { version = "0.2.5", features = ["js"] } hex = { version = "0.4.3", optional = true } anyhow = "1.0.56" -fvm_sdk = { version = "3.0.0-alpha.1", optional = true } +fvm_sdk = { version = "3.0.0-alpha.2", optional = true } blake2b_simd = "1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 9160e23e2..5db73860e 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -430,7 +430,7 @@ where } fn hash(&self, hasher: fvm_shared::crypto::hash::SupportedHashes, data: &[u8]) -> Vec { - fvm::crypto::hash(hasher, data) + fvm::crypto::hash_owned(hasher, data) } fn recover_secp_public_key( From a38d600f0b24af66276a455b1c1119fdfc75682b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Sep 2022 15:02:35 -0700 Subject: [PATCH 032/339] test runtime: use Rc. (#599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the real FvmRuntime, we expect that cloning the &Blockstore returned by Runtime#store() would simply clone the reference. This is because inside the FVM, blockstore calls proxy out to FVM syscalls; the proxy itself holds no state, and cloning it is unimportant. However, in the MockRuntime, we were using a physical MemoryBlockstore. Cloning it would return a new physical copy, thus breaking the expectation. This commit makes MockRuntime use an Rc, so all clones will point to the same MemoryBlockstore, thus preserving the expectation. Co-authored-by: Raúl Kripalani --- actors/market/tests/harness.rs | 2 +- actors/miner/tests/expiration_queue.rs | 2 +- actors/miner/tests/miner_actor_test_bitfield_queue.rs | 2 +- actors/verifreg/tests/harness/mod.rs | 4 ++-- runtime/src/test_utils.rs | 7 ++++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 8c815123c..547db7120 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -661,7 +661,7 @@ pub fn assert_deal_deleted(rt: &mut MockRuntime, deal_id: DealID, p: DealProposa let pending_deals: Hamt<&fvm_ipld_blockstore::MemoryBlockstore, DealProposal> = fil_actors_runtime::make_map_with_root_and_bitwidth( &st.pending_proposals, - &rt.store, + &*rt.store, PROPOSALS_AMT_BITWIDTH, ) .unwrap(); diff --git a/actors/miner/tests/expiration_queue.rs b/actors/miner/tests/expiration_queue.rs index 1000e3a8c..6119e9152 100644 --- a/actors/miner/tests/expiration_queue.rs +++ b/actors/miner/tests/expiration_queue.rs @@ -716,7 +716,7 @@ fn empty_expiration_queue_with_quantizing( let empty_array = Amt::<(), _>::new_with_bit_width(&rt.store, TEST_AMT_BITWIDTH).flush().unwrap(); - ExpirationQueue::new(&rt.store, &empty_array, quant).unwrap() + ExpirationQueue::new(&*rt.store, &empty_array, quant).unwrap() } fn empty_expiration_queue(rt: &MockRuntime) -> ExpirationQueue { diff --git a/actors/miner/tests/miner_actor_test_bitfield_queue.rs b/actors/miner/tests/miner_actor_test_bitfield_queue.rs index bdf3119dc..702cda83f 100644 --- a/actors/miner/tests/miner_actor_test_bitfield_queue.rs +++ b/actors/miner/tests/miner_actor_test_bitfield_queue.rs @@ -229,7 +229,7 @@ fn empty_bitfield_queue_with_quantizing( ) -> BitFieldQueue { let cid = Amt::<(), _>::new_with_bit_width(&rt.store, bitwidth).flush().unwrap(); - BitFieldQueue::new(&rt.store, &cid, quant).unwrap() + BitFieldQueue::new(&*rt.store, &cid, quant).unwrap() } fn empty_bitfield_queue(rt: &MockRuntime, bitwidth: u32) -> BitFieldQueue { diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 44488fc1a..b7760631a 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -217,7 +217,7 @@ impl Harness { fn load_verifiers(rt: &MockRuntime) -> Map { let state: State = rt.get_state(); - make_map_with_root_and_bitwidth::<_, BigIntDe>(&state.verifiers, &rt.store, HAMT_BIT_WIDTH) + make_map_with_root_and_bitwidth::<_, BigIntDe>(&state.verifiers, &*rt.store, HAMT_BIT_WIDTH) .unwrap() } @@ -225,7 +225,7 @@ fn load_clients(rt: &MockRuntime) -> Map { let state: State = rt.get_state(); make_map_with_root_and_bitwidth::<_, BigIntDe>( &state.verified_clients, - &rt.store, + &*rt.store, HAMT_BIT_WIDTH, ) .unwrap() diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 3f551a9f9..40c7cc761 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -4,6 +4,7 @@ use core::fmt; use std::cell::RefCell; use std::collections::{BTreeMap, HashMap, VecDeque}; +use std::rc::Rc; use anyhow::anyhow; use cid::multihash::{Code, Multihash as OtherMultihash}; @@ -136,7 +137,7 @@ pub struct MockRuntime { // VM Impl pub in_call: bool, - pub store: MemoryBlockstore, + pub store: Rc, pub in_transaction: bool, // Expectations @@ -688,7 +689,7 @@ impl MessageInfo for MockRuntime { } } -impl Runtime for MockRuntime { +impl Runtime> for MockRuntime { fn network_version(&self) -> NetworkVersion { self.network_version } @@ -907,7 +908,7 @@ impl Runtime for MockRuntime { ret } - fn store(&self) -> &MemoryBlockstore { + fn store(&self) -> &Rc { &self.store } From 739590e98a90d5ff29b3997c7579b3cb1bb40ac4 Mon Sep 17 00:00:00 2001 From: raulk Date: Fri, 2 Sep 2022 23:17:51 +0100 Subject: [PATCH 033/339] EVM: implement SELFDESTRUCT opcode. (#593) This required a small refactor to allow us to mutably borrow the Runtime. --- actors/evm/src/interpreter/execution.rs | 13 +++-- .../evm/src/interpreter/instructions/call.rs | 2 +- .../src/interpreter/instructions/lifecycle.rs | 24 +++------- .../src/interpreter/instructions/storage.rs | 4 +- actors/evm/src/interpreter/output.rs | 5 +- actors/evm/src/interpreter/system.rs | 48 +++++++++---------- actors/evm/src/lib.rs | 48 ++++++++++++++----- actors/evm/tests/selfdestruct.hex | 1 + actors/evm/tests/selfdestruct.rs | 42 ++++++++++++++++ actors/evm/tests/selfdestruct.sol | 7 +++ 10 files changed, 130 insertions(+), 64 deletions(-) create mode 100644 actors/evm/tests/selfdestruct.hex create mode 100644 actors/evm/tests/selfdestruct.rs create mode 100644 actors/evm/tests/selfdestruct.sol diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index e08e8534b..848c22966 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] +use fvm_shared::address::Address as FilecoinAddress; use { super::instructions::*, super::opcode::OpCode, @@ -23,6 +24,8 @@ pub struct ExecutionState { pub input_data: Bytes, pub return_data: Bytes, pub output_data: Bytes, + /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. + pub selfdestroyed: Option, } impl ExecutionState { @@ -33,12 +36,13 @@ impl ExecutionState { input_data, return_data: Default::default(), output_data: Bytes::new(), + selfdestroyed: None, } } } struct Machine<'r, BS: Blockstore, RT: Runtime> { - system: &'r System<'r, BS, RT>, + system: &'r mut System<'r, BS, RT>, runtime: &'r mut ExecutionState, bytecode: &'r Bytecode<'r>, pc: usize, @@ -90,7 +94,7 @@ macro_rules! def_ins { impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { pub fn new( - system: &'r System<'r, BS, RT>, + system: &'r mut System<'r, BS, RT>, runtime: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { @@ -843,7 +847,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { SELFDESTRUCT(m) { lifecycle::selfdestruct(m.runtime, m.system)?; - Ok(ControlFlow::Continue) + Ok(ControlFlow::Exit) // selfdestruct halts the current context } } @@ -853,7 +857,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { pub fn execute<'r, BS: Blockstore, RT: Runtime>( bytecode: &'r Bytecode, runtime: &'r mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &'r mut System<'r, BS, RT>, ) -> Result { let mut m = Machine::new(system, runtime, bytecode); m.execute()?; @@ -861,5 +865,6 @@ pub fn execute<'r, BS: Blockstore, RT: Runtime>( reverted: m.reverted, status_code: StatusCode::Success, output_data: m.runtime.output_data.clone(), + selfdestroyed: m.runtime.selfdestroyed, }) } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index ffc553064..97274bd79 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -113,7 +113,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( _kind: CallKind, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; - let rt = platform.rt; + let rt = &*platform.rt; // as immutable reference let _gas = stack.pop(); // EVM gas is not used in FVM let dst: H160 = crate::interpreter::uints::_u256_to_address(stack.pop()); diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index c09fe6066..6cba2c836 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,3 +1,4 @@ +use crate::interpreter::address::Address; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, @@ -15,22 +16,11 @@ pub fn create<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + _system: &'r mut System<'r, BS, RT>, ) -> Result<(), StatusCode> { - // Commented due to horrible borrow checker issues that stem from owning a HAMT during - // the entire execution inside the System. The HAMT needs to be flushed and dropped when - // create_actor, delete_actor, and send are called. All other methods taking a &mut self - // on the Runtime should not require &mut. - // - // let beneficiary_addr = Address::from(state.stack.pop()); - // - // if let Some(id_addr) = beneficiary_addr.as_id_address() { - // platform.rt.delete_actor(&id_addr)?; - // } else { - // todo!("no support for non-ID addresses") - // } - // - // Ok(()) - todo!() + let beneficiary_addr = Address::from(state.stack.pop()); + let id_addr = beneficiary_addr.as_id_address().expect("no support for non-ID addresses yet"); + state.selfdestroyed = Some(id_addr); + Ok(()) } diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index aa5198a09..2d1dfcc44 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -7,7 +7,7 @@ use { #[inline] pub fn sload<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + platform: &'r mut System<'r, BS, RT>, ) -> Result<(), StatusCode> { // where? let location = state.stack.pop(); @@ -24,7 +24,7 @@ pub fn sload<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn sstore<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + platform: &'r mut System<'r, BS, RT>, ) -> Result<(), StatusCode> { let location = state.stack.pop(); let value = state.stack.pop(); diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index df82a7a39..87d775d3a 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,4 +1,5 @@ use fil_actors_runtime::ActorError; +use fvm_shared::address::Address as FilecoinAddress; use { bytes::Bytes, fvm_ipld_encoding::Cbor, @@ -14,8 +15,10 @@ pub struct Output { pub status_code: StatusCode, /// Output data returned. pub output_data: Bytes, - // indicates if revert was requested + /// Indicates if revert was requested pub reverted: bool, + /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. + pub selfdestroyed: Option, } impl Debug for Output { diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 64548b50e..2ec74fa3e 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -6,7 +6,6 @@ use { fil_actors_runtime::{runtime::Runtime, ActorError}, fvm_ipld_blockstore::Blockstore, fvm_ipld_hamt::Hamt, - std::cell::RefCell, }; #[derive(Clone, Copy, Debug)] @@ -26,56 +25,53 @@ pub enum StorageStatus { /// Platform Abstraction Layer /// that bridges the FVM world to EVM world pub struct System<'r, BS: Blockstore, RT: Runtime> { - pub rt: &'r RT, - state: RefCell>, + pub rt: &'r mut RT, + state: &'r mut Hamt, } impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { - pub fn new(rt: &'r RT, state_cid: Cid) -> anyhow::Result { - Ok(Self { rt, state: RefCell::new(Hamt::load(&state_cid, rt.store())?) }) + pub fn new(rt: &'r mut RT, state: &'r mut Hamt) -> anyhow::Result { + Ok(Self { rt, state }) } - pub fn flush_state(&self) -> Result { - self.state.borrow_mut().flush().map_err(|e| ActorError::illegal_state(e.to_string())) + /// Reborrow the system with a shorter lifetime. + #[allow(clippy::needless_lifetimes)] + pub fn reborrow<'a>(&'a mut self) -> System<'a, BS, RT> { + System { rt: &mut *self.rt, state: &mut *self.state } + } + + pub fn flush_state(&mut self) -> Result { + self.state.flush().map_err(|e| ActorError::illegal_state(e.to_string())) } /// Get value of a storage key. - pub fn get_storage(&self, key: U256) -> Result, StatusCode> { + pub fn get_storage(&mut self, key: U256) -> Result, StatusCode> { let mut key_bytes = [0u8; 32]; key.to_big_endian(&mut key_bytes); - Ok(self - .state - .borrow() - .get(&key) - .map_err(|e| StatusCode::InternalError(e.to_string()))? - .cloned()) + Ok(self.state.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned()) } /// Set value of a storage key. - pub fn set_storage(&self, key: U256, value: Option) -> Result { + pub fn set_storage( + &mut self, + key: U256, + value: Option, + ) -> Result { let mut key_bytes = [0u8; 32]; key.to_big_endian(&mut key_bytes); - let prev_value = self - .state - .borrow() - .get(&key) - .map_err(|e| StatusCode::InternalError(e.to_string()))? - .cloned(); + let prev_value = + self.state.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned(); let mut storage_status = if prev_value == value { StorageStatus::Unchanged } else { StorageStatus::Modified }; if value.is_none() { - self.state - .borrow_mut() - .delete(&key) - .map_err(|e| StatusCode::InternalError(e.to_string()))?; + self.state.delete(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?; storage_status = StorageStatus::Deleted; } else { self.state - .borrow_mut() .set(key, value.unwrap()) .map_err(|e| StatusCode::InternalError(e.to_string()))?; } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 94f4f6a81..c8cc2b08f 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,7 @@ pub mod interpreter; mod state; +use fvm_shared::address::Address; use { crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, crate::state::State, @@ -38,7 +39,7 @@ pub struct EvmContractActor; impl EvmContractActor { pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> where - BS: Blockstore, + BS: Blockstore + Clone, RT: Runtime, { rt.validate_immediate_caller_accept_any()?; @@ -54,22 +55,23 @@ impl EvmContractActor { return Err(ActorError::illegal_argument("no bytecode provided".into())); } - // initialize contract state - let init_contract_state_cid = - Hamt::<_, U256, U256>::new(rt.store()).flush().map_err(|e| { - ActorError::unspecified(format!("failed to flush contract state: {e:?}")) - })?; + // create an empty storage HAMT to pass it down for execution. + let mut hamt = Hamt::<_, U256, U256>::new(rt.store().clone()); + // create an instance of the platform abstraction layer -- note: do we even need this? - let system = System::new(rt, init_contract_state_cid).map_err(|e| { + let mut system = System::new(rt, &mut hamt).map_err(|e| { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; + // create a new execution context let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + // identify bytecode valid jump destinations let bytecode = Bytecode::new(¶ms.bytecode) .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; + // invoke the contract constructor - let exec_status = execute(&bytecode, &mut exec_state, &system) + let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()) .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; if !exec_status.reverted @@ -103,14 +105,16 @@ impl EvmContractActor { params: InvokeParams, ) -> Result where - BS: Blockstore, + BS: Blockstore + Clone, RT: Runtime, { rt.validate_immediate_caller_accept_any()?; + let mut selfdestroyed = Option::
::default(); + // TODO this is fine in a transaction for now, as we don't have yet cross-contact calls // some refactoring will be needed when we start making cross contract calls. - rt.transaction(|state: &mut State, rt| { + let output = rt.transaction(|state: &mut State, rt| { let bytecode: Vec = match rt.store().get(&state.bytecode).map_err(|e| { ActorError::unspecified(format!("failed to parse bytecode: {e:?}")) @@ -122,7 +126,17 @@ impl EvmContractActor { let bytecode = Bytecode::new(&bytecode) .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; - let system = System::new(rt, state.contract_state).map_err(|e| { + // clone the blockstore here to pass to the System, this is bound to the HAMT. + let blockstore = rt.store().clone(); + + // load the storage HAMT + let mut hamt = Hamt::load(&state.contract_state, blockstore).map_err(|e| { + ActorError::illegal_state(format!( + "failed to load storage HAMT on invoke: {e:?}, e" + )) + })?; + + let mut system = System::new(rt, &mut hamt).map_err(|e| { ActorError::unspecified(format!( "failed to create execution abstraction layer: {e:?}" )) @@ -130,9 +144,11 @@ impl EvmContractActor { let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); - let exec_status = execute(&bytecode, &mut exec_state, &system) + let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()) .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; + selfdestroyed = exec_status.selfdestroyed; + // XXX is this correct handling of reverts? or should we fail execution? if exec_status.status_code == StatusCode::Success { let result = RawBytes::from(exec_status.output_data.to_vec()); @@ -148,7 +164,13 @@ impl EvmContractActor { exec_status.status_code ))) } - }) + })?; + + if let Some(addr) = selfdestroyed { + rt.delete_actor(&addr)? + } + + Ok(output) } } diff --git a/actors/evm/tests/selfdestruct.hex b/actors/evm/tests/selfdestruct.hex new file mode 100644 index 000000000..4e74e5897 --- /dev/null +++ b/actors/evm/tests/selfdestruct.hex @@ -0,0 +1 @@ +6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212205539162e3dd1c84b3cf9043b55e3890bbe09c5726899ad6003650e8f1ac698b564736f6c63430008070033 \ No newline at end of file diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs new file mode 100644 index 000000000..b0a276191 --- /dev/null +++ b/actors/evm/tests/selfdestruct.rs @@ -0,0 +1,42 @@ +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; + +#[test] +fn test_selfdestruct() { + let mut rt = MockRuntime::default(); + + let contract = Address::new_id(100); + let beneficiary = Address::new_id(1001); + + let params = evm::ConstructorParams { + bytecode: hex::decode(include_str!("selfdestruct.hex")).unwrap().into(), + input_data: RawBytes::default(), + }; + + // invoke constructor + rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); + rt.expect_validate_caller_any(); + rt.set_origin(contract); + + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + let solidity_params = hex::decode("35f46994").unwrap(); + let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + rt.expect_validate_caller_any(); + rt.expect_delete_actor(beneficiary); + + rt.call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); +} diff --git a/actors/evm/tests/selfdestruct.sol b/actors/evm/tests/selfdestruct.sol new file mode 100644 index 000000000..9bb0fda5b --- /dev/null +++ b/actors/evm/tests/selfdestruct.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.8.0; + +contract Selfdestruct { + function die() public { + selfdestruct(payable(address(0xFF000000000000000000000000000000000003E9))); + } +} \ No newline at end of file From 3b819850686ba6eeab90f9b5eb00057bc94bcacf Mon Sep 17 00:00:00 2001 From: raulk Date: Mon, 5 Sep 2022 19:45:09 +0100 Subject: [PATCH 034/339] fix: remove dependency on getrandom. (#603) --- .github/workflows/ci.yml | 4 ++ Cargo.lock | 101 ++++++++++----------------------------- actors/evm/Cargo.toml | 6 +-- runtime/Cargo.toml | 8 ++-- 4 files changed, 37 insertions(+), 82 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c80417ba..d5f7ad7a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,3 +70,7 @@ jobs: BUILD_FIL_NETWORK: ${{ matrix.network }} run: | cargo run -- -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' diff --git a/Cargo.lock b/Cargo.lock index 00a1f36db..229df918f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,18 +15,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a" +checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7" [[package]] name = "arrayref" @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab006897723d9352f63e2b13047177c3982d8d79709d713ce7747a8f19fd1b0" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" dependencies = [ "autocfg", "concurrent-queue", @@ -239,18 +239,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -333,9 +324,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.19" +version = "3.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d43934757334b5c0519ff882e1ab9647ac0258b47c24c4f490d78e42697fd5" +checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" dependencies = [ "atty", "bitflags", @@ -402,9 +393,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -584,7 +575,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.2", + "block-buffer", "crypto-common", ] @@ -881,7 +872,6 @@ dependencies = [ "fvm_ipld_hamt", "fvm_sdk", "fvm_shared", - "getrandom", "hex", "indexmap", "integer-encoding", @@ -896,7 +886,7 @@ dependencies = [ "regex", "serde", "serde_repr", - "sha2 0.10.3", + "sha2", "thiserror", "unsigned-varint", ] @@ -1171,7 +1161,7 @@ dependencies = [ "multihash", "once_cell", "serde", - "sha2 0.10.3", + "sha2", "thiserror", ] @@ -1236,10 +1226,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1287,27 +1275,6 @@ 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-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1431,14 +1398,11 @@ dependencies = [ "arrayref", "base64", "digest 0.9.0", - "hmac-drbg", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand", "serde", - "sha2 0.9.9", - "typenum", ] [[package]] @@ -1512,7 +1476,7 @@ dependencies = [ "ripemd", "serde", "serde-big-array", - "sha2 0.10.3", + "sha2", "sha3", "unsigned-varint", ] @@ -1630,9 +1594,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "opaque-debug" @@ -1937,22 +1901,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215" +checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" dependencies = [ "cfg-if", "cpufeatures", @@ -1961,9 +1912,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a31480366ec990f395a61b7c08122d99bd40544fdb5abcfc1b06bb29994312c" +checksum = "eaedf34ed289ea47c2b741bb72e5357a209512d67bcd4bda44359e5bf0470f56" dependencies = [ "digest 0.10.3", "keccak", @@ -2125,18 +2076,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" +checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" +checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" dependencies = [ "proc-macro2", "quote", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 75ac68026..3891d433f 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -38,12 +38,12 @@ fixed-hash = { version = "0.7.0", default-features = false } impl-serde = { version = "0.3.2", default-features = false } arrayvec = { version = "0.7.2", features = ["serde"] } hex = "0.4.3" -substrate-bn = "0.6.0" -near-blake2 = {version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git"} +substrate-bn = { version = "0.6.0", default-features = false } +near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } -libsecp256k1 = { version = "0.7.0", features = ["static-context"] } +libsecp256k1 = { version = "0.7.0", features = ["static-context"], default-features = false } hex-literal = "0.3.4" [features] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1ff0a7eb2..aa581d23c 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -23,8 +23,6 @@ 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.56" fvm_sdk = { version = "3.0.0-alpha.2", optional = true } @@ -32,11 +30,12 @@ blake2b_simd = "1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" multihash = { version = "0.16.1", default-features = false } -rand = "0.8.5" +rand = { version = "0.8.5", default-features = false } serde_repr = "0.1.8" regex = "1" itertools = "0.10" -libsecp256k1 = { version = "0.7.1"} +libsecp256k1 = { version = "0.7.1", default-features = false } + [dependencies.sha2] version = "0.10" @@ -44,6 +43,7 @@ version = "0.10" [dev-dependencies] derive_builder = "0.10.2" hex = "0.4.3" +rand = { version = "0.8.5" } [features] default = [] From 4bddc7bf5197d1d83ba25b06e4206002d940cb91 Mon Sep 17 00:00:00 2001 From: Maciej Witowski Date: Mon, 5 Sep 2022 22:41:32 +0200 Subject: [PATCH 035/339] Make test verify if selfdestruct actually works (#604) --- actors/evm/tests/selfdestruct.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index b0a276191..312cb5615 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -39,4 +39,5 @@ fn test_selfdestruct() { &RawBytes::serialize(params).unwrap(), ) .unwrap(); + rt.verify(); } From 9322e27ab2c435e84e2cfdf2c862e7a2bda975ef Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 6 Sep 2022 23:36:11 +0300 Subject: [PATCH 036/339] basic contract assembler for testing EVM (#610) * add etk-asm dev dependency * cargo lock * basic contract assembler * magic calc contract * rustfmt * clippy * hide error details from magic calc contract constructor * clippy * add docs for asm::new_contract --- Cargo.lock | 105 ++++++++++++++++++++++++++ actors/evm/Cargo.toml | 1 + actors/evm/tests/asm.rs | 60 +++++++++++++++ actors/evm/tests/calc.rs | 159 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 actors/evm/tests/asm.rs create mode 100644 actors/evm/tests/calc.rs diff --git a/Cargo.lock b/Cargo.lock index 229df918f..23efc3e85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,12 +579,33 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[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" @@ -655,6 +676,7 @@ dependencies = [ "bytes", "cid", "derive_more", + "etk-asm", "fil_actors_runtime", "fixed-hash", "fvm_ipld_blockstore", @@ -1616,6 +1638,50 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "pest" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04" +dependencies = [ + "once_cell", + "pest", + "sha-1", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1899,6 +1965,17 @@ dependencies = [ "syn", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha2" version = "0.10.5" @@ -1929,6 +2006,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "snafu" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.4.7" @@ -2109,6 +2208,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[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.3" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 3891d433f..7c7dbde35 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -45,6 +45,7 @@ near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/ne fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } libsecp256k1 = { version = "0.7.0", features = ["static-context"], default-features = false } hex-literal = "0.3.4" +etk-asm = "^0.2.1" [features] fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs new file mode 100644 index 000000000..0c58a80be --- /dev/null +++ b/actors/evm/tests/asm.rs @@ -0,0 +1,60 @@ +use etk_asm::ingest::Ingest; +use evm::interpreter::opcode::OpCode::*; +use fil_actor_evm as evm; + +#[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); + ingest_body.ingest(name, body)?; + // 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::::from([ + PUSH4 as u8, + ((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, + DUP1 as u8, + PUSH4 as u8, + ((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, + PUSH1 as u8, + 0x00, + CODECOPY as u8, + PUSH1 as u8, + 0x00, + RETURN as u8, + ]); + // 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/calc.rs b/actors/evm/tests/calc.rs new file mode 100644 index 000000000..f8b41de4a --- /dev/null +++ b/actors/evm/tests/calc.rs @@ -0,0 +1,159 @@ +mod asm; + +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; + +#[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 + +push1 0x00 +calldataload +push1 0xe0 # 28 byte shift == 224 bits +shr + +# 0x00 -> jmp get_magic +dup1 +iszero +%push(get_magic) +jumpi + +# 0x01 -> jmp add_magic +dup1 +push1 0x01 +eq +%push(add_magic) +jumpi + +# 0x02 -> jmp mul_magic +dup1 +push1 0x02 +eq +%push(mul_magic) +jumpi + +# unknown method, barf returning nothing +push1 0x00 +dup1 +revert + +#### 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 dataa +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 = MockRuntime::default(); + + // invoke constructor + rt.expect_validate_caller_any(); + + let params = + evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // invoke contract -- get_magic + let contract_params = vec![0u8; 32]; + let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + 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 params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + 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 params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(0x84)); +} From c72420ea4a38ff31a46f159d60887855f8df699d Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 7 Sep 2022 15:08:55 +0300 Subject: [PATCH 037/339] Some housekeeping (#612) * add dispatch macros for contract assembler * avoid extraneous stack copy --- actors/evm/tests/asm.rs | 30 +++++++++++++++++++++++++++--- actors/evm/tests/calc.rs | 34 +++++----------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index 0c58a80be..995448f09 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -2,6 +2,29 @@ use etk_asm::ingest::Ingest; use evm::interpreter::opcode::OpCode::*; 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 +"#; + #[allow(dead_code)] /// Creates a new EVM contract constructon bytecode (initcode), suitable for initializing the EVM actor. /// Arguments: @@ -12,7 +35,8 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a // the contract code let mut body_code = Vec::new(); let mut ingest_body = Ingest::new(&mut body_code); - ingest_body.ingest(name, body)?; + 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); @@ -32,7 +56,7 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a + 1 // 0x00 -- source memory offset + 1 // RETURN ; - let mut constructor_code = Vec::::from([ + let mut constructor_code = vec![ PUSH4 as u8, ((body_code_len >> 24) & 0xff) as u8, ((body_code_len >> 16) & 0xff) as u8, @@ -50,7 +74,7 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a PUSH1 as u8, 0x00, RETURN as u8, - ]); + ]; // the actual contract code let mut contract_code = Vec::new(); contract_code.append(&mut init_code); diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index f8b41de4a..6b0ab0627 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -18,35 +18,11 @@ sstore # - 0x00000001 -> ADD arg, magic value # - 0x00000002 -> MUL arg, magic value -push1 0x00 -calldataload -push1 0xe0 # 28 byte shift == 224 bits -shr - -# 0x00 -> jmp get_magic -dup1 -iszero -%push(get_magic) -jumpi - -# 0x01 -> jmp add_magic -dup1 -push1 0x01 -eq -%push(add_magic) -jumpi - -# 0x02 -> jmp mul_magic -dup1 -push1 0x02 -eq -%push(mul_magic) -jumpi - -# unknown method, barf returning nothing -push1 0x00 -dup1 -revert +%dispatch_begin() +%dispatch(0x00, get_magic) +%dispatch(0x01, add_magic) +%dispatch(0x02, mul_magic) +%dispatch_end() #### method implementation get_magic: From bbee4107519bfdcef353c054883d817799e31d27 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 8 Sep 2022 18:46:09 +0300 Subject: [PATCH 038/339] EVM: Implement CALL (#614) * refactor evm contract invocation so that we can make cross contract calls Limits the scope of the transaction to just saving the state. * rustfmt * clean up precompile calls and make scaffolding for actual implementation - drops H160 -- wtf is that even useful - adds dispatcher for actual call implementations - removes inline directive from big things * clean up error handling surrounding call; don't panic left and right * fix error handling with status codes * implement CALL * propagate nested ActorErrors from bytecode execution * more nested actor error handling * rustfmt * pass value through CALL * rustfmt * correctly handle call variant arguments the value was missing from call/callcode. also kill the useless create call kinds. * rustfmt * update comment, move common check outside of march arm * clippy * rustfmt * fix typo * ok_or_else instead of match. * Precompile asm tests (#615) * organize test files, WIP precompile ASM test * fix precompile test asm, add tests for sha precompile * use sha2-256 instead of keccak in precompiles * un-reorg evm tests * un-rename simplecoin test * save return data * fix handling of output memory regions * address space is 32 bits * names, names, names * proxy call test * fix overflow Co-authored-by: Melanie Riise --- .../evm/src/interpreter/instructions/call.rs | 145 +++++++++++++----- .../src/interpreter/instructions/memory.rs | 5 +- actors/evm/src/interpreter/output.rs | 30 ++-- actors/evm/src/interpreter/precompiles.rs | 30 ++-- actors/evm/src/interpreter/uints.rs | 9 ++ actors/evm/src/lib.rs | 103 +++++++------ actors/evm/tests/call.rs | 114 ++++++++++++++ actors/evm/tests/precompile.rs | 81 ++++++++++ 8 files changed, 403 insertions(+), 114 deletions(-) create mode 100644 actors/evm/tests/call.rs create mode 100644 actors/evm/tests/precompile.rs diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 97274bd79..69aafbe1b 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,14 +1,19 @@ use { super::memory::get_memory_region, + crate::interpreter::address::Address, crate::interpreter::instructions::memory::MemoryRegion, crate::interpreter::output::StatusCode, crate::interpreter::precompiles, crate::interpreter::stack::Stack, crate::interpreter::ExecutionState, crate::interpreter::System, - crate::interpreter::{H160, U256}, + crate::interpreter::U256, + crate::RawBytes, + crate::{InvokeParams, Method}, + fil_actors_runtime::runtime::builtins::Type as ActorType, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, + fvm_shared::econ::TokenAmount, }; /// The kind of call-like instruction. @@ -18,11 +23,8 @@ pub enum CallKind { DelegateCall, StaticCall, CallCode, - Create, - Create2 { salt: U256 }, } -#[inline] pub fn calldataload(state: &mut ExecutionState) { let index = state.stack.pop(); let input_len = state.input_data.len(); @@ -47,7 +49,6 @@ pub fn calldatasize(state: &mut ExecutionState) { state.stack.push(u128::try_from(state.input_data.len()).unwrap().into()); } -#[inline] pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let mem_index = state.stack.pop(); let input_index = state.stack.pop(); @@ -80,7 +81,6 @@ pub fn codesize(stack: &mut Stack, code: &[u8]) { stack.push(U256::from(code.len())) } -#[inline] pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> { let mem_index = state.stack.pop(); let input_index = state.stack.pop(); @@ -106,53 +106,126 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod Ok(()) } -#[inline] pub fn call<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, - _kind: CallKind, + kind: CallKind, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; let rt = &*platform.rt; // as immutable reference - let _gas = stack.pop(); // EVM gas is not used in FVM - let dst: H160 = crate::interpreter::uints::_u256_to_address(stack.pop()); - let input_offset = stack.pop(); - let input_size = stack.pop(); - let output_offset = stack.pop(); - let output_size = stack.pop(); - - // XXX do we need this? - stack.push(U256::zero()); // Assume failure. + let (_gas, dst, value, input_offset, input_size, output_offset, output_size) = match kind { + CallKind::Call | CallKind::CallCode => ( + stack.pop(), + stack.pop(), + stack.pop(), + stack.pop(), + stack.pop(), + stack.pop(), + stack.pop(), + ), + + CallKind::DelegateCall | CallKind::StaticCall => ( + stack.pop(), + stack.pop(), + U256::from(0), + stack.pop(), + stack.pop(), + stack.pop(), + stack.pop(), + ), + }; - // TODO Errs - let input_region = get_memory_region(memory, input_offset, input_size).unwrap(); - let output_region = get_memory_region(memory, output_offset, output_size).unwrap(); + let input_region = get_memory_region(memory, input_offset, input_size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; - let output = { + let mut result = { // ref to memory is dropped after calling so we can mutate it on output later let input_data = input_region .map(|MemoryRegion { offset, size }| &memory[offset..][..size.get()]) - .unwrap_or_default(); + .ok_or(StatusCode::InvalidMemoryAccess)?; - let output = if precompiles::Precompiles::::is_precompile(&dst) { + if precompiles::Precompiles::::is_precompile(&dst) { precompiles::Precompiles::call_precompile(rt, dst, input_data) + .map_err(|_| StatusCode::PrecompileFailure)? } else { - todo!() - }; - - output.unwrap() + // CALL and its brethren can only invoke other EVM contracts; see the (magic) + // CALLMETHOD/METHODNUM opcodes for calling fil actors with native call + // conventions. + let dst_addr = Address::from(dst) + .as_id_address() + .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; + + let dst_code_cid = rt + .get_actor_code_cid( + &rt.resolve_address(&dst_addr).ok_or_else(|| { + StatusCode::BadAddress("cannot resolve address".to_string()) + })?, + ) + .ok_or_else(|| StatusCode::BadAddress("unknown actor".to_string()))?; + let evm_code_cid = rt.get_code_cid_for_type(ActorType::EVM); + if dst_code_cid != evm_code_cid { + return Err(StatusCode::BadAddress("cannot call non EVM actor".to_string())); + } + + match kind { + CallKind::Call => { + let params = InvokeParams { input_data: RawBytes::from(input_data.to_vec()) }; + let result = rt.send( + &dst_addr, + Method::InvokeContract as u64, + RawBytes::serialize(params).map_err(|_| { + StatusCode::InternalError( + "failed to marshall invocation data".to_string(), + ) + })?, + TokenAmount::from(&value), + ); + result.map_err(StatusCode::from)?.to_vec() + } + CallKind::DelegateCall => { + todo!() + } + CallKind::StaticCall => { + todo!() + } + CallKind::CallCode => { + todo!() + } + } + } }; - let output_data = output_region - .map(|MemoryRegion { offset, size }| { - &mut memory[offset..][..size.get()] // would like to use get for this to err instead of panic - }) - .unwrap_or_default(); + // save return_data + state.return_data = result.clone().into(); - // TODO errs - output_data.get_mut(..output.len()).unwrap().copy_from_slice(&output); + // copy return data to output region if it is non-zero + let output_usize = if output_size.bits() < 32 { + output_size.as_usize() + } else { + // XXX that's probably a bug, should we barf instead? + 1 << 31 + }; + if output_usize > 0 { + let output_region = get_memory_region(memory, output_offset, output_size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let output_data = output_region + .map(|MemoryRegion { offset, size }| &mut memory[offset..][..size.get()]) + .ok_or(StatusCode::InvalidMemoryAccess)?; + + // truncate if needed + let mut result_usize = result.len(); + if result_usize > output_usize { + result_usize = output_usize; + result.truncate(output_usize); + } - // TODO do things after writing into output - todo!(); + output_data + .get_mut(..result_usize) + .ok_or(StatusCode::InvalidMemoryAccess)? + .copy_from_slice(&result); + } + + stack.push(U256::from(1)); + Ok(()) } diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 08ce43d09..c73a8bda6 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -9,6 +9,7 @@ use { /// The size of the EVM 256-bit word in bytes. const WORD_SIZE: usize = 32; +#[derive(Debug)] pub struct MemoryRegion { pub offset: usize, pub size: NonZeroUsize, @@ -50,7 +51,7 @@ fn get_memory_region_u64( #[inline] #[allow(clippy::result_unit_err)] pub fn get_memory_region( - state: &mut Memory, + mem: &mut Memory, offset: U256, size: U256, ) -> Result, ()> { @@ -62,7 +63,7 @@ pub fn get_memory_region( return Err(()); } - get_memory_region_u64(state, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some) + get_memory_region_u64(mem, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some) } #[inline] diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index 87d775d3a..a135acae1 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,12 +1,6 @@ -use fil_actors_runtime::ActorError; +use fil_actors_runtime::ActorError as RTActorError; use fvm_shared::address::Address as FilecoinAddress; -use { - bytes::Bytes, - fvm_ipld_encoding::Cbor, - serde::{Deserialize, Serialize}, - std::fmt::Debug, - strum_macros::Display, -}; +use {bytes::Bytes, std::fmt::Debug, strum_macros::Display}; /// Output of EVM execution. #[derive(Clone, PartialEq, Eq)] @@ -33,7 +27,7 @@ impl Debug for Output { /// Message status code. #[must_use] -#[derive(Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, Display, PartialEq, Eq)] pub enum StatusCode { /// Execution finished with success. #[strum(serialize = "success")] @@ -109,15 +103,19 @@ pub enum StatusCode { /// EVM implementation generic internal error. #[strum(serialize = "internal error")] InternalError(String), + + /// Invalid Address + #[strum(serialize = "bad address")] + BadAddress(String), + + /// Nested Actor invocation Error + #[strum(serialize = "runtime actor error")] + ActorError(RTActorError), } // Map ActorError to a generic internal error status code. -// -// TODO need to figure out error handling. -impl From for StatusCode { - fn from(ae: ActorError) -> Self { - Self::InternalError(String::from(ae.msg())) +impl From for StatusCode { + fn from(ae: RTActorError) -> Self { + Self::ActorError(ae) } } - -impl Cbor for StatusCode {} diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index f1bdeaac6..5db4b5fef 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, ops::Mul}; -use super::{H160, U256}; +use super::U256; use fil_actors_runtime::runtime::{Primitives, Runtime}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::{ @@ -27,7 +27,7 @@ pub type PrecompileResult = Result, PrecompileError>; // TODO i dont lik const fn gen_precompiles() -> [PrecompileFn; 9] { [ ec_recover, // ecrecover 0x01 - sha256, // SHA256 (Keccak) 0x02 + sha256, // SHA2-256 0x02 ripemd160, // ripemd160 0x03 identity, // identity 0x04 modexp, // modexp 0x05 @@ -42,17 +42,17 @@ pub struct Precompiles(PhantomData, PhantomData); impl> Precompiles { const PRECOMPILES: [PrecompileFn; 9] = gen_precompiles(); - const MAX_PRECOMPILE: H160 = { - let mut bytes = [0u8; 20]; - bytes[0] = Self::PRECOMPILES.len() as u8; - H160(bytes) + const MAX_PRECOMPILE: U256 = { + let mut limbs = [0u64; 4]; + limbs[0] = Self::PRECOMPILES.len() as u64; + U256(limbs) }; - pub fn call_precompile(runtime: &RT, precompile_addr: H160, input: &[u8]) -> PrecompileResult { + pub fn call_precompile(runtime: &RT, precompile_addr: U256, input: &[u8]) -> PrecompileResult { Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](runtime, input) } - pub fn is_precompile(addr: &H160) -> bool { + pub fn is_precompile(addr: &U256) -> bool { !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE } } @@ -92,7 +92,7 @@ fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { } fn sha256(rt: &RT, input: &[u8]) -> PrecompileResult { - Ok(rt.hash(SupportedHashes::Keccak256, input)) + Ok(rt.hash(SupportedHashes::Sha2_256, input)) } fn ripemd160(rt: &RT, input: &[u8]) -> PrecompileResult { @@ -310,6 +310,18 @@ mod tests { use fil_actors_runtime::test_utils::MockRuntime; use hex_literal::hex; + #[test] + fn sha256() { + use super::sha256 as hash; + let input = "foo bar baz boxy".as_bytes(); + + let rt = MockRuntime::default(); + + let expected = hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); + let res = hash(&rt, input).unwrap(); + assert_eq!(&res, &expected); + } + // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { use super::MockRuntime; diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index cafb73258..c51ed2728 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -5,6 +5,7 @@ use { fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}, + fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, impl_serde::{impl_fixed_hash_serde, impl_uint_serde}, std::cmp::Ordering, @@ -25,6 +26,14 @@ impl From<&TokenAmount> for U256 { } } +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)) + } +} + // make ETH uints serde serializable, // so it can work with Hamt and other // IPLD structures seamlessly diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index c8cc2b08f..2174dfe4f 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,7 +1,6 @@ pub mod interpreter; mod state; -use fvm_shared::address::Address; use { crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, crate::state::State, @@ -71,8 +70,11 @@ impl EvmContractActor { .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; // invoke the contract constructor - let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()) - .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; + let exec_status = + execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; if !exec_status.reverted && exec_status.status_code == StatusCode::Success @@ -95,6 +97,8 @@ impl EvmContractActor { rt.create(&state)?; Ok(()) + } else if let StatusCode::ActorError(e) = exec_status.status_code { + Err(e) } else { Err(ActorError::unspecified("EVM constructor failed".to_string())) } @@ -110,66 +114,63 @@ impl EvmContractActor { { rt.validate_immediate_caller_accept_any()?; - let mut selfdestroyed = Option::
::default(); - - // TODO this is fine in a transaction for now, as we don't have yet cross-contact calls - // some refactoring will be needed when we start making cross contract calls. - let output = rt.transaction(|state: &mut State, rt| { - let bytecode: Vec = - match rt.store().get(&state.bytecode).map_err(|e| { - ActorError::unspecified(format!("failed to parse bytecode: {e:?}")) - })? { - Some(bytes) => bytes, - None => return Err(ActorError::unspecified("missing bytecode".to_string())), - }; - - let bytecode = Bytecode::new(&bytecode) - .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; - - // clone the blockstore here to pass to the System, this is bound to the HAMT. - let blockstore = rt.store().clone(); - - // load the storage HAMT - let mut hamt = Hamt::load(&state.contract_state, blockstore).map_err(|e| { - ActorError::illegal_state(format!( - "failed to load storage HAMT on invoke: {e:?}, e" - )) - })?; + let state: State = rt.state()?; + let bytecode: Vec = rt + .store() + .get(&state.bytecode) + .map_err(|e| ActorError::unspecified(format!("failed to load bytecode: {e:?}")))? + .ok_or_else(|| ActorError::unspecified("missing bytecode".to_string()))?; - let mut system = System::new(rt, &mut hamt).map_err(|e| { - ActorError::unspecified(format!( - "failed to create execution abstraction layer: {e:?}" - )) - })?; + let bytecode = Bytecode::new(&bytecode) + .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; - let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + // clone the blockstore here to pass to the System, this is bound to the HAMT. + let blockstore = rt.store().clone(); - let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()) - .map_err(|e| ActorError::unspecified(format!("EVM execution error: {e:?}")))?; + // load the storage HAMT + let mut hamt = Hamt::load(&state.contract_state, blockstore).map_err(|e| { + ActorError::illegal_state(format!("failed to load storage HAMT on invoke: {e:?}, e")) + })?; - selfdestroyed = exec_status.selfdestroyed; + let mut system = System::new(rt, &mut hamt).map_err(|e| { + ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) + })?; - // XXX is this correct handling of reverts? or should we fail execution? - if exec_status.status_code == StatusCode::Success { - let result = RawBytes::from(exec_status.output_data.to_vec()); + let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); - if !exec_status.reverted { - state.contract_state = system.flush_state()?; - } + let exec_status = + execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; - Ok(result) - } else { - Err(ActorError::unspecified(format!( - "EVM contract invocation failed: status: {}", - exec_status.status_code - ))) + // TODO this is not the correct handling of reverts -- we need to abort (and return the + // output data), so that the entire transaction (including parent/sibling calls) + // can be reverted. + if exec_status.status_code == StatusCode::Success { + if !exec_status.reverted { + // this needs to be outside the transaction or else rustc has a fit about + // mutably borrowing the runtime twice.... sigh. + let contract_state = system.flush_state()?; + rt.transaction(|state: &mut State, _rt| { + state.contract_state = contract_state; + Ok(()) + })?; } - })?; + } else if let StatusCode::ActorError(e) = exec_status.status_code { + return Err(e); + } else { + return Err(ActorError::unspecified(format!( + "EVM contract invocation failed: status: {}", + exec_status.status_code + ))); + } - if let Some(addr) = selfdestroyed { + if let Some(addr) = exec_status.selfdestroyed { rt.delete_actor(&addr)? } + let output = RawBytes::from(exec_status.output_data.to_vec()); Ok(output) } } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs new file mode 100644 index 000000000..d93fc63e7 --- /dev/null +++ b/actors/evm/tests/call.rs @@ -0,0 +1,114 @@ +mod asm; + +use evm::interpreter::address::Address as EVMAddress; +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address as FILAddress; +use fvm_shared::bigint::Zero; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; + +#[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 +push1 0x00 +# 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() +} + +#[test] +fn test_call() { + let mut rt = MockRuntime::default(); + + // construct the proxy + let contract = call_proxy_contract(); + rt.expect_validate_caller_any(); + let params = + evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // create a mock target and proxy a call through the proxy + let target = FILAddress::new_id(0x100); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + let evm_target = EVMAddress::from_id_address(&target).unwrap(); + 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 params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + + let proxy_call_contract_params = vec![0u8; 4]; + let proxy_call_params = + evm::InvokeParams { input_data: RawBytes::from(proxy_call_contract_params) }; + + // expected return data + let mut return_data = vec![0u8; 32]; + return_data[31] = 0x42; + + rt.expect_validate_caller_any(); + rt.expect_send( + target, + evm::Method::InvokeContract as u64, + RawBytes::serialize(proxy_call_params).unwrap(), + TokenAmount::zero(), + RawBytes::from(return_data), + ExitCode::OK, + ); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); +} diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs new file mode 100644 index 000000000..b0bcfec4e --- /dev/null +++ b/actors/evm/tests/precompile.rs @@ -0,0 +1,81 @@ +mod asm; + +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; + +#[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 = MockRuntime::default(); + + // invoke constructor + rt.expect_validate_caller_any(); + + let params = + evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // invoke contract + let contract_params = vec![0u8; 32]; + let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + + rt.expect_validate_caller_any(); + let result = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + let expected = + hex_literal::hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); + assert_eq!( + U256::from_big_endian(&result), + U256::from(expected), + "\n{}\n{}", + hex::encode(&*result), + hex::encode(expected) + ); +} From 82c36dbd980ced1845274fd5a7ee6d75448d9289 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 8 Sep 2022 19:21:54 +0300 Subject: [PATCH 039/339] EVM: remove H160/H256 abstractions and transactions (#619) * remove H160/H256 abstraction and transactions, use (safe) Address type for addresses. * rustfmt * store addresses as a 20-byte array internally * rustfmt * implement Debug for Address * clippy * improve error message in try_from conversion failure --- actors/evm/src/interpreter/address.rs | 41 +- .../evm/src/interpreter/instructions/call.rs | 2 +- .../src/interpreter/instructions/lifecycle.rs | 2 +- actors/evm/src/interpreter/mod.rs | 7 +- actors/evm/src/interpreter/transaction.rs | 788 ------------------ actors/evm/src/interpreter/uints.rs | 47 +- 6 files changed, 33 insertions(+), 854 deletions(-) delete mode 100644 actors/evm/src/interpreter/transaction.rs diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index cda664c94..fe2f77ba8 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -1,16 +1,32 @@ -use crate::interpreter::{H160, H256}; +use crate::StatusCode; use crate::U256; use fvm_shared::address::Address as FilecoinAddress; /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). /// /// TODO this type will eventually handle f4 address detection. -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Address(H160); +#[derive(PartialEq, Eq, Clone)] +pub struct Address([u8; 20]); -impl From for Address { - fn from(v: U256) -> Self { - Self(H256(v.into()).into()) +impl TryFrom for Address { + type Error = StatusCode; + + fn try_from(v: U256) -> Result { + // top 12 bytes must be 0s; + // enforce that constraint so that we validate that the word is a valid address + let mut bytes = [0u8; 32]; + v.to_big_endian(&mut bytes); + if !bytes[..12].iter().all(|&byte| byte == 0) { + Err(StatusCode::BadAddress(format!("invalid address: {}", hex::encode(bytes)))) + } else { + Ok(Self(bytes[12..].try_into().unwrap())) + } + } +} + +impl std::fmt::Debug for Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&hex::encode(self.0)) } } @@ -25,7 +41,7 @@ impl Address { let mut bytes = [0u8; 20]; bytes[0] = 0xff; bytes[12..].copy_from_slice(&id.to_be_bytes()); - Address(bytes.try_into().unwrap()) + Address(bytes) } /// Interpret the hash as an ID address in EVM-form, and return a Filecoin ID address if that's @@ -37,16 +53,15 @@ impl Address { /// 0 1-11 12 /// 0xff \[0x00...] [id address...] pub fn as_id_address(&self) -> Option { - let val = &self.0 .0; - if (val[0] != 0xff) || !val[1..12].iter().all(|&byte| byte == 0) { + if (self.0[0] != 0xff) || !self.0[1..12].iter().all(|&byte| byte == 0) { return None; } - Some(FilecoinAddress::new_id(u64::from_be_bytes(val[12..].try_into().unwrap()))) + Some(FilecoinAddress::new_id(u64::from_be_bytes(self.0[12..].try_into().unwrap()))) } /// Returns this Address as an EVM word. pub fn as_evm_word(&self) -> U256 { - U256::from(&self.0[..]) + U256::from_big_endian(&self.0) } } @@ -56,7 +71,7 @@ mod tests { use crate::U256; use fvm_shared::address::Address as FilecoinAddress; - const TYPE_PADDING: &[u8] = &[0; 12]; // padding preceding H160 (12 bytes) + const TYPE_PADDING: &[u8] = &[0; 12]; // padding (12 bytes) 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) @@ -67,7 +82,7 @@ mod tests { #[test] fn $name() { let evm_bytes = $input.concat(); - let evm_addr = Address::from(U256::from(evm_bytes.as_slice())); + let evm_addr = Address::try_from(U256::from(evm_bytes.as_slice())).unwrap(); assert_eq!( evm_addr.as_id_address(), $expectation diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 69aafbe1b..b7c5f87b5 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -152,7 +152,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( // CALL and its brethren can only invoke other EVM contracts; see the (magic) // CALLMETHOD/METHODNUM opcodes for calling fil actors with native call // conventions. - let dst_addr = Address::from(dst) + let dst_addr = Address::try_from(dst)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 6cba2c836..012488046 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -19,7 +19,7 @@ pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, _system: &'r mut System<'r, BS, RT>, ) -> Result<(), StatusCode> { - let beneficiary_addr = Address::from(state.stack.pop()); + let beneficiary_addr = Address::try_from(state.stack.pop())?; let id_addr = beneficiary_addr.as_id_address().expect("no support for non-ID addresses yet"); state.selfdestroyed = Some(id_addr); Ok(()) diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 3892ccd44..8b48e5fba 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -8,7 +8,6 @@ pub mod output; pub mod precompiles; pub mod stack; pub mod system; -pub mod transaction; pub mod uints; pub use { @@ -16,11 +15,7 @@ pub use { execution::{execute, ExecutionState}, output::{Output, StatusCode}, system::System, - transaction::{ - SignedTransaction, Transaction, TransactionAction, TransactionRecoveryId, - TransactionSignature, - }, - uints::{H160, H256, U256, U512}, + uints::{U256, U512}, }; #[macro_export] diff --git a/actors/evm/src/interpreter/transaction.rs b/actors/evm/src/interpreter/transaction.rs deleted file mode 100644 index 6bcbb4451..000000000 --- a/actors/evm/src/interpreter/transaction.rs +++ /dev/null @@ -1,788 +0,0 @@ -#![allow(dead_code)] - -use { - crate::interpreter::{H160, H256, U256}, - bytes::Bytes, - fil_actors_runtime::ActorError, - fvm_shared::crypto::signature::SECP_PUB_LEN, - rlp::{DecoderError, Rlp, RlpStream}, - sha3::{Digest, Keccak256}, - std::{fmt::Debug, ops::Deref}, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum TransactionAction { - Call(H160), - Create, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct AccessListItem { - pub address: H160, - pub slots: Vec, -} - -pub enum Transaction { - Legacy { - chain_id: Option, - nonce: u64, - gas_price: U256, - gas_limit: u64, - action: TransactionAction, - value: U256, - input: Bytes, - }, - EIP2930 { - chain_id: u64, - nonce: u64, - gas_price: U256, - gas_limit: u64, - action: TransactionAction, - value: U256, - input: Bytes, - access_list: Vec, - }, - EIP1559 { - chain_id: u64, - nonce: u64, - max_priority_fee_per_gas: U256, - max_fee_per_gas: U256, - gas_limit: u64, - action: TransactionAction, - value: U256, - input: Bytes, - access_list: Vec, - }, -} - -#[derive(Debug)] -pub struct TransactionRecoveryId(pub u64); - -#[derive(Debug)] -pub struct TransactionSignature { - pub v: TransactionRecoveryId, - pub r: H256, - pub s: H256, -} - -#[derive(Debug)] -pub struct SignedTransaction { - pub transaction: Transaction, - pub signature: TransactionSignature, -} - -impl rlp::Encodable for TransactionAction { - fn rlp_append(&self, s: &mut RlpStream) { - match self { - Self::Call(address) => { - s.encoder().encode_value(&address[..]); - } - Self::Create => s.encoder().encode_value(&[]), - } - } -} - -impl rlp::Decodable for TransactionAction { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_empty() { - if rlp.is_data() { - Ok(TransactionAction::Create) - } else { - Err(DecoderError::RlpExpectedToBeData) - } - } else { - Ok(TransactionAction::Call(rlp.as_val()?)) - } - } -} - -impl rlp::Encodable for AccessListItem { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - s.append(&self.address); - s.append_list(&self.slots); - } -} - -impl rlp::Decodable for AccessListItem { - fn decode(rlp: &Rlp) -> Result { - Ok(Self { address: rlp.val_at(0)?, slots: rlp.list_at(1)? }) - } -} - -impl Transaction { - /// Calculates the hash of the transaction fields without the signature. - /// This value is the input to the signing function and the signature - /// is caluculated over this hash and the sender private key. - pub fn hash(&self) -> H256 { - let mut s = RlpStream::new(); - match self { - Transaction::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } => { - if let Some(chain_id) = chain_id { - s.begin_list(9); - s.append(nonce); - s.append(gas_price); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append(chain_id); - s.append(&0_u8); - s.append(&0_u8); - } else { - s.begin_list(6); - s.append(nonce); - s.append(gas_limit); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - } - } - Transaction::EIP2930 { - chain_id, - nonce, - gas_price, - gas_limit, - action, - value, - input, - access_list, - } => { - s.append_raw(&[1u8], 0); - s.begin_list(8); - s.append(chain_id); - s.append(nonce); - s.append(gas_price); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append_list(access_list); - } - Transaction::EIP1559 { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - action, - value, - input, - access_list, - } => { - s.append_raw(&[2u8], 0); - s.begin_list(9); - s.append(chain_id); - s.append(nonce); - s.append(max_priority_fee_per_gas); - s.append(max_fee_per_gas); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append_list(access_list); - } - }; - - H256::from_slice(Keccak256::digest(s.as_raw()).as_slice()) - } - - pub fn nonce(&self) -> u64 { - *match self { - Transaction::Legacy { nonce, .. } => nonce, - Transaction::EIP2930 { nonce, .. } => nonce, - Transaction::EIP1559 { nonce, .. } => nonce, - } - } - - pub fn chain_id(&self) -> Option { - match self { - Transaction::Legacy { chain_id, .. } => *chain_id, - Transaction::EIP2930 { chain_id, .. } => Some(*chain_id), - Transaction::EIP1559 { chain_id, .. } => Some(*chain_id), - } - } - - pub fn gas_price(&self) -> U256 { - *match self { - Transaction::Legacy { gas_price, .. } => gas_price, - Transaction::EIP2930 { gas_price, .. } => gas_price, - Transaction::EIP1559 { max_fee_per_gas, .. } => max_fee_per_gas, - } - } - - pub fn gas_limit(&self) -> u64 { - *match self { - Transaction::Legacy { gas_limit, .. } => gas_limit, - Transaction::EIP2930 { gas_limit, .. } => gas_limit, - Transaction::EIP1559 { gas_limit, .. } => gas_limit, - } - } - - pub fn action(&self) -> TransactionAction { - match self { - Transaction::Legacy { action, .. } => action, - Transaction::EIP2930 { action, .. } => action, - Transaction::EIP1559 { action, .. } => action, - } - .clone() - } - - pub fn input(&self) -> Bytes { - match self { - Transaction::Legacy { input, .. } => input, - Transaction::EIP2930 { input, .. } => input, - Transaction::EIP1559 { input, .. } => input, - } - .clone() - } - - pub fn value(&self) -> U256 { - *match self { - Transaction::Legacy { value, .. } => value, - Transaction::EIP2930 { value, .. } => value, - Transaction::EIP1559 { value, .. } => value, - } - } -} - -impl Deref for TransactionRecoveryId { - type Target = u64; - - fn deref(&self) -> &u64 { - &self.0 - } -} - -impl TransactionRecoveryId { - pub fn odd_y_parity(&self) -> u8 { - if self.0 == 27 || self.0 == 28 || self.0 > 36 { - ((self.0 - 1) % 2) as u8 - } else { - 4 - } - } - - pub fn chain_id(&self) -> Option { - if self.0 > 36 { - Some((self.0 - 35) / 2) - } else { - None - } - } -} - -impl TryFrom<&[u8]> for SignedTransaction { - type Error = DecoderError; - - fn try_from(value: &[u8]) -> Result { - if value.is_empty() { - return Err(DecoderError::RlpIsTooShort); - } - - match value[0] { - 0x01 => parse_eip2930_transaction(value), - 0x02 => parse_eip1559_transaction(value), - _ => parse_legacy_transaction(value), - } - } -} - -impl Deref for SignedTransaction { - type Target = Transaction; - - fn deref(&self) -> &Self::Target { - &self.transaction - } -} - -impl SignedTransaction { - /// Creates RLP serialized representation of the transaction. - /// This value is the input to the hash function that is used - /// to calculate the final transaction hash as it appears on - /// blockchain explorers. This representation can be sent directly - /// to ETH nodes and to the FVM-EVM bridge - pub fn serialize(&self) -> Vec { - let mut s = RlpStream::new(); - match &self.transaction { - Transaction::Legacy { nonce, gas_price, gas_limit, action, value, input, .. } => { - s.begin_list(9); - s.append(nonce); - s.append(gas_price); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append(&self.signature.v.0); - s.append(&self.signature.r); - s.append(&self.signature.s); - } - Transaction::EIP2930 { - chain_id, - nonce, - gas_price, - gas_limit, - action, - value, - input, - access_list, - } => { - s.append_raw(&[1u8], 0); - s.begin_list(11); - s.append(chain_id); - s.append(nonce); - s.append(gas_price); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append_list(access_list); - s.append(&self.signature.v.0); - s.append(&self.signature.r); - s.append(&self.signature.s); - } - Transaction::EIP1559 { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - action, - value, - input, - access_list, - } => { - s.append_raw(&[2u8], 0); - s.begin_list(12); - s.append(chain_id); - s.append(nonce); - s.append(max_priority_fee_per_gas); - s.append(max_fee_per_gas); - s.append(gas_limit); - s.append(action); - s.append(value); - s.append(input); - s.append_list(access_list); - s.append(&self.signature.v.0); - s.append(&self.signature.r); - s.append(&self.signature.s); - } - }; - s.as_raw().to_vec() - } - - pub fn hash(&self) -> H256 { - H256::from_slice(Keccak256::digest(&self.serialize()).as_slice()) - } - - /// The secp256k1 public key of the transaction sender. - /// - /// This public key can used to derive the equivalent Filecoin account - pub fn sender_public_key(&self) -> Result<[u8; SECP_PUB_LEN], ActorError> { - todo!(); - // let mut sig = [0u8; 65]; - // sig[..32].copy_from_slice(self.signature.r.as_bytes()); - // sig[32..64].copy_from_slice(self.signature.s.as_bytes()); - - // if matches!(self.transaction, Transaction::Legacy { .. }) { - // sig[64] = self.signature.v.odd_y_parity(); - // } else { - // sig[64] = self.signature.v.0 as u8; - // } - - // #[cfg(not(test))] // use a syscall to fvm - // return fvm_sdk::crypto::recover_secp_public_key( - // &self.transaction.hash().to_fixed_bytes(), - // &sig, - // ) - // .map_err(|e| { - // ActorError::illegal_argument(format!("failed to recover public key: {e:?}")) - // }); - - // #[cfg(test)] - // // invoke the recovery impl directly as there is not FVM running this code - // return Ok( - // fvm_shared::crypto::signature::ops::recover_secp_public_key( - // &self.transaction.hash().to_fixed_bytes(), - // &sig, - // ) - // .unwrap() - // .serialize(), - // ); - } - - /// Ethereum sender address which is 20-bytes trimmed keccak256(pubkey) - pub fn sender_address(&self) -> Result { - let pubkey = self.sender_public_key()?; - let address_slice = &Keccak256::digest(&pubkey[1..])[12..]; - Ok(H160::from_slice(address_slice)) - } -} - -/// rlp([nonce, gasPrice, gasLimit, to, value, data, init, v, r, s]) -fn parse_legacy_transaction(bytes: &[u8]) -> Result { - let rlp = Rlp::new(bytes); - - if rlp.item_count()? != 9 { - return Err(DecoderError::RlpIncorrectListLen); - } - - let signature = TransactionSignature { - v: TransactionRecoveryId(rlp.val_at(6)?), - r: rlp.val_at(7)?, - s: rlp.val_at(8)?, - }; - - Ok(SignedTransaction { - transaction: Transaction::Legacy { - chain_id: signature.v.chain_id(), - nonce: rlp.val_at(0)?, - gas_price: rlp.val_at(1)?, - gas_limit: rlp.val_at(2)?, - action: rlp.val_at(3)?, - value: rlp.val_at(4)?, - input: rlp.val_at(5)?, - }, - signature, - }) -} - -/// 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, -/// accessList, signatureYParity, signatureR, signatureS]) -fn parse_eip2930_transaction(bytes: &[u8]) -> Result { - let rlp = Rlp::new(&bytes[1..]); - - if rlp.item_count()? != 11 { - return Err(DecoderError::RlpIncorrectListLen); - } - - let signature = TransactionSignature { - v: TransactionRecoveryId(rlp.val_at(8)?), - r: rlp.val_at(9)?, - s: rlp.val_at(10)?, - }; - - Ok(SignedTransaction { - transaction: Transaction::EIP2930 { - chain_id: rlp.val_at(0)?, - nonce: rlp.val_at(1)?, - gas_price: rlp.val_at(2)?, - gas_limit: rlp.val_at(3)?, - action: rlp.val_at(4)?, - value: rlp.val_at(5)?, - input: rlp.val_at(6)?, - access_list: rlp.list_at(7)?, - }, - signature, - }) -} - -/// 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, -/// gas_limit, destination, amount, data, access_list, signature_y_parity, -/// signature_r, signature_s]) -fn parse_eip1559_transaction(bytes: &[u8]) -> Result { - let rlp = Rlp::new(&bytes[1..]); - - if rlp.item_count()? != 12 { - return Err(DecoderError::RlpIncorrectListLen); - } - - Ok(SignedTransaction { - signature: TransactionSignature { - v: TransactionRecoveryId(rlp.val_at(9)?), - r: rlp.val_at(10)?, - s: rlp.val_at(11)?, - }, - transaction: Transaction::EIP1559 { - chain_id: rlp.val_at(0)?, - nonce: rlp.val_at(1)?, - max_priority_fee_per_gas: rlp.val_at(2)?, - max_fee_per_gas: rlp.val_at(3)?, - gas_limit: rlp.val_at(4)?, - action: rlp.val_at(5)?, - value: rlp.val_at(6)?, - input: rlp.val_at(7)?, - access_list: rlp.list_at(8)?, - }, - }) -} - -impl Debug for Transaction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } => f - .debug_struct("Legacy") - .field("chain_id", chain_id) - .field("nonce", nonce) - .field("gas_price", gas_price) - .field("gas_limit", gas_limit) - .field("action", action) - .field("value", value) - .field("input", &hex::encode(&input)) - .finish(), - Self::EIP2930 { - chain_id, - nonce, - gas_price, - gas_limit, - action, - value, - input, - access_list, - } => f - .debug_struct("EIP2930") - .field("chain_id", chain_id) - .field("nonce", nonce) - .field("gas_price", gas_price) - .field("gas_limit", gas_limit) - .field("action", action) - .field("value", value) - .field("input", &hex::encode(&input)) - .field("access_list", access_list) - .finish(), - Self::EIP1559 { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - action, - value, - input, - access_list, - } => f - .debug_struct("EIP1559") - .field("chain_id", chain_id) - .field("nonce", nonce) - .field("max_priority_fee_per_gas", max_priority_fee_per_gas) - .field("max_fee_per_gas", max_fee_per_gas) - .field("gas_limit", gas_limit) - .field("action", action) - .field("value", value) - .field("input", &hex::encode(&input)) - .field("access_list", access_list) - .finish(), - } - } -} - -#[cfg(test)] -mod tests { - use { - crate::interpreter::{ - transaction::{AccessListItem, Transaction, TransactionAction}, - SignedTransaction, H160, H256, U256, - }, - hex_literal::hex, - }; - - #[ignore] - #[test] - fn decode_legacy_transaction() { - // https://etherscan.io/tx/0x3741aea434dc6e9e740be0113af4bac372fcdd2fa2188409c93c9405cbdcaaf0 - let raw = hex!( - "f9016b0885113abe69b38302895c947a250d5630b4cf539739df2c5dacb4c659f2488d80b90 - 1044a25d94a00000000000000000000000000000000000000000000000022b1c8c1227a0000 - 000000000000000000000000000000000000000000000003f0a59430f92a924400000000000 - 000000000000000000000000000000000000000000000000000a00000000000000000000000 - 0012021043bbaab3b71b2217655787a13d24cf618b000000000000000000000000000000000 - 00000000000000000000000603c6a1e00000000000000000000000000000000000000000000 - 00000000000000000002000000000000000000000000fe9a29ab92522d14fc65880d8172142 - 61d8479ae000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc225 - a01df6c364ee7d2b684bbb6e3892fee69a1bc4fc487222b003ea57ec1596884916a01e1643f - de193fde5e6be4ae0b2d4c4669560132a6dc87b6404d5c0cdc743fee6 - " - ); - - let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); - - // test sender recovery - assert_eq!( - H160::from_slice(&hex!("12021043bbaab3b71b2217655787a13d24cf618b")), - transaction.sender_address().unwrap() - ); - - // test transaction hash computation: - assert_eq!( - H256::from_slice(&hex!( - "3741aea434dc6e9e740be0113af4bac372fcdd2fa2188409c93c9405cbdcaaf0" - )), - transaction.hash() - ); - - // test decoded fields - if let Transaction::Legacy { chain_id, nonce, gas_price, gas_limit, action, value, input } = - transaction.transaction - { - assert_eq!(Some(1), chain_id); - assert_eq!(8, nonce); - assert_eq!(U256::from(74000001459u64), gas_price); - assert_eq!(166236, gas_limit); - assert_eq!(U256::zero(), value); - - assert_eq!( - TransactionAction::Call(H160::from_slice(&hex!( - "7a250d5630b4cf539739df2c5dacb4c659f2488d" - ))), - action - ); - - assert_eq!( - &hex!( - "4a25d94a00000000000000000000000000000000000000000000000022b1c8c1227a - 0000000000000000000000000000000000000000000000000003f0a59430f92a9244 - 00000000000000000000000000000000000000000000000000000000000000a00000 - 0000000000000000000012021043bbaab3b71b2217655787a13d24cf618b00000000 - 000000000000000000000000000000000000000000000000603c6a1e000000000000 - 00000000000000000000000000000000000000000000000000020000000000000000 - 00000000fe9a29ab92522d14fc65880d817214261d8479ae00000000000000000000 - 0000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - ) as &[u8], - &input - ); - } else { - panic!("decoded into wrong transaction type"); - } - } - - #[ignore] - #[test] - fn decode_eip2930_transaction() { - // https://etherscan.io/tx/0xfbf20efe99271206c0f5b497a92bee2e66f8bf9991e07648935194f17610b36e - let raw = hex!( - "01f8bb01808522ecb25c008307a120942a48420d75777af4c99970c0ed3c25effd1c08b - e80843ccfd60bf84ff794fbfed54d426217bf75d2ce86622c1e5faf16b0a6e1a00000000 - 000000000000000000000000000000000000000000000000000000000d694d9db270c1b5 - e3bd161e8c8503c55ceabee709552c080a03057d1077af1fc48bdfe2a8eac03caf686145 - b52342e77ad6982566fe39e0691a00507044aa767a50dc926d0daa4dd616b1e5a8d2e578 - 1df5bc9feeee5a5139d61" - ); - - let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); - - // test if the right transaction type was detected - assert!(matches!(transaction.transaction, Transaction::EIP2930 { .. })); - - // test transaction hash computation: - assert_eq!( - H256::from_slice(&hex!( - "fbf20efe99271206c0f5b497a92bee2e66f8bf9991e07648935194f17610b36e" - )), - transaction.hash() - ); - - // test sender recovery - assert_eq!( - H160::from_slice(&hex!("4e2b6cc39e22026d8ce21214646a657ab7eb92b3")), - transaction.sender_address().unwrap() - ); - - if let Transaction::EIP2930 { - chain_id, - nonce, - gas_price, - gas_limit, - action, - value, - input, - access_list, - } = transaction.transaction - { - assert_eq!(1, chain_id); - assert_eq!(0, nonce); - assert_eq!(U256::from(150000000000u64), gas_price); - assert_eq!(500000, gas_limit); - assert_eq!(U256::zero(), value); - assert_eq!( - TransactionAction::Call(H160::from_slice(&hex!( - "2a48420d75777af4c99970c0ed3c25effd1c08be" - ))), - action - ); - assert_eq!(&hex!("3ccfd60b") as &[u8], &input); - assert_eq!( - vec![ - AccessListItem { - address: H160::from_slice(&hex!( - "fbfed54d426217bf75d2ce86622c1e5faf16b0a6" - )), - slots: vec![H256::from_slice(&hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - ))] - }, - AccessListItem { - address: H160::from_slice(&hex!( - "d9db270c1b5e3bd161e8c8503c55ceabee709552" - )), - slots: vec![] - } - ], - access_list - ) - } else { - panic!("decoded into wrong transaction type"); - } - } - - #[ignore] - #[test] - fn decode_eip1559_transaction() { - // https://etherscan.io/tx/0x734678f719001015c5b5f5cbac6a9210ede7ee6ce63e746ff2e9eecda3ab68c7 - let raw = hex!( - "02f8720104843b9aca008504eb6480bc82520894f76c5b19e86c256 - 482f4aad1dae620a0c3ac0cd68717699d954d540080c080a05a5206a8e0486b8e101bcf - 4ed5b290df24a4d54f1ca752c859fa19c291244b98a0177166d96fd69db70628d99855b - 400c8a149b2254c211a0a00645830f5338218" - ); - - let transaction = SignedTransaction::try_from(&raw[..]).unwrap(); - - // test if the right transaction type was detected - assert!(matches!(transaction.transaction, Transaction::EIP1559 { .. })); - - // test transaction hash computation: - assert_eq!( - H256::from_slice(&hex!( - "734678f719001015c5b5f5cbac6a9210ede7ee6ce63e746ff2e9eecda3ab68c7" - )), - transaction.hash() - ); - - // test sender recovery - assert_eq!( - H160::from_slice(&hex!("d882fab949fe224befd0e85afcc5f13d67980102")), - transaction.sender_address().unwrap() - ); - - if let Transaction::EIP1559 { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - action, - value, - input, - access_list, - } = transaction.transaction - { - assert_eq!(1, chain_id); - assert_eq!(4, nonce); - assert_eq!(21000, gas_limit); - assert_eq!(U256::from(6590050000000000u64), value); - assert_eq!(U256::from(1000000000), max_priority_fee_per_gas); - assert_eq!(U256::from(21129101500u64), max_fee_per_gas); - assert_eq!(&[] as &[u8], &input); - assert_eq!(Vec::::new(), access_list); - assert_eq!( - TransactionAction::Call(H160::from_slice(&hex!( - "f76c5b19e86c256482f4aad1dae620a0c3ac0cd6" - ))), - action - ); - } else { - panic!("decoded into wrong transaction type"); - } - } -} diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index c51ed2728..afe3787c4 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -4,21 +4,13 @@ #![allow(clippy::ptr_offset_with_cast, clippy::assign_op_pattern)] use { - fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}, - fvm_shared::bigint::BigInt, - fvm_shared::econ::TokenAmount, - impl_serde::{impl_fixed_hash_serde, impl_uint_serde}, - std::cmp::Ordering, - uint::construct_uint, + fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, impl_serde::impl_uint_serde, + std::cmp::Ordering, uint::construct_uint, }; construct_uint! { pub struct U256(4); } // ethereum word size construct_uint! { pub struct U512(8); } // used for addmod and mulmod opcodes -construct_fixed_hash! { pub struct H160(20); } // ethereum address -construct_fixed_hash! { pub struct H256(32); } // Keccak256 -impl_fixed_hash_conversions!(H256, H160); - impl From<&TokenAmount> for U256 { fn from(amount: &TokenAmount) -> U256 { let (_, bytes) = amount.atto().to_bytes_be(); @@ -39,8 +31,6 @@ impl From<&U256> for TokenAmount { // IPLD structures seamlessly impl_uint_serde!(U256, 4); impl_uint_serde!(U512, 8); -impl_fixed_hash_serde!(H160, 20); -impl_fixed_hash_serde!(H256, 32); macro_rules! impl_hamt_hash { ($type:ident) => { @@ -57,22 +47,6 @@ fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { &v[v.iter().take_while(|&&b| b == 0).count()..] } -macro_rules! impl_rlp_codec_hash { - ($type:ident) => { - impl rlp::Encodable for $type { - fn rlp_append(&self, s: &mut rlp::RlpStream) { - let bytes = self.as_fixed_bytes(); - s.encoder().encode_value(&bytes[..]); - } - } - impl rlp::Decodable for $type { - fn decode(rlp: &rlp::Rlp) -> Result { - rlp.decoder().decode_value(|bytes| Ok($type::from_slice(bytes))) - } - } - }; -} - macro_rules! impl_rlp_codec_uint { ($type:ident, $bytes_len: expr) => { impl rlp::Encodable for $type { @@ -94,15 +68,10 @@ macro_rules! impl_rlp_codec_uint { } // Hamt support -impl_hamt_hash!(H160); -impl_hamt_hash!(H256); - impl_hamt_hash!(U256); impl_hamt_hash!(U512); // RLP Support -impl_rlp_codec_hash!(H160); -impl_rlp_codec_hash!(H256); impl_rlp_codec_uint!(U256, 32); impl_rlp_codec_uint!(U512, 64); @@ -128,18 +97,6 @@ pub fn u128_words_to_u256(high: u128, low: u128) -> U256 { U256::from_big_endian(&bytes) } -#[inline] -pub(crate) fn _u256_to_address(v: U256) -> H160 { - let mut bytes = [0u8; 32]; - v.to_big_endian(&mut bytes); - H160::from_slice(&bytes) -} - -#[inline] -pub fn address_to_u256(v: H160) -> U256 { - U256::from_big_endian(v.as_bytes()) -} - const SIGN_BITMASK_U128: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; const FLIPH_BITMASK_U128: u128 = 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; From 320989b7916a9f02eab0803bf278ab5be5a24683 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 8 Sep 2022 11:16:03 -0700 Subject: [PATCH 040/339] fix: cleanup runtime dependencies. (#623) - Fixes the non-test build by making libsecp256k1 _optional_ unless we're testing. - Makes a lot of dependencies "test only". - Removes some unnecessary dependencies. --- Cargo.lock | 35 +++++++++++++++++++++++++++-------- actors/evm/Cargo.toml | 1 - runtime/Cargo.toml | 28 +++++++++++++++++----------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23efc3e85..3539f9b3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,15 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -575,7 +584,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer", + "block-buffer 0.10.3", "crypto-common", ] @@ -686,7 +695,6 @@ dependencies = [ "hex", "hex-literal", "impl-serde", - "libsecp256k1", "log", "multihash", "near-blake2", @@ -883,7 +891,6 @@ name = "fil_actors_runtime" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "base64", "blake2b_simd", "byteorder", "cid", @@ -895,8 +902,6 @@ dependencies = [ "fvm_sdk", "fvm_shared", "hex", - "indexmap", - "integer-encoding", "itertools", "lazy_static", "libsecp256k1", @@ -908,7 +913,7 @@ dependencies = [ "regex", "serde", "serde_repr", - "sha2", + "sha2 0.10.5", "thiserror", "unsigned-varint", ] @@ -1183,7 +1188,7 @@ dependencies = [ "multihash", "once_cell", "serde", - "sha2", + "sha2 0.10.5", "thiserror", ] @@ -1425,6 +1430,7 @@ dependencies = [ "libsecp256k1-gen-genmult", "rand", "serde", + "sha2 0.9.9", ] [[package]] @@ -1498,7 +1504,7 @@ dependencies = [ "ripemd", "serde", "serde-big-array", - "sha2", + "sha2 0.10.5", "sha3", "unsigned-varint", ] @@ -1976,6 +1982,19 @@ dependencies = [ "digest 0.10.3", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.5" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 7c7dbde35..f5c6ed1cc 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -43,7 +43,6 @@ near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/ne [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } -libsecp256k1 = { version = "0.7.0", features = ["static-context"], default-features = false } hex-literal = "0.3.4" etk-asm = "^0.2.1" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index aa581d23c..485321250 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -16,29 +16,35 @@ num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } lazy_static = "1.4.0" 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" -hex = { version = "0.4.3", optional = true } anyhow = "1.0.56" -fvm_sdk = { version = "3.0.0-alpha.2", optional = true } -blake2b_simd = "1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.2.2" multihash = { version = "0.16.1", default-features = false } -rand = { version = "0.8.5", default-features = false } serde_repr = "0.1.8" regex = "1" itertools = "0.10" -libsecp256k1 = { version = "0.7.1", default-features = false } +# A fake-proofs dependency but... we can't select on that feature here because we enable it from +# build.rs. +sha2 = "0.10" + +# fil-actor +fvm_sdk = { version = "3.0.0-alpha.2", optional = true } + +# 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 } -[dependencies.sha2] -version = "0.10" +[dependencies.libsecp256k1] +version = "0.7.1" +default-features = false +features = ["static-context", "std"] +optional = true [dev-dependencies] derive_builder = "0.10.2" @@ -79,4 +85,4 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2"] +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand"] From 4507f15f9b472af5c1f6390d8b4daeece61ae0bb Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 4 Aug 2022 08:08:42 +0200 Subject: [PATCH 041/339] set codegen units to 1 for wasm (#512) --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index f94f26d5a..266f1855f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,3 +95,4 @@ overflow-checks = true lto = "thin" opt-level = "z" strip = true +codegen-units = 1 From d1730d01d8943e6171d16e653bd49352f2ca5de5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Aug 2022 21:09:50 -0700 Subject: [PATCH 042/339] ci: ensure that Cargo.lock is up-to-date (#544) fixes #105 --- .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5f7ad7a4..41fe639b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: uses: ./.github/actions/rust-cargo-run with: command: clippy - args: --all --all-targets -- -D warnings + args: --locked --all --all-targets -- -D warnings components: clippy github_token: ${{ secrets.GITHUB_TOKEN }} @@ -46,7 +46,7 @@ jobs: uses: ./.github/actions/rust-cargo-run with: command: test - args: --all + args: --locked --all --no-fail-fast github_token: ${{ secrets.GITHUB_TOKEN }} build: @@ -69,7 +69,7 @@ jobs: env: BUILD_FIL_NETWORK: ${{ matrix.network }} run: | - cargo run -- -o output/builtin-actors.car + 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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 355743475..9b007046e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: env: BUILD_FIL_NETWORK: ${{ matrix.network }} run: | - cargo run -- -o output/builtin-actors.car + cargo run --locked -- -o output/builtin-actors.car - name: Publishing release and uploading bundle to GitHub env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From cef12332d3be89a699d4caa184c05eabf2e606ff Mon Sep 17 00:00:00 2001 From: Macro Hoober <59944291+macro-ss@users.noreply.github.com> Date: Wed, 17 Aug 2022 11:41:18 +0800 Subject: [PATCH 043/339] refine check-clippy with makefile (#552) --- .github/workflows/ci.yml | 7 +------ Makefile | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41fe639b2..5b787ac2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,12 +30,7 @@ jobs: - name: Checking out uses: actions/checkout@v2 - name: Running clippy - uses: ./.github/actions/rust-cargo-run - with: - command: clippy - args: --locked --all --all-targets -- -D warnings - components: clippy - github_token: ${{ secrets.GITHUB_TOKEN }} + run: make check test: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index a5f6fe844..7ae0c1b12 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,12 @@ VERSION ?= $(error VERSION environment variable must be set) # Run cargo check check: deps-build - cargo check --workspace --tests --benches --lib --bins --examples + cargo clippy --all --all-targets -- -D warnings # Ensure we have the build dependencies deps-build: rustup target add wasm32-unknown-unknown + rustup component add clippy # Run cargo test test: deps-build From b36ebd1b2bc9d8ff7f6c5ab17b5001c9edeebff5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 8 Sep 2022 18:55:04 -0700 Subject: [PATCH 044/339] move rustfmt CI job into makefile (#557) --- .github/workflows/ci.yml | 7 +------ Makefile | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b787ac2c..0993c46c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,12 +17,7 @@ jobs: - name: Checking out uses: actions/checkout@v2 - name: Running rustfmt - uses: ./.github/actions/rust-cargo-run - with: - command: fmt - args: -- --check - components: rustfmt - github_token: ${{ secrets.GITHUB_TOKEN }} + run: make rustfmt check-clippy: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 7ae0c1b12..7d709ea5c 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ SHELL=/usr/bin/env bash BUMP ?= patch VERSION ?= $(error VERSION environment variable must be set) +# Run cargo fmt +rustfmt: deps-build + cargo fmt -- --check + # Run cargo check check: deps-build cargo clippy --all --all-targets -- -D warnings @@ -11,7 +15,7 @@ check: deps-build # Ensure we have the build dependencies deps-build: rustup target add wasm32-unknown-unknown - rustup component add clippy + rustup component add clippy rustfmt # Run cargo test test: deps-build From 013ec9e2fd3be7b58930c697186fe50742226069 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Wed, 31 Aug 2022 01:50:14 +0200 Subject: [PATCH 045/339] Add code coverage report (#120) Generate a code coverage report and add it as artifact as well as uploading it to CodeCov. Co-authored-by: raulk Co-authored-by: Piotr Galar Co-authored-by: Alex <445306+anorth@users.noreply.github.com> --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++++++++++ rust-toolchain | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0993c46c7..a94d02d10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,3 +64,63 @@ jobs: 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: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_INCREMENTAL: 0 + CACHE_SKIP_SAVE: ${{ matrix.push == '' || matrix.push == 'false' }} + steps: + - name: Checking out + uses: actions/checkout@v2 + - name: Install Rust toolchain + uses: ./.github/actions/rust-cargo-run + with: + command: version + components: llvm-tools-preview + github_token: ${{ secrets.GITHUB_TOKEN }} + - 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 + - name: Install demangler + run: cargo install rustfilt + - name: Create coverage report + env: + # Incremental build is not supported when profiling. + CARGO_INCREMENTAL: 0 + # Make sure that each run of an executable creates a new profile file, + # with the default name they would override each other. + LLVM_PROFILE_FILE: "%m.profraw" + # -Cinstrument-coverage: enable llvm coverage instrumentation + # -Ccodegen-units=1: building in parallel is not supported when profiling + # -Copt-level=0: disable optimizations for more accurate coverage + # -Coverflow-checks=off: checking for overflow is not needed for coverage reporting + # -Cinline-threshold=0: do not inline + # For code coverage the `-Clink-dead-code` flag should be set, as dead + # code should be considered as not covered code. Though this would make + # the build fail, hence it is not set. + RUSTFLAGS: -Cinstrument-coverage -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Cinline-threshold=0 + run: cargo test --workspace --exclude fil_builtin_actors_bundle + - name: Merge profiling data + # Do *not* use sparse output. It leads to more lines that are not taken + # into account at all + run: llvm-profdata merge --output=default.profdata $(find . -name '*.profraw') + - name: Create HTML coverage report + # The compiled files contain the coverage information. From running the + # tests we don't know what those files are called, hence use all files + # from the `./target/debug/deps` directory which don't have an extension. + run: | + OBJECT_OPTIONS=($(find ./target/debug/deps/* -name '*' -not -name '*\.*' -printf ' --object %p')) + # Create HTML report of this project, we don't care about coverage of + # dependencies + llvm-cov show --Xdemangler=rustfilt --show-expansions --show-line-counts-or-regions --ignore-filename-regex=".cargo|.rustup|/rustc|./tests/" --format=html --output-dir=./llvm-show --instr-profile=default.profdata ${OBJECT_OPTIONS[@]} + # Create file to be uploaded to codecov + llvm-cov export --ignore-filename-regex=".cargo|.rustup|/rustc|./tests" --format=lcov --instr-profile=default.profdata ${OBJECT_OPTIONS[@]} > lcov.info + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: code-coverage-report + path: llvm-show/* + - name: Upload coverage to Codecov + uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + files: lcov.info diff --git a/rust-toolchain b/rust-toolchain index 7cc6ef413..af92bdd9f 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.63.0 \ No newline at end of file +1.63.0 From ba1fd1ffc8641ff1e70ed9c60306c3b56bc13ece Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 8 Sep 2022 10:42:25 -0700 Subject: [PATCH 046/339] fix: check code both with all targets and without (#622) Otherwise, rust will "unify" the features between the test targets and the non-test targets, so we'll miss non-test dependency issues. --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7d709ea5c..60f7f0734 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,15 @@ VERSION ?= $(error VERSION environment variable must be set) # Run cargo fmt rustfmt: deps-build - cargo fmt -- --check + cargo fmt --all --check + +# NOTE: Check all targets, then check the build target specifically. Otherwise, it might build for +# testing but not otherwise due to feature resolution shenanigans. # Run cargo check check: deps-build cargo clippy --all --all-targets -- -D warnings + cargo clippy --all -- -D warnings # Ensure we have the build dependencies deps-build: From 87e40847621c63865c20702d2f911275709a1c83 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 9 Sep 2022 10:50:26 +0300 Subject: [PATCH 047/339] EVM: some small fixes (#621) * add note about gas * barf with jumbo output addresses; can't do more than 32 bits anyway in wasm. * add TODO comment about output memory limit --- actors/evm/src/interpreter/instructions/call.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index b7c5f87b5..6996a718f 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -114,6 +114,8 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( let ExecutionState { stack, memory, .. } = state; let rt = &*platform.rt; // as immutable reference + // NOTE gas is currently ignored as FVM's send doesn't allow the caller to specify a gas + // limit (external invocation gas limit applies). This may changed in the future. let (_gas, dst, value, input_offset, input_size, output_offset, output_size) = match kind { CallKind::Call | CallKind::CallCode => ( stack.pop(), @@ -200,11 +202,13 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( state.return_data = result.clone().into(); // copy return data to output region if it is non-zero + // TODO this limits addressable output to 2G (31 bits full), + // but it is still probably too much and we should consistently limit further. + // See also https://github.com/filecoin-project/ref-fvm/issues/851 let output_usize = if output_size.bits() < 32 { output_size.as_usize() } else { - // XXX that's probably a bug, should we barf instead? - 1 << 31 + return Err(StatusCode::InvalidMemoryAccess); }; if output_usize > 0 { let output_region = get_memory_region(memory, output_offset, output_size) From bf3f20b9076258989738c1fbc26716a98c7e64fa Mon Sep 17 00:00:00 2001 From: raulk Date: Sat, 10 Sep 2022 07:41:37 +0100 Subject: [PATCH 048/339] EVM runtime: implement GetBytecode and GetStorageAt getters. (#631) * EVM runtime: implement GetBytecode and GetStorageAt getters. GetBytecode returns the CID of the bytecode block. In the future, the FVM's reachability analysis should add this CID to the reachable set of the caller. GetBytecode is publicly callable. GetStorageAt returns the value stored in the supplied EVM storage key. This method is private and only intended for calls from instrumentation and client functionality, such as the eth_getStorageAt JSON-RPC API operation. In order to make it private, it validates that the caller is the zero address. It needs to be private to preserve EVM guarantees: no contract is able to arbitrarily access storage keys from another contract. The client must form a message from the 0x0 sender address for this method to work. * it's not scala! Co-authored-by: vyzo --- .../evm/src/interpreter/instructions/call.rs | 8 +- actors/evm/src/lib.rs | 58 ++++++++ actors/evm/tests/basic.rs | 124 ++++++++++++++++++ 3 files changed, 186 insertions(+), 4 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 6996a718f..01ccd0253 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -205,11 +205,11 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( // TODO this limits addressable output to 2G (31 bits full), // but it is still probably too much and we should consistently limit further. // See also https://github.com/filecoin-project/ref-fvm/issues/851 - let output_usize = if output_size.bits() < 32 { - output_size.as_usize() - } else { + if output_size.bits() >= 32 { return Err(StatusCode::InvalidMemoryAccess); - }; + } + let output_usize = output_size.as_usize(); + if output_usize > 0 { let output_region = get_memory_region(memory, output_offset, output_size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 2174dfe4f..262bd5fe4 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -5,6 +5,7 @@ use { crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, crate::state::State, bytes::Bytes, + cid::Cid, fil_actors_runtime::{ actor_error, cbor, runtime::{ActorCode, Runtime}, @@ -32,6 +33,8 @@ const MAX_CODE_SIZE: usize = 24 << 10; pub enum Method { Constructor = METHOD_CONSTRUCTOR, InvokeContract = 2, + GetBytecode = 3, + GetStorageAt = 4, } pub struct EvmContractActor; @@ -173,6 +176,48 @@ impl EvmContractActor { let output = RawBytes::from(exec_status.output_data.to_vec()); Ok(output) } + + pub fn bytecode(rt: &mut RT) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + // 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()?; + Ok(state.bytecode) + } + + pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + // 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([&fvm_shared::address::Address::new_id(0)])?; + + let state: State = rt.state()?; + let blockstore = rt.store().clone(); + + // load the storage HAMT + let mut hamt = + Hamt::<_, _, U256>::load(&state.contract_state, blockstore).map_err(|e| { + ActorError::illegal_state(format!( + "failed to load storage HAMT on invoke: {e:?}, e" + )) + })?; + + let mut system = System::new(rt, &mut hamt).map_err(|e| { + ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) + })?; + + system + .get_storage(params.storage_key) + .map_err(|st| ActorError::unspecified(format!("failed to get storage key: {}", &st)))? + .ok_or_else(|| ActorError::not_found(String::from("storage key not found"))) + } } impl ActorCode for EvmContractActor { @@ -193,6 +238,14 @@ impl ActorCode for EvmContractActor { Some(Method::InvokeContract) => { Self::invoke_contract(rt, cbor::deserialize_params(params)?) } + Some(Method::GetBytecode) => { + let cid = Self::bytecode(rt)?; + Ok(RawBytes::serialize(cid)?) + } + Some(Method::GetStorageAt) => { + let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(value)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } @@ -208,3 +261,8 @@ pub struct ConstructorParams { pub struct InvokeParams { pub input_data: RawBytes, } + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetStorageAtParams { + pub storage_key: U256, +} diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 68b9f1941..4e746a977 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -1,6 +1,11 @@ +mod asm; + +use cid::Cid; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::ActorError; +use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -72,3 +77,122 @@ fn basic_contract_construction_and_invocation() { .unwrap(); 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 = MockRuntime::default(); + + rt.expect_validate_caller_any(); + let params = + evm::ConstructorParams { bytecode: init_code.into(), input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + rt.reset(); + + rt.expect_validate_caller_any(); + let returned_bytecode_cid: Cid = rt + .call::(evm::Method::GetBytecode as u64, &Default::default()) + .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 = MockRuntime::default(); + + rt.expect_validate_caller_any(); + let params = + evm::ConstructorParams { bytecode: init_code.into(), input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + 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, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + rt.reset(); + + assert_eq!(U256::from(0xfffa), value); + + // + // Get a storage key that doesn't exist. + // + let params = evm::GetStorageAtParams { storage_key: 0xaaaa.into() }; + + rt.expect_validate_caller_addr(vec![sender]); + let ret = rt.call::( + evm::Method::GetStorageAt as u64, + &RawBytes::serialize(params).unwrap(), + ); + rt.verify(); + + assert_eq!(ActorError::not_found("storage key not found".to_string()), ret.err().unwrap()); +} From f5be9a98a8f12b66eb2c2f21271056216096f1f6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 12 Sep 2022 15:21:02 +0300 Subject: [PATCH 049/339] EVM: Implement CALLACTOR/METHODNUM and REVERT abort semantics (#633) * implement CALLACTOR/METHODNUM * signal EVM_CONTRACT_REVERTED error on reverts * handle REVERT in CALL/CALLACTOR result * update comments about REVERT semantics * simplify call error handling * fix tests * fix empty input data handling for calls * add mnemonic opcodes for CALLACTOR and METHODNUM to assembler such a hack, but no other way. * add methodnum and callactor tests * rustfmt * lift return * fix comment --- actors/evm/src/interpreter/execution.rs | 15 +- .../evm/src/interpreter/instructions/call.rs | 128 ++++++++++----- .../src/interpreter/instructions/memory.rs | 6 +- actors/evm/src/interpreter/opcode.rs | 6 + actors/evm/src/interpreter/output.rs | 2 +- actors/evm/src/lib.rs | 63 ++++---- actors/evm/tests/asm.rs | 19 ++- actors/evm/tests/basic.rs | 20 +-- actors/evm/tests/calc.rs | 30 ++-- actors/evm/tests/call.rs | 146 +++++++++++++++++- actors/evm/tests/opcodes/callactor.hex | 1 + actors/evm/tests/opcodes/methodnum.hex | 1 + actors/evm/tests/precompile.rs | 12 +- actors/evm/tests/selfdestruct.rs | 8 +- 14 files changed, 332 insertions(+), 125 deletions(-) create mode 100644 actors/evm/tests/opcodes/callactor.hex create mode 100644 actors/evm/tests/opcodes/methodnum.hex diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 848c22966..26f099f54 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -21,6 +21,7 @@ use { pub struct ExecutionState { pub stack: Stack, pub memory: Memory, + pub method: u64, pub input_data: Bytes, pub return_data: Bytes, pub output_data: Bytes, @@ -29,10 +30,11 @@ pub struct ExecutionState { } impl ExecutionState { - pub fn new(input_data: Bytes) -> Self { + pub fn new(method: u64, input_data: Bytes) -> Self { Self { stack: Stack::default(), memory: Memory::default(), + method, input_data, return_data: Default::default(), output_data: Bytes::new(), @@ -849,6 +851,17 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { lifecycle::selfdestruct(m.runtime, m.system)?; Ok(ControlFlow::Exit) // selfdestruct halts the current context } + + // FEVM extensions opcodes + CALLACTOR(m) { + call::callactor(m.runtime, m.system)?; + Ok(ControlFlow::Continue) + } + + METHODNUM(m) { + call::methodnum(m.runtime); + Ok(ControlFlow::Continue) + } } const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 01ccd0253..4d56621d3 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -9,8 +9,7 @@ use { crate::interpreter::System, crate::interpreter::U256, crate::RawBytes, - crate::{InvokeParams, Method}, - fil_actors_runtime::runtime::builtins::Type as ActorType, + crate::{Method, EVM_CONTRACT_REVERTED}, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, fvm_shared::econ::TokenAmount, @@ -141,50 +140,30 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( let input_region = get_memory_region(memory, input_offset, input_size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; - let mut result = { + let result = { // ref to memory is dropped after calling so we can mutate it on output later - let input_data = input_region - .map(|MemoryRegion { offset, size }| &memory[offset..][..size.get()]) - .ok_or(StatusCode::InvalidMemoryAccess)?; + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + &[] + }; if precompiles::Precompiles::::is_precompile(&dst) { - precompiles::Precompiles::call_precompile(rt, dst, input_data) - .map_err(|_| StatusCode::PrecompileFailure)? + let result = precompiles::Precompiles::call_precompile(rt, dst, input_data) + .map_err(|_| StatusCode::PrecompileFailure)?; + Ok(RawBytes::from(result)) } else { - // CALL and its brethren can only invoke other EVM contracts; see the (magic) - // CALLMETHOD/METHODNUM opcodes for calling fil actors with native call - // conventions. let dst_addr = Address::try_from(dst)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; - let dst_code_cid = rt - .get_actor_code_cid( - &rt.resolve_address(&dst_addr).ok_or_else(|| { - StatusCode::BadAddress("cannot resolve address".to_string()) - })?, - ) - .ok_or_else(|| StatusCode::BadAddress("unknown actor".to_string()))?; - let evm_code_cid = rt.get_code_cid_for_type(ActorType::EVM); - if dst_code_cid != evm_code_cid { - return Err(StatusCode::BadAddress("cannot call non EVM actor".to_string())); - } - match kind { - CallKind::Call => { - let params = InvokeParams { input_data: RawBytes::from(input_data.to_vec()) }; - let result = rt.send( - &dst_addr, - Method::InvokeContract as u64, - RawBytes::serialize(params).map_err(|_| { - StatusCode::InternalError( - "failed to marshall invocation data".to_string(), - ) - })?, - TokenAmount::from(&value), - ); - result.map_err(StatusCode::from)?.to_vec() - } + CallKind::Call => rt.send( + &dst_addr, + Method::InvokeContract as u64, + RawBytes::from(input_data.to_vec()), + TokenAmount::from(&value), + ), CallKind::DelegateCall => { todo!() } @@ -198,6 +177,19 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( } }; + if let Err(ae) = result { + return if ae.exit_code() == EVM_CONTRACT_REVERTED { + // reverted -- we don't have return data yet + // push failure + stack.push(U256::zero()); + Ok(()) + } else { + Err(StatusCode::from(ae)) + }; + } + + let mut result = result.unwrap().to_vec(); + // save return_data state.return_data = result.clone().into(); @@ -233,3 +225,65 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( stack.push(U256::from(1)); Ok(()) } + +pub fn callactor<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) -> Result<(), StatusCode> { + let ExecutionState { stack, memory, .. } = state; + let rt = &*platform.rt; // as immutable reference + + // stack: GAS DEST VALUE METHODNUM INPUT-OFFSET INPUT-SIZE + // NOTE: we don't need output-offset/output-size (which the CALL instructions have) + // becase these are kinda useless; we can just use RETURNDATA anyway. + // NOTE: gas is currently ignored + let _gas = stack.pop(); + let dst = stack.pop(); + let value = stack.pop(); + let method = stack.pop(); + let input_offset = stack.pop(); + let input_size = stack.pop(); + + let input_region = get_memory_region(memory, input_offset, input_size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; + + let result = { + let dst_addr = Address::try_from(dst)? + .as_id_address() + .ok_or_else(|| StatusCode::BadAddress(format!("not an actor id address: {}", dst)))?; + + if method.bits() > 64 { + return Err(StatusCode::ArgumentOutOfRange(format!("bad method number: {}", method))); + } + let methodnum = method.as_u64(); + + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + &[] + } + .to_vec(); + rt.send(&dst_addr, methodnum, RawBytes::from(input_data), TokenAmount::from(&value)) + }; + + if let Err(ae) = result { + return if ae.exit_code() == EVM_CONTRACT_REVERTED { + // reverted -- we don't have return data yet + // push failure + stack.push(U256::zero()); + Ok(()) + } else { + Err(StatusCode::from(ae)) + }; + } + + // save return_data + state.return_data = result.unwrap().to_vec().into(); + // push success + stack.push(U256::from(1)); + Ok(()) +} + +pub fn methodnum(state: &mut ExecutionState) { + state.stack.push(U256::from(state.method)); +} diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index c73a8bda6..892e27434 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -35,7 +35,7 @@ fn get_memory_region_u64( offset: U256, size: NonZeroUsize, ) -> Result { - if offset > U256::from(u32::MAX) { + if offset.bits() >= 32 { return Err(()); } @@ -55,11 +55,11 @@ pub fn get_memory_region( offset: U256, size: U256, ) -> Result, ()> { - if size == U256::zero() { + if size.is_zero() { return Ok(None); } - if size > U256::from(u32::MAX) { + if size.bits() >= 32 { return Err(()); } diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs index f99413bd6..d687d80cf 100644 --- a/actors/evm/src/interpreter/opcode.rs +++ b/actors/evm/src/interpreter/opcode.rs @@ -213,6 +213,12 @@ def_opcodes! { 0xa2: LOG2(4, -4), 0xa3: LOG3(5, -5), 0xa4: LOG4(6, -6), + ////////////////////////////////////////////////////////// + // FEVM extension opcodes + // FIL call conventions + 0xb0: CALLACTOR(6, -5), + 0xb1: METHODNUM(0, 1), + ////////////////////////////////////////////////////////// 0xf0: CREATE(3, -2), 0xf1: CALL(7, -6), 0xf2: CALLCODE(7, -6), diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index a135acae1..1fd74e254 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -94,7 +94,7 @@ pub enum StatusCode { /// An argument to a state accessing method has a value outside of the /// accepted range of values. #[strum(serialize = "argument out of range")] - ArgumentOutOfRange, + ArgumentOutOfRange(String), /// The caller does not have enough funds for value transfer. #[strum(serialize = "insufficient balance")] diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 262bd5fe4..03893e73d 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -7,7 +7,7 @@ use { bytes::Bytes, cid::Cid, fil_actors_runtime::{ - actor_error, cbor, + cbor, runtime::{ActorCode, Runtime}, ActorDowncast, ActorError, }, @@ -28,6 +28,8 @@ fil_actors_runtime::wasm_trampoline!(EvmContractActor); /// The contract code size limit is 24kB. const MAX_CODE_SIZE: usize = 24 << 10; +pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(27); + #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -66,7 +68,10 @@ impl EvmContractActor { })?; // create a new execution context - let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + let mut exec_state = ExecutionState::new( + Method::Constructor as u64, + Bytes::copy_from_slice(¶ms.input_data), + ); // identify bytecode valid jump destinations let bytecode = Bytecode::new(¶ms.bytecode) @@ -79,10 +84,15 @@ impl EvmContractActor { _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; - if !exec_status.reverted - && exec_status.status_code == StatusCode::Success - && !exec_status.output_data.is_empty() - { + // TODO this does not return revert data yet, but it has correct semantics. + if exec_status.reverted { + Err(ActorError::unchecked(EVM_CONTRACT_REVERTED, "constructor reverted".to_string())) + } else if exec_status.status_code == StatusCode::Success { + if exec_status.output_data.is_empty() { + return Err(ActorError::unspecified( + "EVM constructor returned empty contract".to_string(), + )); + } // constructor ran to completion successfully and returned // the resulting bytecode. let contract_bytecode = exec_status.output_data; @@ -109,7 +119,8 @@ impl EvmContractActor { pub fn invoke_contract( rt: &mut RT, - params: InvokeParams, + method: u64, + input_data: &RawBytes, ) -> Result where BS: Blockstore + Clone, @@ -139,7 +150,7 @@ impl EvmContractActor { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; - let mut exec_state = ExecutionState::new(Bytes::copy_from_slice(¶ms.input_data)); + let mut exec_state = ExecutionState::new(method, input_data.to_vec().into()); let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { @@ -147,19 +158,20 @@ impl EvmContractActor { _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; - // TODO this is not the correct handling of reverts -- we need to abort (and return the - // output data), so that the entire transaction (including parent/sibling calls) - // can be reverted. - if exec_status.status_code == StatusCode::Success { - if !exec_status.reverted { - // this needs to be outside the transaction or else rustc has a fit about - // mutably borrowing the runtime twice.... sigh. - let contract_state = system.flush_state()?; - rt.transaction(|state: &mut State, _rt| { - state.contract_state = contract_state; - Ok(()) - })?; - } + // TODO this does not return revert data yet, but it has correct semantics. + if exec_status.reverted { + return Err(ActorError::unchecked( + EVM_CONTRACT_REVERTED, + "contract reverted".to_string(), + )); + } else if exec_status.status_code == StatusCode::Success { + // this needs to be outside the transaction or else rustc has a fit about + // mutably borrowing the runtime twice.... sigh. + let contract_state = system.flush_state()?; + rt.transaction(|state: &mut State, _rt| { + state.contract_state = contract_state; + Ok(()) + })?; } else if let StatusCode::ActorError(e) = exec_status.status_code { return Err(e); } else { @@ -236,7 +248,7 @@ impl ActorCode for EvmContractActor { Ok(RawBytes::default()) } Some(Method::InvokeContract) => { - Self::invoke_contract(rt, cbor::deserialize_params(params)?) + Self::invoke_contract(rt, Method::InvokeContract as u64, params) } Some(Method::GetBytecode) => { let cid = Self::bytecode(rt)?; @@ -246,7 +258,7 @@ impl ActorCode for EvmContractActor { let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(value)?) } - None => Err(actor_error!(unhandled_message; "Invalid method")), + None => Self::invoke_contract(rt, method, params), } } } @@ -257,11 +269,6 @@ pub struct ConstructorParams { pub input_data: RawBytes, } -#[derive(Serialize_tuple, Deserialize_tuple)] -pub struct InvokeParams { - pub input_data: RawBytes, -} - #[derive(Serialize_tuple, Deserialize_tuple)] pub struct GetStorageAtParams { pub storage_key: U256, diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index 995448f09..561672374 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -35,7 +35,8 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a // 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; + let body = with_fevm_extensions(body); + 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(); @@ -82,3 +83,19 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a contract_code.append(&mut body_code); Ok(contract_code) } + +// this is a hack to support mnemonics for the FEVM extension opcodes +// it is really ugly, but the etk assmebler doesn't currently support any way to +// directly embed (otherwise invalid) asm instructions in the stream... sigh. +// Ideally we would just do them as macros like +// %macro methodnum() +// 0xb1 +// %end +// Note that to add insult to injury, macros cannot %include_hex... double sigh. +// So f*ck it, we'll just hack this until there is support. +// See also https://github.com/quilt/etk/issues/110 +fn with_fevm_extensions(body: &str) -> String { + body.to_owned() + .replace("@callactor", "%include_hex(\"tests/opcodes/callactor.hex\")") + .replace("@methodnum", "%include_hex(\"tests/opcodes/methodnum.hex\")") +} diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 4e746a977..7b4eb8abb 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -44,15 +44,11 @@ fn basic_contract_construction_and_invocation() { let mut arg0 = vec![0u8; 32]; solidity_params.append(&mut arg0); - let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + let input_data = RawBytes::from(solidity_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(0)); @@ -66,15 +62,11 @@ fn basic_contract_construction_and_invocation() { arg0[31] = 100; // the owner address solidity_params.append(&mut arg0); - let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + let input_data = RawBytes::from(solidity_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(10000)); } diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index 6b0ab0627..537da444a 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -89,15 +89,11 @@ fn test_magic_calc() { // invoke contract -- get_magic let contract_params = vec![0u8; 32]; - let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + let input_data = RawBytes::from(contract_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); @@ -105,15 +101,11 @@ fn test_magic_calc() { let mut contract_params = vec![0u8; 36]; contract_params[3] = 0x01; contract_params[35] = 0x01; - let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + let input_data = RawBytes::from(contract_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(0x43)); @@ -121,15 +113,11 @@ fn test_magic_calc() { let mut contract_params = vec![0u8; 36]; contract_params[3] = 0x02; contract_params[35] = 0x02; - let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + let input_data = RawBytes::from(contract_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(0x84)); } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index d93fc63e7..f0a4f5baa 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -84,11 +84,10 @@ fn test_call() { // dest + method 0 with no data let mut contract_params = vec![0u8; 36]; evm_target_word.to_big_endian(&mut contract_params[..32]); - let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + let input_data = RawBytes::from(contract_params); let proxy_call_contract_params = vec![0u8; 4]; - let proxy_call_params = - evm::InvokeParams { input_data: RawBytes::from(proxy_call_contract_params) }; + let proxy_call_input_data = RawBytes::from(proxy_call_contract_params); // expected return data let mut return_data = vec![0u8; 32]; @@ -98,17 +97,154 @@ fn test_call() { rt.expect_send( target, evm::Method::InvokeContract as u64, - RawBytes::serialize(proxy_call_params).unwrap(), + proxy_call_input_data, TokenAmount::zero(), RawBytes::from(return_data), ExitCode::OK, ); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); +} + +#[allow(dead_code)] +pub fn methodnum_contract() -> Vec { + // a simple contract that just returns the invocation methodnum + let init = ""; + let body = r#" +@methodnum +push1 0x00 +mstore + +push1 0x20 +push1 0x00 +return +"#; + + asm::new_contract("methodnum", init, body).unwrap() +} + +#[test] +fn test_methodnum() { + let mut rt = MockRuntime::default(); + + let contract = methodnum_contract(); + rt.expect_validate_caller_any(); + let params = + evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; let result = rt .call::( - evm::Method::InvokeContract as u64, + evm::Method::Constructor as u64, &RawBytes::serialize(params).unwrap(), ) .unwrap(); + expect_empty(result); + rt.verify(); + + // invoke the contract + rt.expect_validate_caller_any(); + + let result = rt.call::(0x42, &RawBytes::default()).unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); +} + +#[allow(dead_code)] +pub fn callactor_proxy_contract() -> Vec { + let init = ""; + let body = r#" +# this contract takes an address, method and the call payload and proxies a CALLACTOR call +# to that address +# get call payload size +push1 0x40 +calldatasize +sub +# store payload to mem 0x00 +push1 0x40 +push1 0x00 +calldatacopy + +# prepare the proxy call +# input offset and size +push1 0x40 +calldatasize +sub +push1 0x00 +# method +push1 0x20 +calldataload +# value +push1 0x00 +# dest address +push1 0x00 +calldataload +# gas +push1 0x00 +# do the call +@callactor + +# return result through +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("callactor-proxy", init, body).unwrap() +} + +#[test] +fn test_callactor() { + let mut rt = MockRuntime::default(); + + // construct the proxy + let contract = callactor_proxy_contract(); + rt.expect_validate_caller_any(); + let params = + evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // create a mock target and proxy a call through the proxy + let target = FILAddress::new_id(0x100); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + let evm_target = EVMAddress::from_id_address(&target).unwrap(); + let evm_target_word = evm_target.as_evm_word(); + + // dest + method 0x42 with no data + let mut contract_params = vec![0u8; 64]; + evm_target_word.to_big_endian(&mut contract_params[..32]); + contract_params[63] = 0x42; + let input_data = RawBytes::from(contract_params); + + let proxy_call_input_data = RawBytes::default(); + + // expected return data + let mut return_data = vec![0u8; 32]; + return_data[31] = 0x42; + + rt.expect_validate_caller_any(); + rt.expect_send( + target, + 0x42, + proxy_call_input_data, + TokenAmount::zero(), + RawBytes::from(return_data), + ExitCode::OK, + ); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } diff --git a/actors/evm/tests/opcodes/callactor.hex b/actors/evm/tests/opcodes/callactor.hex new file mode 100644 index 000000000..334536890 --- /dev/null +++ b/actors/evm/tests/opcodes/callactor.hex @@ -0,0 +1 @@ +b0 \ No newline at end of file diff --git a/actors/evm/tests/opcodes/methodnum.hex b/actors/evm/tests/opcodes/methodnum.hex new file mode 100644 index 000000000..611d98703 --- /dev/null +++ b/actors/evm/tests/opcodes/methodnum.hex @@ -0,0 +1 @@ +b1 \ No newline at end of file diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index b0bcfec4e..f415e65b3 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -21,7 +21,7 @@ jump # call hash, output written to 0x0200 sha256_hash: jumpdest push1 0x20 # out size (32 bytes) -push2 0x0200 # out offset +push2 0x0200 # out offset push1 0x10 # in size (16 bytes) push2 0x0110 # in offset push1 0x00 # _value @@ -59,15 +59,11 @@ fn test_precompile_hash() { // invoke contract let contract_params = vec![0u8; 32]; - let params = evm::InvokeParams { input_data: RawBytes::from(contract_params) }; + let input_data = RawBytes::from(contract_params); rt.expect_validate_caller_any(); - let result = rt - .call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + let result = + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); let expected = hex_literal::hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index 312cb5615..ad88f8c3f 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -30,14 +30,10 @@ fn test_selfdestruct() { rt.verify(); let solidity_params = hex::decode("35f46994").unwrap(); - let params = evm::InvokeParams { input_data: RawBytes::from(solidity_params) }; + let input_data = RawBytes::from(solidity_params); rt.expect_validate_caller_any(); rt.expect_delete_actor(beneficiary); - rt.call::( - evm::Method::InvokeContract as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); rt.verify(); } From b0778860cb5bbc5057adbd88439c3489ff526de4 Mon Sep 17 00:00:00 2001 From: raulk Date: Mon, 12 Sep 2022 18:42:55 +0100 Subject: [PATCH 050/339] EVM runtime: implement EXT* opcodes. (#632) Co-authored-by: vyzo --- .../evm/src/interpreter/instructions/call.rs | 32 +-- .../evm/src/interpreter/instructions/ext.rs | 92 +++++++-- .../src/interpreter/instructions/memory.rs | 95 ++++++++- actors/evm/src/interpreter/output.rs | 10 + actors/evm/tests/ext_opcodes.rs | 187 ++++++++++++++++++ 5 files changed, 368 insertions(+), 48 deletions(-) create mode 100644 actors/evm/tests/ext_opcodes.rs diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 4d56621d3..11bf7adb6 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,5 +1,5 @@ use { - super::memory::get_memory_region, + super::memory::{copy_to_memory, get_memory_region}, crate::interpreter::address::Address, crate::interpreter::instructions::memory::MemoryRegion, crate::interpreter::output::StatusCode, @@ -188,39 +188,13 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( }; } - let mut result = result.unwrap().to_vec(); + let result = result.unwrap().to_vec(); // save return_data state.return_data = result.clone().into(); // copy return data to output region if it is non-zero - // TODO this limits addressable output to 2G (31 bits full), - // but it is still probably too much and we should consistently limit further. - // See also https://github.com/filecoin-project/ref-fvm/issues/851 - if output_size.bits() >= 32 { - return Err(StatusCode::InvalidMemoryAccess); - } - let output_usize = output_size.as_usize(); - - if output_usize > 0 { - let output_region = get_memory_region(memory, output_offset, output_size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; - let output_data = output_region - .map(|MemoryRegion { offset, size }| &mut memory[offset..][..size.get()]) - .ok_or(StatusCode::InvalidMemoryAccess)?; - - // truncate if needed - let mut result_usize = result.len(); - if result_usize > output_usize { - result_usize = output_usize; - result.truncate(output_usize); - } - - output_data - .get_mut(..result_usize) - .ok_or(StatusCode::InvalidMemoryAccess)? - .copy_from_slice(&result); - } + copy_to_memory(memory, output_offset, output_size, U256::zero(), result.as_slice())?; stack.push(U256::from(1)); Ok(()) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 85c9df959..cb08f5b30 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -1,3 +1,11 @@ +use crate::interpreter::address::Address; +use crate::interpreter::instructions::memory::copy_to_memory; +use crate::U256; +use cid::Cid; +use fil_actors_runtime::runtime::builtins::Type; +use fil_actors_runtime::ActorError; +use fvm_shared::econ::TokenAmount; +use num_traits::Zero; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, @@ -6,30 +14,80 @@ use { #[inline] pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - // TODO - // 1. call actor::get_actor_code_cid - // 2. check that it matches our code CID (it's an EVM actor) - // 3. call GetEvmBytecode method, returns the CID of the EVM bytecode block - // 4. open the block - // 5. return the length - todo!() + let addr = state.stack.pop(); + // TODO 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 = get_evm_bytecode_cid(platform.rt, addr) + .and_then(|cid| get_evm_bytecode(platform.rt, &cid)) + .map(|bytecode| bytecode.len())?; + + state.stack.push(len.into()); + Ok(()) } pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - // TODO - - todo!(); + let addr = state.stack.pop(); + let cid = get_evm_bytecode_cid(platform.rt, addr)?; + let digest = cid.hash().digest(); + // Take the first 32 bytes of the Multihash + let digest_len = digest.len().min(32); + state.stack.push(digest[..digest_len].into()); + Ok(()) } pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!(); + let ExecutionState { stack, .. } = state; + let (addr, dest_offset, data_offset, size) = + (stack.pop(), stack.pop(), stack.pop(), stack.pop()); + let bytecode = get_evm_bytecode_cid(platform.rt, addr) + .and_then(|cid| get_evm_bytecode(platform.rt, &cid))?; + + copy_to_memory(&mut state.memory, dest_offset, size, data_offset, bytecode.as_slice())?; + + Ok(()) +} + +fn get_evm_bytecode_cid>( + rt: &RT, + addr: U256, +) -> Result { + let addr = Address::try_from(addr)? + .as_id_address() + .ok_or_else(|| StatusCode::BadAddress("no support for non-ID addresses yet".to_string()))?; + + let evm_cid = rt.get_code_cid_for_type(Type::EVM); + let target_cid = rt.get_actor_code_cid(&addr.id().expect("not an ID address")); + + if Some(evm_cid) != target_cid { + return Err(StatusCode::InvalidArgument( + "cannot invoke EXTCODESIZE for non-EVM actor".to_string(), + )); // TODO better error code + } + + let cid = rt + .send(&addr, crate::Method::GetBytecode as u64, Default::default(), TokenAmount::zero())? + .deserialize()?; + Ok(cid) +} + +pub fn get_evm_bytecode>( + rt: &RT, + cid: &Cid, +) -> Result, StatusCode> { + let raw_bytecode = rt + .store() + .get(cid) // TODO this is inefficient; should call stat here. + .map_err(|e| StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)))? + .ok_or_else(|| ActorError::not_found("bytecode block not found".to_string()))?; + Ok(raw_bytecode) } diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 892e27434..093966aa8 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -39,13 +39,14 @@ fn get_memory_region_u64( return Err(()); } - let new_size = offset.as_usize() + size.get(); + let offset_usize = offset.as_usize(); + let new_size = offset_usize + size.get(); let current_size = mem.len(); if new_size > current_size { grow_memory(mem, new_size)?; } - Ok(MemoryRegion { offset: offset.as_usize(), size }) + Ok(MemoryRegion { offset: offset_usize, size }) } #[inline] @@ -66,6 +67,51 @@ pub fn get_memory_region( get_memory_region_u64(mem, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some) } +pub fn copy_to_memory( + memory: &mut Memory, + dest_offset: U256, + dest_size: U256, + data_offset: U256, + data: &[u8], +) -> Result<(), StatusCode> { + // TODO this limits addressable output to 2G (31 bits full), + // but it is still probably too much and we should consistently limit further. + // See also https://github.com/filecoin-project/ref-fvm/issues/851 + if dest_size.bits() >= 32 { + return Err(StatusCode::InvalidMemoryAccess); + } + let output_usize = dest_size.as_usize(); + + if data_offset.bits() >= 32 { + return Err(StatusCode::InvalidMemoryAccess); + } + let data_offset_usize = data_offset.as_usize(); + if data_offset_usize > data.len() { + return Err(StatusCode::InvalidMemoryAccess); + } + + if output_usize > 0 { + // Limit the size if we're copying less than the data length. + let mut copy_len = data.len() - data_offset_usize; + if output_usize < copy_len { + copy_len = output_usize; + } + + let output_region = get_memory_region(memory, dest_offset, dest_size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let output_data = output_region + .map(|MemoryRegion { offset, size }| &mut memory[offset..][..size.get()]) + .ok_or(StatusCode::InvalidMemoryAccess)?; + + output_data + .get_mut(..copy_len) + .ok_or(StatusCode::InvalidMemoryAccess)? + .copy_from_slice(&data[data_offset_usize..][..copy_len]); + } + + Ok(()) +} + #[inline] pub fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> { let index = state.stack.pop(); @@ -116,3 +162,48 @@ pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { pub fn msize(state: &mut ExecutionState) { state.stack.push(u64::try_from(state.memory.len()).unwrap().into()); } + +#[cfg(test)] +mod tests { + use super::*; + use crate::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(), &[]); + assert_eq!(result, Err(StatusCode::InvalidMemoryAccess)); + } + + #[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]); + 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); + 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); + assert_eq!(result, Ok(())); + assert_eq!(mem.len(), 32); + assert_eq!(&mem[0..4], result_data); + } +} diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index 1fd74e254..1c052aa65 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -50,6 +50,10 @@ pub enum StatusCode { #[strum(serialize = "invalid instruction")] InvalidInstruction, + /// An argument passed to an instruction does not meet expectations. + #[strum(serialize = "invalid argument")] + InvalidArgument(String), + /// An undefined instruction has been encountered. #[strum(serialize = "undefined instruction")] UndefinedInstruction, @@ -119,3 +123,9 @@ impl From for StatusCode { Self::ActorError(ae) } } + +impl From for StatusCode { + fn from(err: fvm_ipld_encoding::Error) -> Self { + Self::InternalError(format!("IPLD error: {:?}", &err)) + } +} diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs new file mode 100644 index 000000000..be2f5b00b --- /dev/null +++ b/actors/evm/tests/ext_opcodes.rs @@ -0,0 +1,187 @@ +mod asm; + +use cid::Cid; +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address as FILAddress; +use fvm_shared::bigint::Zero; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; + +#[test] +fn test_extcodesize() { + let bytecode: RawBytes = { + let init = ""; + let body = r#" + # get code size of address f088 + push20 0xff00000000000000000000000000000000000088 + extcodesize + # store size at location 0x00 + push1 0x00 + mstore + # return 0x00..0x20 + push1 0x20 + push1 0x00 + return + "#; + + asm::new_contract("extcodesize", init, body).unwrap() + } + .into(); + + let mut rt = MockRuntime::default(); + rt.expect_validate_caller_any(); + let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // 0x88 is an EVM actor + let target = FILAddress::new_id(0x88); + rt.actor_code_cids.insert(target, *EVM_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_validate_caller_any(); + rt.expect_send( + target, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + RawBytes::serialize(&bytecode_cid).unwrap(), + ExitCode::OK, + ); + + let result = rt + .call::(evm::Method::InvokeContract as u64, &Default::default()) + .unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(0x04)); +} + +#[test] +fn test_extcodehash() { + let bytecode: RawBytes = { + let init = ""; + let body = r#" + # get code hash of address f088 + push20 0xff00000000000000000000000000000000000088 + extcodehash + # store size at location 0x00 + push1 0x00 + mstore + # return 0x00..0x20 + push1 0x20 + push1 0x00 + return + "#; + + asm::new_contract("extcodehash", init, body).unwrap() + } + .into(); + + let mut rt = MockRuntime::default(); + rt.expect_validate_caller_any(); + let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // 0x88 is an EVM actor + let target = FILAddress::new_id(0x88); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + // a random CID + let bytecode_cid = + Cid::try_from("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); + + rt.expect_validate_caller_any(); + rt.expect_send( + target, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + RawBytes::serialize(&bytecode_cid).unwrap(), + ExitCode::OK, + ); + + let result = rt + .call::(evm::Method::InvokeContract as u64, &Default::default()) + .unwrap(); + + assert_eq!(U256::from_big_endian(&result), U256::from(&bytecode_cid.hash().digest()[..32])); +} + +#[test] +fn test_extcodecopy() { + let bytecode: RawBytes = { + let init = ""; + let body = r#" + push1 0xff + push1 0x00 + push1 0x00 + push20 0xff00000000000000000000000000000000000088 + extcodecopy + # return 0x00..0x04 + push1 0x04 + push1 0x00 + return + "#; + + asm::new_contract("extcodecopy", init, body).unwrap() + } + .into(); + + let mut rt = MockRuntime::default(); + rt.expect_validate_caller_any(); + let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + // 0x88 is an EVM actor + let target = FILAddress::new_id(0x88); + rt.actor_code_cids.insert(target, *EVM_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_validate_caller_any(); + rt.expect_send( + target, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + RawBytes::serialize(&bytecode_cid).unwrap(), + ExitCode::OK, + ); + + let result = rt + .call::(evm::Method::InvokeContract as u64, &Default::default()) + .unwrap(); + + assert_eq!(other_bytecode.as_slice(), result.bytes()); +} From aa64e5e6467930379f0c8633b65a8e6256a50a40 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 13 Sep 2022 09:20:43 -0700 Subject: [PATCH 051/339] optimized tests & ci caching (#640) * chore: run tests with some optimizations enabled Specifically, optimize bigint, hashing, and the test_vm. Preferiably we'd just optimize everything, but that makes things slow. * ci: save test cache * ci: don't build root bundle in tests We test when building the bundle itself. * ci: rev key and save codecov cache Co-authored-by: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> --- .github/actions/rust-cargo-run/action.yml | 6 +++++- .github/workflows/ci.yml | 7 ++++--- Cargo.toml | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml index e2e0714ed..9d9ae02b6 100644 --- a/.github/actions/rust-cargo-run/action.yml +++ b/.github/actions/rust-cargo-run/action.yml @@ -17,6 +17,10 @@ inputs: save_cache: description: Whether to save the SSCACHE required: false + cache_name: + description: The name of the cache to save/restore + required: true + default: test runs: using: composite @@ -37,7 +41,7 @@ runs: CACHE_SKIP_SAVE: ${{ inputs.save_cache == '' || inputs.save_cache == 'false' }} with: version: v0.2.15 - shared-key: v1 # change this to invalidate sccache for this job + shared-key: v2-${{ 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 a94d02d10..f297b07cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,14 +36,14 @@ jobs: uses: ./.github/actions/rust-cargo-run with: command: test - args: --locked --all --no-fail-fast + args: --locked --all --no-fail-fast --exclude=fil_builtin_actors_bundle github_token: ${{ secrets.GITHUB_TOKEN }} + save_cache: true build: runs-on: ubuntu-latest env: CARGO_INCREMENTAL: 0 - CACHE_SKIP_SAVE: ${{ matrix.push == '' || matrix.push == 'false' }} strategy: matrix: network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] @@ -69,7 +69,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_INCREMENTAL: 0 - CACHE_SKIP_SAVE: ${{ matrix.push == '' || matrix.push == 'false' }} steps: - name: Checking out uses: actions/checkout@v2 @@ -79,6 +78,8 @@ jobs: command: version components: llvm-tools-preview github_token: ${{ secrets.GITHUB_TOKEN }} + 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 - name: Install demangler diff --git a/Cargo.toml b/Cargo.toml index 266f1855f..16c22abff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,3 +96,17 @@ lto = "thin" opt-level = "z" strip = true codegen-units = 1 + +## So tests don't take ages. + +[profile.dev.package."num-bigint"] +opt-level = 2 + +[profile.dev.package."sha2"] +opt-level = 2 + +[profile.dev.package."blake2b_simd"] +opt-level = 2 + +[profile.dev.package."test_vm"] +opt-level = 2 From 7b52f8db05642e22f571734a960784fae4f86129 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 13 Sep 2022 11:00:35 -0700 Subject: [PATCH 052/339] build: avoid RUSTFLAGS (#642) This can cause issues with caching. Unfortunately, we can only specify them per-target, not per-profile. --- .cargo/config.toml | 7 +++++++ .github/workflows/ci.yml | 15 +-------------- Cargo.toml | 8 ++++++++ build.rs | 7 ------- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 7aa36c0b8..1e877e665 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,3 +3,10 @@ rustflags = [ # Force unwrapping Result<_, Err>, especially for tests. "-D", "unused_must_use", ] + +[target.wasm32-unknown-unknown] +rustflags = [ + "-Ctarget-feature=+bulk-memory", + "-Ctarget-feature=+crt-static", + "-Clink-arg=--export-table", +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f297b07cf..f27745098 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,8 +42,6 @@ jobs: build: runs-on: ubuntu-latest - env: - CARGO_INCREMENTAL: 0 strategy: matrix: network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] @@ -68,7 +66,6 @@ jobs: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CARGO_INCREMENTAL: 0 steps: - name: Checking out uses: actions/checkout@v2 @@ -86,20 +83,10 @@ jobs: run: cargo install rustfilt - name: Create coverage report env: - # Incremental build is not supported when profiling. - CARGO_INCREMENTAL: 0 # Make sure that each run of an executable creates a new profile file, # with the default name they would override each other. LLVM_PROFILE_FILE: "%m.profraw" - # -Cinstrument-coverage: enable llvm coverage instrumentation - # -Ccodegen-units=1: building in parallel is not supported when profiling - # -Copt-level=0: disable optimizations for more accurate coverage - # -Coverflow-checks=off: checking for overflow is not needed for coverage reporting - # -Cinline-threshold=0: do not inline - # For code coverage the `-Clink-dead-code` flag should be set, as dead - # code should be considered as not covered code. Though this would make - # the build fail, hence it is not set. - RUSTFLAGS: -Cinstrument-coverage -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Cinline-threshold=0 + RUSTFLAGS: "-Cinstrument-coverage" run: cargo test --workspace --exclude fil_builtin_actors_bundle - name: Merge profiling data # Do *not* use sparse output. It leads to more lines that are not taken diff --git a/Cargo.toml b/Cargo.toml index 16c22abff..f1ca28f83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ lto = "thin" opt-level = "z" strip = true codegen-units = 1 +incremental = false ## So tests don't take ages. @@ -110,3 +111,10 @@ opt-level = 2 [profile.dev.package."test_vm"] opt-level = 2 + +[profile.coverage] +inherits = "test" +incremental = false +codegen-units = 1 +opt-level = 0 +overflow-checks = false diff --git a/build.rs b/build.rs index e52a20867..c7b7b7c28 100644 --- a/build.rs +++ b/build.rs @@ -28,8 +28,6 @@ const ACTORS: &[(&Package, &ID)] = &[ ("evm", "evm"), ]; -const WASM_FEATURES: &[&str] = &["+bulk-memory", "+crt-static"]; - /// Default Cargo features to activate during the build. const DEFAULT_CARGO_FEATURES: &[&str] = &["fil-actor"]; @@ -119,10 +117,6 @@ fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed={}", file); } - let rustflags = - WASM_FEATURES.iter().flat_map(|flag| ["-Ctarget-feature=", *flag, " "]).collect::() - + "-Clink-arg=--export-table"; - // Compute Cargo features to apply. let features = { let extra = EXTRA_CARGO_FEATURES @@ -142,7 +136,6 @@ fn main() -> Result<(), Box> { .arg("--locked") .arg("--features=".to_owned() + &features.join(",")) .arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap()) - .env("RUSTFLAGS", rustflags) .env(NETWORK_ENV, network_name) .stdout(Stdio::piped()) .stderr(Stdio::piped()) From e09dd7b769c3bdd445fca7c3f49f7ea66522093a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 13 Sep 2022 12:17:40 -0700 Subject: [PATCH 053/339] build: optimize bitfield operations in tests (#643) This isn't as big of an issue as hashing, but it still matters for tests. --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f1ca28f83..12b42995b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,9 @@ incremental = false ## So tests don't take ages. +[profile.dev.package."fvm_ipld_bitfield"] +opt-level = 2 + [profile.dev.package."num-bigint"] opt-level = 2 From 7fc40f3c32d835a9781f4276d8666dda5398e2d3 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 22 Aug 2022 15:36:51 +0200 Subject: [PATCH 054/339] Introduce a check for unique Robust Address in the Init Actor Resolves https://github.com/filecoin-project/builtin-actors/issues/567 --- actors/init/src/state.rs | 18 +++++++-- actors/init/tests/init_actor_test.rs | 57 ++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index e1b29567b..5e256789b 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -6,14 +6,13 @@ use anyhow::anyhow; use cid::multihash::Code; use cid::Cid; use fil_actors_runtime::{ - make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, + actor_error, make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; #[cfg(feature = "m2-native")] use fvm_ipld_encoding::CborStore; -use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::{Address, Protocol}; use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; @@ -44,17 +43,28 @@ impl State { } /// Allocates a new ID address and stores a mapping of the argument address to it. + /// Fails if the argument address is already present in the map to facilitate a tombstone + /// for when the predictable robust address generation is implemented. /// Returns the newly-allocated address. pub fn map_address_to_new_id( &mut self, store: &BS, addr: &Address, - ) -> Result { + ) -> anyhow::Result { let id = self.next_id; self.next_id += 1; let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; - map.set(addr.to_bytes().into(), id)?; + let is_new = map.set_if_absent(addr.to_bytes().into(), id)?; + if !is_new { + // this is impossible today as the robust address is a hash of unique inputs + // but in close future predictable address generation will make this possible + return Err(anyhow!(actor_error!( + forbidden, + "robust address {} is already allocated in the address map", + addr + ))); + } self.address_map = map.flush()?; Ok(id) diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 163ce4290..0bfcad7c1 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -48,6 +48,63 @@ fn abort_cant_call_exec() { check_state(&rt); } +#[test] +fn repeated_robust_address() { + let mut rt = construct_runtime(); + construct_and_verify(&mut rt); + + // setup one msig actor + let unique_address = Address::new_actor(b"multisig"); + let fake_params = ConstructorParams { network_name: String::from("fake_param") }; + { + // Actor creating multisig actor + let some_acc_actor = Address::new_id(1234); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, some_acc_actor); + + rt.new_actor_addr = Some(unique_address); + + // Next id + let expected_id = 100; + let expected_id_addr = Address::new_id(expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + + // Expect a send to the multisig actor constructor + rt.expect_send( + expected_id_addr, + METHOD_CONSTRUCTOR, + RawBytes::serialize(&fake_params).unwrap(), + 0u8.into(), + RawBytes::default(), + ExitCode::OK, + ); + + // Return should have been successful. Check the returned addresses + let exec_ret = exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap(); + let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).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); + } + + // Simulate repeated robust address, as it could be a case with predictable address generation + { + rt.new_actor_addr = Some(unique_address); + + rt.expect_validate_caller_any(); + let exec_params = ExecParams { + code_cid: *MULTISIG_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(&fake_params).unwrap(), + }; + + let ret = + rt.call::(Method::Exec as u64, &RawBytes::serialize(&exec_params).unwrap()); + + rt.verify(); + assert!(ret.is_err()); + assert_eq!(ret.unwrap_err().exit_code(), ExitCode::USR_FORBIDDEN) + } +} + #[test] fn create_2_payment_channels() { let mut rt = construct_runtime(); From 0927953302aed8d5e704cc8cd080661a90985227 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 14 Sep 2022 13:00:55 +0300 Subject: [PATCH 055/339] Next: fix broken tests (#648) * fix broken init test * fix broken rand import * only enable rand/std_rng when test_utils is specified --- actors/init/tests/init_actor_test.rs | 4 ++-- runtime/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 0bfcad7c1..4fc2cba2b 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -73,7 +73,7 @@ fn repeated_robust_address() { expected_id_addr, METHOD_CONSTRUCTOR, RawBytes::serialize(&fake_params).unwrap(), - 0u8.into(), + TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); @@ -130,7 +130,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( expected_id_addr, diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 485321250..d46232fee 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -85,4 +85,4 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand"] +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng"] From a0309a87bdef5c3744bacc3c62303414da6e12cc Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 14 Sep 2022 10:26:57 -0700 Subject: [PATCH 056/339] Add ripemd160 precompile test (#651) --- actors/evm/src/interpreter/precompiles.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 5db4b5fef..7bf55a9c0 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -322,6 +322,18 @@ mod tests { assert_eq!(&res, &expected); } + #[test] + fn ripemd160() { + use super::ripemd160 as hash; + let input = "foo bar baz boxy".as_bytes(); + + let rt = MockRuntime::default(); + + let expected = hex!("4cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); + let res = hash(&rt, input).unwrap(); + assert_eq!(&res, &expected); + } + // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { use super::MockRuntime; From f9a171307bb74999bff67b1867c51b077e866cec Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Sep 2022 15:56:56 -0700 Subject: [PATCH 057/339] fix: remove input_data from EVM constructor (#661) EVM contracts don't take parameters on construction. --- actors/evm/src/lib.rs | 6 +----- actors/evm/tests/basic.rs | 7 ++----- actors/evm/tests/calc.rs | 3 +-- actors/evm/tests/call.rs | 9 +++------ actors/evm/tests/ext_opcodes.rs | 6 +++--- actors/evm/tests/precompile.rs | 3 +-- actors/evm/tests/selfdestruct.rs | 1 - 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 03893e73d..95bfe3f1b 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -68,10 +68,7 @@ impl EvmContractActor { })?; // create a new execution context - let mut exec_state = ExecutionState::new( - Method::Constructor as u64, - Bytes::copy_from_slice(¶ms.input_data), - ); + let mut exec_state = ExecutionState::new(Method::Constructor as u64, Bytes::new()); // identify bytecode valid jump destinations let bytecode = Bytecode::new(¶ms.bytecode) @@ -266,7 +263,6 @@ impl ActorCode for EvmContractActor { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ConstructorParams { pub bytecode: RawBytes, - pub input_data: RawBytes, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 7b4eb8abb..2b48aea0e 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -17,7 +17,6 @@ fn basic_contract_construction_and_invocation() { let params = evm::ConstructorParams { bytecode: hex::decode(include_str!("simplecoin.hex")).unwrap().into(), - input_data: RawBytes::default(), }; // invoke constructor @@ -99,8 +98,7 @@ return let mut rt = MockRuntime::default(); rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: init_code.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: init_code.into() }; let result = rt .call::( evm::Method::Constructor as u64, @@ -140,8 +138,7 @@ sstore"; let mut rt = MockRuntime::default(); rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: init_code.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: init_code.into() }; let result = rt .call::( evm::Method::Constructor as u64, diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index 537da444a..f722812b3 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -75,8 +75,7 @@ fn test_magic_calc() { // invoke constructor rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: contract.into() }; let result = rt .call::( diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index f0a4f5baa..930951f4e 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -63,8 +63,7 @@ fn test_call() { // construct the proxy let contract = call_proxy_contract(); rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: contract.into() }; let result = rt .call::( evm::Method::Constructor as u64, @@ -131,8 +130,7 @@ fn test_methodnum() { let contract = methodnum_contract(); rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: contract.into() }; let result = rt .call::( evm::Method::Constructor as u64, @@ -204,8 +202,7 @@ fn test_callactor() { // construct the proxy let contract = callactor_proxy_contract(); rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: contract.into() }; let result = rt .call::( evm::Method::Constructor as u64, diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index be2f5b00b..ffa1b14a4 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -34,7 +34,7 @@ fn test_extcodesize() { let mut rt = MockRuntime::default(); rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode }; let result = rt .call::( evm::Method::Constructor as u64, @@ -93,7 +93,7 @@ fn test_extcodehash() { let mut rt = MockRuntime::default(); rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode }; let result = rt .call::( evm::Method::Constructor as u64, @@ -150,7 +150,7 @@ fn test_extcodecopy() { let mut rt = MockRuntime::default(); rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode, input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode }; let result = rt .call::( evm::Method::Constructor as u64, diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index f415e65b3..81a03369b 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -45,8 +45,7 @@ fn test_precompile_hash() { // invoke constructor rt.expect_validate_caller_any(); - let params = - evm::ConstructorParams { bytecode: contract.into(), input_data: RawBytes::default() }; + let params = evm::ConstructorParams { bytecode: contract.into() }; let result = rt .call::( diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index ad88f8c3f..3f5d85a51 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -12,7 +12,6 @@ fn test_selfdestruct() { let params = evm::ConstructorParams { bytecode: hex::decode(include_str!("selfdestruct.hex")).unwrap().into(), - input_data: RawBytes::default(), }; // invoke constructor From 5ef23f4b9ae7f83eda4db37ad7aa2283ff181d6c Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 16 Sep 2022 09:14:05 -0700 Subject: [PATCH 058/339] rename evm actor's `Address` to `EthAddress` (#665) rename evm actor's type to --- actors/evm/src/interpreter/address.rs | 22 +++++++++---------- .../evm/src/interpreter/instructions/call.rs | 6 ++--- .../src/interpreter/instructions/context.rs | 8 +++---- .../evm/src/interpreter/instructions/ext.rs | 4 ++-- .../src/interpreter/instructions/lifecycle.rs | 4 ++-- actors/evm/tests/call.rs | 6 ++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index fe2f77ba8..92eb1e6a0 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -6,9 +6,9 @@ use fvm_shared::address::Address as FilecoinAddress; /// /// TODO this type will eventually handle f4 address detection. #[derive(PartialEq, Eq, Clone)] -pub struct Address([u8; 20]); +pub struct EthAddress([u8; 20]); -impl TryFrom for Address { +impl TryFrom for EthAddress { type Error = StatusCode; fn try_from(v: U256) -> Result { @@ -24,24 +24,24 @@ impl TryFrom for Address { } } -impl std::fmt::Debug for Address { +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 Address { +impl EthAddress { /// Expect a Filecoin address type containing an ID address, and return an address in EVM-form. - pub fn from_id_address(addr: &FilecoinAddress) -> Option
{ - addr.id().ok().map(Address::from_id) + pub fn from_id_address(addr: &FilecoinAddress) -> Option { + addr.id().ok().map(EthAddress::from_id) } /// Returns an EVM-form ID address from actor ID. - pub fn from_id(id: u64) -> Address { + pub fn from_id(id: u64) -> EthAddress { let mut bytes = [0u8; 20]; bytes[0] = 0xff; bytes[12..].copy_from_slice(&id.to_be_bytes()); - Address(bytes) + EthAddress(bytes) } /// Interpret the hash as an ID address in EVM-form, and return a Filecoin ID address if that's @@ -67,7 +67,7 @@ impl Address { #[cfg(test)] mod tests { - use crate::interpreter::address::Address; + use crate::interpreter::address::EthAddress; use crate::U256; use fvm_shared::address::Address as FilecoinAddress; @@ -82,7 +82,7 @@ mod tests { #[test] fn $name() { let evm_bytes = $input.concat(); - let evm_addr = Address::try_from(U256::from(evm_bytes.as_slice())).unwrap(); + let evm_addr = EthAddress::try_from(U256::from(evm_bytes.as_slice())).unwrap(); assert_eq!( evm_addr.as_id_address(), $expectation @@ -90,7 +90,7 @@ mod tests { // test inverse conversion, if a valid ID address was supplied if let Some(fil_addr) = $expectation { - assert_eq!(Address::from_id_address(&fil_addr), Some(evm_addr)); + assert_eq!(EthAddress::from_id_address(&fil_addr), Some(evm_addr)); } } )* diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 11bf7adb6..3fa45c787 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,6 +1,6 @@ use { super::memory::{copy_to_memory, get_memory_region}, - crate::interpreter::address::Address, + crate::interpreter::address::EthAddress, crate::interpreter::instructions::memory::MemoryRegion, crate::interpreter::output::StatusCode, crate::interpreter::precompiles, @@ -153,7 +153,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( .map_err(|_| StatusCode::PrecompileFailure)?; Ok(RawBytes::from(result)) } else { - let dst_addr = Address::try_from(dst)? + let dst_addr = EthAddress::try_from(dst)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; @@ -222,7 +222,7 @@ pub fn callactor<'r, BS: Blockstore, RT: Runtime>( .map_err(|_| StatusCode::InvalidMemoryAccess)?; let result = { - let dst_addr = Address::try_from(dst)? + let dst_addr = EthAddress::try_from(dst)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress(format!("not an actor id address: {}", dst)))?; diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 3bde29c60..a08506bd8 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,4 +1,4 @@ -use crate::interpreter::address::Address; +use crate::interpreter::address::EthAddress; use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, @@ -19,7 +19,7 @@ pub fn caller<'r, BS: Blockstore, RT: Runtime>( platform: &'r System<'r, BS, RT>, ) { let id = platform.rt.message().caller().id().unwrap(); - state.stack.push(Address::from_id(id).as_evm_word()) + state.stack.push(EthAddress::from_id(id).as_evm_word()) } #[inline] @@ -28,7 +28,7 @@ pub fn address<'r, BS: Blockstore, RT: Runtime>( platform: &'r System<'r, BS, RT>, ) { let id = platform.rt.message().receiver().id().unwrap(); - state.stack.push(Address::from_id(id).as_evm_word()) + state.stack.push(EthAddress::from_id(id).as_evm_word()) } #[inline] @@ -37,7 +37,7 @@ pub fn origin<'r, BS: Blockstore, RT: Runtime>( platform: &'r System<'r, BS, RT>, ) { let id = platform.rt.message().origin().id().unwrap(); - state.stack.push(Address::from_id(id).as_evm_word()) + state.stack.push(EthAddress::from_id(id).as_evm_word()) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index cb08f5b30..da909a72a 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -1,4 +1,4 @@ -use crate::interpreter::address::Address; +use crate::interpreter::address::EthAddress; use crate::interpreter::instructions::memory::copy_to_memory; use crate::U256; use cid::Cid; @@ -61,7 +61,7 @@ fn get_evm_bytecode_cid>( rt: &RT, addr: U256, ) -> Result { - let addr = Address::try_from(addr)? + let addr = EthAddress::try_from(addr)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress("no support for non-ID addresses yet".to_string()))?; diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 012488046..e61dc9cc2 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,4 +1,4 @@ -use crate::interpreter::address::Address; +use crate::interpreter::address::EthAddress; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, @@ -19,7 +19,7 @@ pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, _system: &'r mut System<'r, BS, RT>, ) -> Result<(), StatusCode> { - let beneficiary_addr = Address::try_from(state.stack.pop())?; + let beneficiary_addr = EthAddress::try_from(state.stack.pop())?; let id_addr = beneficiary_addr.as_id_address().expect("no support for non-ID addresses yet"); state.selfdestroyed = Some(id_addr); Ok(()) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 930951f4e..c07e77959 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -1,6 +1,6 @@ mod asm; -use evm::interpreter::address::Address as EVMAddress; +use evm::interpreter::address::EthAddress; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; @@ -77,7 +77,7 @@ fn test_call() { let target = FILAddress::new_id(0x100); rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); - let evm_target = EVMAddress::from_id_address(&target).unwrap(); + let evm_target = EthAddress::from_id_address(&target).unwrap(); let evm_target_word = evm_target.as_evm_word(); // dest + method 0 with no data @@ -216,7 +216,7 @@ fn test_callactor() { let target = FILAddress::new_id(0x100); rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); - let evm_target = EVMAddress::from_id_address(&target).unwrap(); + let evm_target = EthAddress::from_id_address(&target).unwrap(); let evm_target_word = evm_target.as_evm_word(); // dest + method 0x42 with no data From 07bed12a9d47d975add1ec5c48a82e9768208d69 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Sep 2022 18:24:46 -0700 Subject: [PATCH 059/339] feat: use consts for addrs (#660) * feat: use consts for addrs And expose actor IDs so we don't need to call `.id().unwrap()` to retrieve them. Co-authored-by: mriise --- Cargo.lock | 7 ++ actors/account/src/lib.rs | 2 +- actors/account/tests/account_actor_test.rs | 2 +- actors/cron/src/lib.rs | 4 +- actors/cron/tests/cron_actor_test.rs | 6 +- actors/init/tests/init_actor_test.rs | 8 +- actors/market/src/lib.rs | 4 +- .../market/tests/cron_tick_deal_slashing.rs | 2 +- .../market/tests/cron_tick_timedout_deals.rs | 10 +-- actors/market/tests/harness.rs | 18 ++-- actors/market/tests/market_actor_test.rs | 12 +-- .../tests/random_cron_epoch_during_publish.rs | 2 +- actors/miner/src/lib.rs | 10 +-- actors/miner/tests/apply_rewards.rs | 14 +-- .../tests/miner_actor_test_construction.rs | 16 ++-- actors/miner/tests/util.rs | 88 +++++++++---------- actors/multisig/src/lib.rs | 2 +- actors/multisig/tests/multisig_actor_test.rs | 38 ++++---- actors/multisig/tests/util.rs | 4 +- actors/paych/tests/paych_actor_test.rs | 2 +- actors/power/src/lib.rs | 4 +- actors/power/tests/harness/mod.rs | 16 ++-- actors/power/tests/power_actor_tests.rs | 56 ++++++------ actors/reward/src/lib.rs | 6 +- actors/reward/tests/reward_actor_test.rs | 28 +++--- actors/system/src/lib.rs | 10 +-- actors/verifreg/src/lib.rs | 6 +- actors/verifreg/tests/harness/mod.rs | 21 ++--- actors/verifreg/tests/verifreg_actor_test.rs | 14 +-- runtime/Cargo.toml | 1 + runtime/src/builtin/singletons.rs | 33 ++++--- runtime/src/lib.rs | 7 -- runtime/src/runtime/fvm.rs | 2 +- runtime/src/test_utils.rs | 2 +- test_vm/src/lib.rs | 52 +++++------ test_vm/src/util.rs | 42 ++++----- test_vm/tests/batch_onboarding.rs | 4 +- test_vm/tests/commit_post_test.rs | 64 +++++++------- test_vm/tests/extend_sectors_test.rs | 14 +-- test_vm/tests/market_miner_withdrawal_test.rs | 6 +- test_vm/tests/multisig_test.rs | 14 +-- test_vm/tests/power_scenario_tests.rs | 32 +++---- test_vm/tests/publish_deals_test.rs | 20 ++--- test_vm/tests/replica_update_test.rs | 12 +-- test_vm/tests/terminate_test.rs | 46 +++++----- test_vm/tests/verifreg_remove_datacap_test.rs | 18 ++-- 46 files changed, 391 insertions(+), 390 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3539f9b3a..7cb5bacf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -909,6 +909,7 @@ dependencies = [ "multihash", "num-derive", "num-traits", + "paste", "rand", "regex", "serde", @@ -1644,6 +1645,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "pest" version = "2.3.0" diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index fc7643cf6..74e8f44df 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -40,7 +40,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; match address.protocol() { Protocol::Secp256k1 | Protocol::BLS => {} protocol => { diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index cef0a073b..5d4d2ac44 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -27,7 +27,7 @@ macro_rules! account_tests { caller_type: SYSTEM_ACTOR_CODE_ID.clone(), ..Default::default() }; - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); if exit_code.is_success() { rt.call::(1, &RawBytes::serialize(addr).unwrap()).unwrap(); diff --git a/actors/cron/src/lib.rs b/actors/cron/src/lib.rs index 891a53d0f..217f84b00 100644 --- a/actors/cron/src/lib.rs +++ b/actors/cron/src/lib.rs @@ -47,7 +47,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.create(&State { entries: params.entries })?; Ok(()) } @@ -59,7 +59,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; let st: State = rt.state()?; for entry in st.entries { diff --git a/actors/cron/tests/cron_actor_test.rs b/actors/cron/tests/cron_actor_test.rs index 4b6201a8f..bc92de96f 100644 --- a/actors/cron/tests/cron_actor_test.rs +++ b/actors/cron/tests/cron_actor_test.rs @@ -19,7 +19,7 @@ fn check_state(rt: &MockRuntime) { fn construct_runtime() -> MockRuntime { MockRuntime { receiver: Address::new_id(100), - caller: *SYSTEM_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -114,14 +114,14 @@ fn epoch_tick_with_entries() { } fn construct_and_verify(rt: &mut MockRuntime, params: &ConstructorParams) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt.call::(1, &RawBytes::serialize(¶ms).unwrap()).unwrap(); assert_eq!(RawBytes::default(), ret); rt.verify(); } fn epoch_tick_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt.call::(2, &RawBytes::default()).unwrap(); assert_eq!(RawBytes::default(), ret); rt.verify(); diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 4fc2cba2b..74538d430 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -27,7 +27,7 @@ fn check_state(rt: &MockRuntime) { fn construct_runtime() -> MockRuntime { MockRuntime { receiver: Address::new_id(1000), - caller: *SYSTEM_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -163,7 +163,7 @@ fn create_storage_miner() { construct_and_verify(&mut rt); // only the storage power actor can create a miner - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); let unique_address = Address::new_actor(b"miner"); rt.new_actor_addr = Some(unique_address); @@ -248,7 +248,7 @@ fn sending_constructor_failure() { construct_and_verify(&mut rt); // Only the storage power actor can create a miner - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); // Assign new address for the storage actor miner let unique_address = Address::new_actor(b"miner"); @@ -288,7 +288,7 @@ fn sending_constructor_failure() { } fn construct_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let params = ConstructorParams { network_name: "mock".to_string() }; let ret = rt.call::(METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap()).unwrap(); diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 1053974ab..4c6ba7983 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -97,7 +97,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; let st = State::new(rt.store()).map_err(|e| { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create market state") @@ -762,7 +762,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*CRON_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&CRON_ACTOR_ADDR))?; let mut amount_slashed = TokenAmount::zero(); let curr_epoch = rt.curr_epoch(); diff --git a/actors/market/tests/cron_tick_deal_slashing.rs b/actors/market/tests/cron_tick_deal_slashing.rs index 83bbaae42..d7ecc3da2 100644 --- a/actors/market/tests/cron_tick_deal_slashing.rs +++ b/actors/market/tests/cron_tick_deal_slashing.rs @@ -238,7 +238,7 @@ fn slash_multiple_deals_in_the_same_epoch() { + &deal_proposal2.provider_collateral + &deal_proposal3.provider_collateral; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), total_slashed, diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index 1da6ef4ef..94c4c74bd 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -41,7 +41,7 @@ fn timed_out_deal_is_slashed_and_deleted() { // do a cron tick for it -> should time out and get slashed rt.set_epoch(process_epoch(START_EPOCH, deal_id)); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), deal_proposal.provider_collateral.clone(), @@ -105,7 +105,7 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l // do a cron tick for it -> should time out and get slashed rt.set_epoch(process_epoch(START_EPOCH, deal_id)); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), deal_proposal.provider_collateral.clone(), @@ -178,7 +178,7 @@ fn timed_out_and_verified_deals_are_slashed_deleted_and_sent_to_the_registry_act }; rt.expect_send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::RESTORE_BYTES_METHOD as u64, RawBytes::serialize(param1).unwrap(), TokenAmount::zero(), @@ -186,7 +186,7 @@ fn timed_out_and_verified_deals_are_slashed_deleted_and_sent_to_the_registry_act ExitCode::OK, ); rt.expect_send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::RESTORE_BYTES_METHOD as u64, RawBytes::serialize(param2).unwrap(), TokenAmount::zero(), @@ -196,7 +196,7 @@ fn timed_out_and_verified_deals_are_slashed_deleted_and_sent_to_the_registry_act let expected_burn = 3 * &deal1.provider_collateral; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_burn, diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 547db7120..c63c0cba7 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -79,8 +79,8 @@ pub fn setup() -> MockRuntime { ]); let mut rt = MockRuntime { - receiver: *STORAGE_MARKET_ACTOR_ADDR, - caller: *SYSTEM_ACTOR_ADDR, + receiver: STORAGE_MARKET_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *INIT_ACTOR_CODE_ID, actor_code_cids, balance: RefCell::new(TokenAmount::from_whole(10)), @@ -107,7 +107,7 @@ pub fn check_state_with_expected(rt: &MockRuntime, expected_patterns: &[Regex]) acc.assert_expected(expected_patterns); } pub fn construct_and_verify(rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( RawBytes::default(), rt.call::(METHOD_CONSTRUCTOR, &RawBytes::default()).unwrap() @@ -362,7 +362,7 @@ pub fn cron_tick_and_assert_balances( let mut payment_end = d.end_epoch; if s.slash_epoch != EPOCH_UNDEFINED { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), d.provider_collateral.clone(), @@ -477,7 +477,7 @@ pub fn publish_deals( .unwrap(); rt.expect_send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::USE_BYTES_METHOD as u64, param, TokenAmount::zero(), @@ -568,8 +568,8 @@ pub fn cron_tick(rt: &mut MockRuntime) { } pub fn cron_tick_raw(rt: &mut MockRuntime) -> Result { - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::CronTick as u64, &RawBytes::default()) } @@ -592,7 +592,7 @@ pub fn expect_query_network_info(rt: &mut MockRuntime) { this_epoch_reward_smoothed: epoch_reward_smooth, }; rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), TokenAmount::zero(), @@ -600,7 +600,7 @@ pub fn expect_query_network_info(rt: &mut MockRuntime) { ExitCode::OK, ); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::CurrentTotalPower as u64, RawBytes::default(), TokenAmount::zero(), diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 810b0d7cf..1ff23fb1a 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -51,12 +51,12 @@ fn test_remove_all_error() { fn simple_construction() { let mut rt = MockRuntime { receiver: Address::new_id(100), - caller: *SYSTEM_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *INIT_ACTOR_CODE_ID, ..Default::default() }; - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( RawBytes::default(), @@ -793,7 +793,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t .unwrap(); rt.expect_send( - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, ext::verifreg::USE_BYTES_METHOD as u64, param, TokenAmount::zero(), @@ -1239,7 +1239,7 @@ fn slash_a_deal_and_make_payment_for_another_deal_in_the_same_epoch() { // cron tick will slash deal1 and make payment for deal2 rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), d1.provider_collateral.clone(), @@ -1472,7 +1472,7 @@ fn locked_fund_tracking_states() { let curr = process_epoch(start_epoch, deal_id3); rt.set_epoch(curr); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), d3.provider_collateral.clone(), @@ -1513,7 +1513,7 @@ fn locked_fund_tracking_states() { clc = TokenAmount::zero(); plc = TokenAmount::zero(); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), d1.provider_collateral, diff --git a/actors/market/tests/random_cron_epoch_during_publish.rs b/actors/market/tests/random_cron_epoch_during_publish.rs index 831e9beb2..d6f01ad1f 100644 --- a/actors/market/tests/random_cron_epoch_during_publish.rs +++ b/actors/market/tests/random_cron_epoch_during_publish.rs @@ -177,7 +177,7 @@ fn cron_processing_of_deal_after_missed_activation_should_fail_and_slash() { // FIXME: cron_tick calls 'VERIFIED_REGISTRY_ACTOR_ADDR' with the 'USE_BYTES_METHOD' method. rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), deal_proposal.provider_collateral.clone(), diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index bc508da2f..fcc0a3535 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -130,7 +130,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(&[*INIT_ACTOR_ADDR])?; + rt.validate_immediate_caller_is(std::iter::once(&INIT_ACTOR_ADDR))?; check_control_addresses(rt.policy(), ¶ms.control_addresses)?; check_peer_info(rt.policy(), ¶ms.peer_id, ¶ms.multi_addresses)?; @@ -1880,7 +1880,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(iter::once(&*STORAGE_POWER_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(iter::once(&STORAGE_POWER_ACTOR_ADDR))?; // This should be enforced by the power actor. We log here just in case // something goes wrong. @@ -2902,7 +2902,7 @@ impl Actor { let (pledge_delta_total, to_burn) = rt.transaction(|st: &mut State, rt| { let mut pledge_delta_total = TokenAmount::zero(); - rt.validate_immediate_caller_is(std::iter::once(&*REWARD_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&REWARD_ACTOR_ADDR))?; let (reward_to_lock, locked_reward_vesting_spec) = locked_reward_from_reward(params.reward); @@ -3196,7 +3196,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_POWER_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&STORAGE_POWER_ACTOR_ADDR))?; let payload: CronEventPayload = from_slice(¶ms.event_payload).map_err(|e| { actor_error!( @@ -3767,7 +3767,7 @@ where } let ret: ext::market::ComputeDataCommitmentReturn = rt .send( - &*STORAGE_MARKET_ACTOR_ADDR, + &STORAGE_MARKET_ACTOR_ADDR, ext::market::COMPUTE_DATA_COMMITMENT_METHOD, RawBytes::serialize(ext::market::ComputeDataCommitmentParamsRef { inputs: data_commitment_inputs, diff --git a/actors/miner/tests/apply_rewards.rs b/actors/miner/tests/apply_rewards.rs index 3d1418cda..1bec82b51 100644 --- a/actors/miner/tests/apply_rewards.rs +++ b/actors/miner/tests/apply_rewards.rs @@ -123,8 +123,8 @@ fn penalty_is_partially_burnt_and_stored_as_fee_debt() { let new_balance = &reward + &amt; rt.set_balance(new_balance); - rt.set_caller(*REWARD_ACTOR_CODE_ID, *REWARD_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*REWARD_ACTOR_ADDR]); + rt.set_caller(*REWARD_ACTOR_CODE_ID, REWARD_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![REWARD_ACTOR_ADDR]); // pledge change is new reward - reward taken for fee debt // zero here since all reward goes to debt @@ -133,7 +133,7 @@ fn penalty_is_partially_burnt_and_stored_as_fee_debt() { // burn initial balance + reward = 2*amt let expect_burnt = 2 * &amt; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expect_burnt, @@ -186,11 +186,11 @@ fn rewards_pay_back_fee_debt() { let remaining_locked = locked_reward - &st.fee_debt; // note that this would be clamped at 0 if difference above is < 0 assert!(remaining_locked.is_positive()); let pledge_delta = remaining_locked.clone(); - rt.set_caller(*REWARD_ACTOR_CODE_ID, *REWARD_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*REWARD_ACTOR_ADDR]); + rt.set_caller(*REWARD_ACTOR_CODE_ID, REWARD_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![REWARD_ACTOR_ADDR]); // expect pledge update rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), @@ -200,7 +200,7 @@ fn rewards_pay_back_fee_debt() { let expect_burnt = st.fee_debt; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expect_burnt.clone(), diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 96926bed3..e34eb3c74 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -47,7 +47,7 @@ fn prepare_env() -> TestEnv { env.rt.actor_code_cids.insert(env.control_addrs[0], *ACCOUNT_ACTOR_CODE_ID); env.rt.actor_code_cids.insert(env.control_addrs[1], *ACCOUNT_ACTOR_CODE_ID); env.rt.hash_func = Box::new(hash); - env.rt.caller = *INIT_ACTOR_ADDR; + env.rt.caller = INIT_ACTOR_ADDR; env.rt.caller_type = *INIT_ACTOR_CODE_ID; env } @@ -68,7 +68,7 @@ fn simple_construction() { let mut env = prepare_env(); let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, AccountMethod::PubkeyAddress as u64, @@ -141,7 +141,7 @@ fn control_addresses_are_resolved_during_construction() { env.rt.id_addresses.insert(control2, control2id); let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, AccountMethod::PubkeyAddress as u64, @@ -175,7 +175,7 @@ fn fails_if_control_address_is_not_an_account_actor() { env.rt.actor_code_cids.insert(control1, *PAYCH_ACTOR_CODE_ID); let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, AccountMethod::PubkeyAddress as u64, @@ -199,7 +199,7 @@ fn test_construct_with_invalid_peer_id() { env.peer_id = vec![0; env.rt.policy.max_peer_id_length + 1]; let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env .rt @@ -218,7 +218,7 @@ fn fails_if_control_addresses_exceeds_maximum_length() { } let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env .rt @@ -237,7 +237,7 @@ fn test_construct_with_large_multiaddr() { } let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env .rt @@ -255,7 +255,7 @@ fn test_construct_with_empty_multiaddr() { env.multiaddrs.push(BytesDe(vec![1])); let params = constructor_params(&env); - env.rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env .rt diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index baaad55a6..262462850 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -212,8 +212,8 @@ impl ActorHarness { rt.actor_code_cids.insert(*a, *ACCOUNT_ACTOR_CODE_ID); } - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); rt.expect_send( self.worker, AccountMethod::PubkeyAddress as u64, @@ -499,7 +499,7 @@ impl ActorHarness { let vdparams = VerifyDealsForActivationParams { sectors: sector_deals }; let vdreturn = VerifyDealsForActivationReturn { sectors: sector_weights }; rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, MarketMethod::VerifyDealsForActivation as u64, RawBytes::serialize(vdparams).unwrap(), TokenAmount::zero(), @@ -515,7 +515,7 @@ impl ActorHarness { aggregate_pre_commit_network_fee(params.sectors.len() as i64, base_fee); let expected_burn = expected_network_fee + state.fee_debt; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_burn, @@ -532,7 +532,7 @@ impl ActorHarness { ); let cron_params = make_deadline_cron_event_params(dlinfo.last()); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(cron_params).unwrap(), TokenAmount::zero(), @@ -590,7 +590,7 @@ impl ActorHarness { }; rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, MarketMethod::VerifyDealsForActivation as u64, RawBytes::serialize(vdparams).unwrap(), TokenAmount::zero(), @@ -603,7 +603,7 @@ impl ActorHarness { let state = self.get_state(rt); if state.fee_debt > TokenAmount::zero() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), state.fee_debt.clone(), @@ -620,7 +620,7 @@ impl ActorHarness { ); let cron_params = make_deadline_cron_event_params(dlinfo.last()); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(cron_params).unwrap(), TokenAmount::zero(), @@ -677,7 +677,7 @@ impl ActorHarness { this_epoch_reward_smoothed: self.epoch_reward_smooth.clone(), }; rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), TokenAmount::zero(), @@ -685,7 +685,7 @@ impl ActorHarness { ExitCode::OK, ); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::CurrentTotalPower as u64, RawBytes::default(), TokenAmount::zero(), @@ -725,7 +725,7 @@ impl ActorHarness { let cdc_params = ComputeDataCommitmentParams { inputs: vec![input] }; let cdc_ret = ComputeDataCommitmentReturn { commds: vec![commd] }; rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ComputeDataCommitment as u64, RawBytes::serialize(cdc_params).unwrap(), TokenAmount::zero(), @@ -759,7 +759,7 @@ impl ActorHarness { unsealed_cid: commd, }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::SubmitPoRepForBulkVerify as u64, RawBytes::serialize(seal).unwrap(), TokenAmount::zero(), @@ -799,7 +799,7 @@ impl ActorHarness { let cdc_params = ComputeDataCommitmentParams { inputs: cdc_inputs }; let cdc_ret = ComputeDataCommitmentReturn { commds: comm_ds.clone() }; rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ComputeDataCommitment as u64, RawBytes::serialize(cdc_params)?, TokenAmount::zero(), @@ -856,7 +856,7 @@ impl ActorHarness { let expected_fee = aggregate_prove_commit_network_fee(precommits.len() as i64, base_fee); assert!(expected_fee.is_positive()); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_fee, @@ -889,8 +889,8 @@ impl ActorHarness { all_sector_numbers.push(pc.info.sector_number); } - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*STORAGE_POWER_ACTOR_ADDR]); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_POWER_ACTOR_ADDR]); let params = ConfirmSectorProofsParams { sectors: all_sector_numbers, @@ -931,7 +931,7 @@ impl ActorHarness { } rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, MarketMethod::ActivateDeals as u64, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), @@ -985,7 +985,7 @@ impl ActorHarness { if expected_pledge != TokenAmount::zero() { rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&expected_pledge).unwrap(), TokenAmount::zero(), @@ -1044,7 +1044,7 @@ impl ActorHarness { pub fn on_deadline_cron(&self, rt: &mut MockRuntime, cfg: CronConfig) { let state = self.get_state(rt); - rt.expect_validate_caller_addr(vec![*STORAGE_POWER_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![STORAGE_POWER_ACTOR_ADDR]); // preamble let mut power_delta = PowerPair::zero(); @@ -1057,7 +1057,7 @@ impl ActorHarness { quality_adjusted_delta: power_delta.qa, }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), @@ -1075,7 +1075,7 @@ impl ActorHarness { if penalty_total != TokenAmount::zero() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), penalty_total.clone(), @@ -1099,7 +1099,7 @@ impl ActorHarness { if pledge_delta != TokenAmount::zero() { rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), @@ -1112,7 +1112,7 @@ impl ActorHarness { if !cfg.no_enrollment { let params = make_deadline_cron_event_params(cfg.expected_enrollment); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::EnrollCronEvent as u64, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), @@ -1125,7 +1125,7 @@ impl ActorHarness { self.epoch_reward_smooth.clone(), self.epoch_qa_power_smooth.clone(), ); - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); rt.call::(Method::OnDeferredCronEvent as u64, &RawBytes::serialize(params).unwrap()) .unwrap(); rt.verify(); @@ -1230,7 +1230,7 @@ impl ActorHarness { quality_adjusted_delta: power_delta.qa, }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), TokenAmount::zero(), @@ -1338,7 +1338,7 @@ impl ActorHarness { quality_adjusted_delta: expected_power_delta.qa, }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), TokenAmount::zero(), @@ -1362,7 +1362,7 @@ impl ActorHarness { if dispute_result.expected_penalty.is_some() { let expected_penalty = dispute_result.expected_penalty.unwrap(); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_penalty, @@ -1374,7 +1374,7 @@ impl ActorHarness { if dispute_result.expected_pledge_delta.is_some() { let expected_pledge_delta = dispute_result.expected_pledge_delta.unwrap(); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&expected_pledge_delta).unwrap(), TokenAmount::zero(), @@ -1439,11 +1439,11 @@ impl ActorHarness { let (lock_amt, _) = locked_reward_from_reward(amt.clone()); let pledge_delta = lock_amt - &penalty; - rt.set_caller(*REWARD_ACTOR_CODE_ID, *REWARD_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*REWARD_ACTOR_ADDR]); + rt.set_caller(*REWARD_ACTOR_CODE_ID, REWARD_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![REWARD_ACTOR_ADDR]); // expect pledge update rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), @@ -1453,7 +1453,7 @@ impl ActorHarness { if penalty > TokenAmount::zero() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), penalty.clone(), @@ -1602,7 +1602,7 @@ impl ActorHarness { quality_adjusted_delta: expected_qa_delta.clone(), }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdateClaimedPower as u64, RawBytes::serialize(claim).unwrap(), TokenAmount::zero(), @@ -1633,7 +1633,7 @@ impl ActorHarness { if expected_debt_repaid > TokenAmount::zero() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_debt_repaid, @@ -1746,7 +1746,7 @@ impl ActorHarness { this_epoch_reward_smoothed: self.epoch_reward_smooth.clone(), }; rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), TokenAmount::zero(), @@ -1768,7 +1768,7 @@ impl ActorHarness { // pay fault fee let to_burn = &penalty_total - &reward_total; rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), to_burn, @@ -1845,7 +1845,7 @@ impl ActorHarness { let mut pledge_delta = TokenAmount::zero(); if expected_fee.is_positive() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_fee.clone(), @@ -1862,7 +1862,7 @@ impl ActorHarness { if !pledge_delta.is_zero() { rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, UPDATE_PLEDGE_TOTAL_METHOD, RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), @@ -1879,7 +1879,7 @@ impl ActorHarness { deal_ids: deal_ids[0..size].to_owned(), }; rt.expect_send( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, ON_MINER_SECTORS_TERMINATE_METHOD, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), @@ -1894,7 +1894,7 @@ impl ActorHarness { quality_adjusted_delta: -sector_power.qa.clone(), }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, UPDATE_CLAIMED_POWER_METHOD, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), @@ -1958,7 +1958,7 @@ impl ActorHarness { if expected_repaid_from_vest > &TokenAmount::zero() { let pledge_delta = expected_repaid_from_vest.neg(); rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, PowerMethod::UpdatePledgeTotal as u64, RawBytes::serialize(&pledge_delta).unwrap(), TokenAmount::zero(), @@ -1970,7 +1970,7 @@ impl ActorHarness { let total_repaid = expected_repaid_from_vest + expected_repaid_from_balance; if total_repaid > TokenAmount::zero() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), total_repaid.clone(), @@ -2003,7 +2003,7 @@ impl ActorHarness { ); if expected_debt_repaid.is_positive() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_debt_repaid.clone(), @@ -2126,7 +2126,7 @@ impl ActorHarness { quality_adjusted_delta: qa_delta, }; rt.expect_send( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, UPDATE_CLAIMED_POWER_METHOD, RawBytes::serialize(params).unwrap(), TokenAmount::zero(), diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index b9749ebe0..869ff71ee 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -55,7 +55,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*INIT_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&INIT_ACTOR_ADDR))?; if params.signers.is_empty() { return Err(actor_error!(illegal_argument; "Must have at least one signer")); diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 9a6d00c57..f14750f68 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -22,7 +22,7 @@ mod util; fn construct_runtime(receiver: Address) -> MockRuntime { MockRuntime { receiver, - caller: *SYSTEM_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -55,8 +55,8 @@ mod constructor_tests { }; rt.set_received(TokenAmount::from_atto(100u8)); - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); let ret = rt.call::( Method::Constructor as u64, &RawBytes::serialize(¶ms).unwrap(), @@ -91,8 +91,8 @@ mod constructor_tests { start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); let ret = rt .call::( Method::Constructor as u64, @@ -114,8 +114,8 @@ mod constructor_tests { unlock_duration: 100, start_epoch: 1234, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); assert_eq!( RawBytes::default(), rt.call::( @@ -144,8 +144,8 @@ mod constructor_tests { unlock_duration: 1, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -172,8 +172,8 @@ mod constructor_tests { unlock_duration: 1, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -193,8 +193,8 @@ mod constructor_tests { unlock_duration: 0, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -216,7 +216,7 @@ mod constructor_tests { unlock_duration: 1, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); rt.expect_send( anne_non_id, METHOD_SEND, @@ -225,7 +225,7 @@ mod constructor_tests { RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_STATE, rt.call::( @@ -245,8 +245,8 @@ mod constructor_tests { unlock_duration: 0, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -268,8 +268,8 @@ mod constructor_tests { unlock_duration: 0, start_epoch: 0, }; - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( diff --git a/actors/multisig/tests/util.rs b/actors/multisig/tests/util.rs index b5e6979cf..1ff3bba74 100644 --- a/actors/multisig/tests/util.rs +++ b/actors/multisig/tests/util.rs @@ -38,8 +38,8 @@ impl ActorHarness { unlock_duration, start_epoch, }; - rt.set_caller(*INIT_ACTOR_CODE_ID, *INIT_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = rt .call::(Method::Constructor as u64, &RawBytes::serialize(params).unwrap()) .unwrap(); diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index 769c6ec8b..f306915c9 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -1135,7 +1135,7 @@ fn require_create_channel_with_lanes(num_lanes: u64) -> (MockRuntime, SignedVouc let mut rt = MockRuntime { receiver: paych_addr, - caller: *INIT_ACTOR_ADDR, + caller: INIT_ACTOR_ADDR, caller_type: *INIT_ACTOR_CODE_ID, actor_code_cids, value_received: received, diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index b421e67b3..4b6c4b5b2 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -75,7 +75,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; let st = State::new(rt.store()).map_err(|e| { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create power actor state") @@ -249,7 +249,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*CRON_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&CRON_ACTOR_ADDR))?; let rewret: ThisEpochRewardReturn = rt .send( diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 24c1b7024..5a6ad643d 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -64,8 +64,8 @@ lazy_static! { pub fn new_runtime() -> MockRuntime { MockRuntime { - receiver: *STORAGE_POWER_ACTOR_ADDR, - caller: *SYSTEM_ACTOR_ADDR, + receiver: STORAGE_POWER_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -100,7 +100,7 @@ pub struct Harness { impl Harness { pub fn construct(&self, rt: &mut MockRuntime) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); rt.call::(Method::Constructor as MethodNum, &RawBytes::default()).unwrap(); rt.verify() } @@ -156,7 +156,7 @@ impl Harness { }; let create_miner_ret = CreateMinerReturn { id_address: *miner, robust_address: *robust }; rt.expect_send( - *INIT_ACTOR_ADDR, + INIT_ACTOR_ADDR, ext::init::EXEC_METHOD, RawBytes::serialize(expected_init_params).unwrap(), value.clone(), @@ -378,7 +378,7 @@ impl Harness { }; rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, ThisEpochReward as u64, RawBytes::default(), TokenAmount::zero(), @@ -422,17 +422,17 @@ impl Harness { // expect power sends to reward actor rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(expected_raw_power)).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::new(0), ); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); rt.set_epoch(current_epoch); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 9b754a064..c090725bc 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -137,7 +137,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { }; rt.expect_send( - *INIT_ACTOR_ADDR, + INIT_ACTOR_ADDR, EXEC_METHOD, RawBytes::serialize(message_params).unwrap(), TokenAmount::from_atto(10), @@ -633,18 +633,18 @@ mod cron_tests { let expected_power = BigInt::zero(); rt.set_epoch(1); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); h.expect_query_network_info(&mut rt); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, RewardMethod::UpdateNetworkKPI as u64, RawBytes::serialize(BigIntSer(&expected_power)).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); @@ -715,7 +715,7 @@ mod cron_tests { let expected_raw_byte_power = BigInt::zero(); rt.set_epoch(4); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); h.expect_query_network_info(&mut rt); let state: State = rt.get_state(); @@ -748,14 +748,14 @@ mod cron_tests { ); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); @@ -773,17 +773,17 @@ mod cron_tests { // run cron once to put it in a clean state at epoch 4 let expected_raw_byte_power = BigInt::zero(); rt.set_epoch(4); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); h.expect_query_network_info(&mut rt); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); @@ -796,7 +796,7 @@ mod cron_tests { // run cron again in the future rt.set_epoch(6); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); h.expect_query_network_info(&mut rt); let state: State = rt.get_state(); @@ -815,14 +815,14 @@ mod cron_tests { ExitCode::OK, ); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&expected_raw_byte_power)).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); @@ -867,7 +867,7 @@ mod cron_tests { h.delete_claim(&mut rt, &miner1); rt.set_epoch(2); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); // process batch verifies first rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); @@ -892,14 +892,14 @@ mod cron_tests { // reward actor is still invoked rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); rt.verify(); @@ -932,7 +932,7 @@ mod cron_tests { h.expect_miners_above_min_power(&mut rt, 1); rt.set_epoch(2); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); // process batch verifies first rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); @@ -967,9 +967,9 @@ mod cron_tests { ExitCode::OK, ); // reward actor is still invoked - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), TokenAmount::zero(), @@ -991,19 +991,19 @@ mod cron_tests { // next epoch, only the reward actor is invoked rt.set_epoch(3); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); h.expect_query_network_info(&mut rt); rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.expect_batch_verify_seals(Vec::new(), Ok(Vec::new())); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); @@ -1242,7 +1242,7 @@ mod cron_batch_proof_verifies_tests { // expect power sends to reward actor rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), TokenAmount::zero(), @@ -1250,10 +1250,10 @@ mod cron_batch_proof_verifies_tests { ExitCode::OK, ); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); rt.set_epoch(0); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); @@ -1274,11 +1274,11 @@ mod cron_batch_proof_verifies_tests { h.expect_query_network_info(&mut rt); rt.expect_batch_verify_seals(infos, Err(anyhow::Error::msg("fail"))); - rt.expect_validate_caller_addr(vec![*CRON_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); // expect power sends to reward actor rt.expect_send( - *REWARD_ACTOR_ADDR, + REWARD_ACTOR_ADDR, UPDATE_NETWORK_KPI, RawBytes::serialize(BigIntSer(&BigInt::zero())).unwrap(), TokenAmount::zero(), @@ -1286,7 +1286,7 @@ mod cron_batch_proof_verifies_tests { ExitCode::OK, ); rt.set_epoch(0); - rt.set_caller(*CRON_ACTOR_CODE_ID, *CRON_ACTOR_ADDR); + rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::OnEpochTickEnd as u64, &RawBytes::default()).unwrap(); rt.verify(); diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index 21db9bdf6..003dfed16 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -61,7 +61,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; if let Some(power) = curr_realized_power { rt.create(&State::new(power))?; @@ -89,7 +89,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; let prior_balance = rt.current_balance(); if params.penalty.is_negative() { return Err(actor_error!(illegal_argument, "negative penalty {}", params.penalty)); @@ -209,7 +209,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_POWER_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&STORAGE_POWER_ACTOR_ADDR))?; let curr_realized_power = curr_realized_power .ok_or_else(|| actor_error!(illegal_argument, "argument cannot be None"))?; diff --git a/actors/reward/tests/reward_actor_test.rs b/actors/reward/tests/reward_actor_test.rs index f8ed63937..33dfe720b 100644 --- a/actors/reward/tests/reward_actor_test.rs +++ b/actors/reward/tests/reward_actor_test.rs @@ -89,7 +89,7 @@ mod test_award_block_reward { let mut rt = construct_and_verify(&StoragePower::default()); rt.set_balance(TokenAmount::from_atto(9)); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( ExitCode::USR_ILLEGAL_STATE, award_block_reward( @@ -109,7 +109,7 @@ mod test_award_block_reward { fn rejects_negative_penalty_or_reward() { let mut rt = construct_and_verify(&StoragePower::default()); rt.set_balance(TokenAmount::from_whole(1)); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let reward_penalty_pairs = [(-1, 0), (0, -1)]; @@ -136,7 +136,7 @@ mod test_award_block_reward { let mut rt = construct_and_verify(&StoragePower::default()); rt.set_balance(TokenAmount::from_whole(1)); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert!(award_block_reward( &mut rt, *WINNER, @@ -153,7 +153,7 @@ mod test_award_block_reward { fn pays_reward_and_tracks_penalty() { let mut rt = construct_and_verify(&StoragePower::default()); rt.set_balance(TokenAmount::from_whole(1_000_000_000)); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let penalty: TokenAmount = TokenAmount::from_atto(100); let gas_reward: TokenAmount = TokenAmount::from_atto(200); let expected_reward: TokenAmount = @@ -191,7 +191,7 @@ mod test_award_block_reward { let small_reward = TokenAmount::from_atto(300); let penalty = TokenAmount::from_atto(100); rt.set_balance(small_reward.clone()); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let miner_penalty = PENALTY_MULTIPLIER * &penalty; let params = RawBytes::serialize(&ext::miner::ApplyRewardParams { @@ -263,7 +263,7 @@ mod test_award_block_reward { // enough balance to pay 3 full rewards and one partial rt.set_balance(TokenAmount::from_atto(3500)); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let expected_reward = TokenAmount::from_atto(1000); let miner_penalty = TokenAmount::zero(); let params = RawBytes::serialize(&ext::miner::ApplyRewardParams { @@ -280,7 +280,7 @@ mod test_award_block_reward { ExitCode::USR_FORBIDDEN, ); rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_reward, @@ -335,12 +335,12 @@ fn test_successive_kpi_updates() { fn construct_and_verify(curr_power: &StoragePower) -> MockRuntime { let mut rt = MockRuntime { - receiver: *REWARD_ACTOR_ADDR, - caller: *SYSTEM_ACTOR_ADDR, + receiver: REWARD_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() }; - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( METHOD_CONSTRUCTOR, @@ -361,7 +361,7 @@ fn award_block_reward( win_count: i64, expected_payment: TokenAmount, ) -> Result { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let miner_penalty = &penalty * PENALTY_MULTIPLIER; rt.expect_send( miner, @@ -378,7 +378,7 @@ fn award_block_reward( if penalty.is_positive() { rt.expect_send( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), expected_payment, @@ -407,8 +407,8 @@ fn this_epoch_reward(rt: &mut MockRuntime) -> ThisEpochRewardReturn { } fn update_network_kpi(rt: &mut MockRuntime, curr_raw_power: &StoragePower) { - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); - rt.expect_validate_caller_addr(vec![*STORAGE_POWER_ACTOR_ADDR]); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_POWER_ACTOR_ADDR]); let params = &RawBytes::serialize(BigIntSer(curr_raw_power)).unwrap(); assert!(rt.call::(Method::UpdateNetworkKPI as u64, params).is_ok()); diff --git a/actors/system/src/lib.rs b/actors/system/src/lib.rs index d8b8c2c5b..c9676b63d 100644 --- a/actors/system/src/lib.rs +++ b/actors/system/src/lib.rs @@ -63,7 +63,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; let state = State::new(rt.store()).map_err(|e| { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") @@ -105,8 +105,8 @@ mod tests { pub fn new_runtime() -> MockRuntime { MockRuntime { - receiver: *SYSTEM_ACTOR_ADDR, - caller: *SYSTEM_ACTOR_ADDR, + receiver: SYSTEM_ACTOR_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -115,8 +115,8 @@ mod tests { #[test] fn construct_with_root_id() { let mut rt = new_runtime(); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); - rt.set_caller(*SYSTEM_ACTOR_CODE_ID, *SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.call::(Method::Constructor as MethodNum, &RawBytes::default()).unwrap(); let state: State = rt.get_state(); diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index 0d6205ca8..a106fe373 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -50,7 +50,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; // root should be an ID address let id_addr = rt @@ -325,7 +325,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_MARKET_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&STORAGE_MARKET_ACTOR_ADDR))?; let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { e.downcast_default( @@ -428,7 +428,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_MARKET_ACTOR_ADDR))?; + rt.validate_immediate_caller_is(std::iter::once(&STORAGE_MARKET_ACTOR_ADDR))?; if params.deal_size < rt.policy().minimum_verified_deal_size { return Err(actor_error!( illegal_argument, diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index b7760631a..76d5f1fe5 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -5,7 +5,6 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntDe; use fvm_shared::{MethodNum, HAMT_BIT_WIDTH}; -use lazy_static::lazy_static; use fil_actor_verifreg::{ Actor as VerifregActor, AddVerifierClientParams, AddVerifierParams, DataCap, Method, @@ -17,14 +16,12 @@ use fil_actors_runtime::{ SYSTEM_ACTOR_ADDR, }; -lazy_static! { - pub static ref ROOT_ADDR: Address = Address::new_id(101); -} +pub const ROOT_ADDR: Address = Address::new_id(101); pub fn new_runtime() -> MockRuntime { MockRuntime { - receiver: *ROOT_ADDR, - caller: *SYSTEM_ACTOR_ADDR, + receiver: ROOT_ADDR, + caller: SYSTEM_ACTOR_ADDR, caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() } @@ -32,7 +29,7 @@ pub fn new_runtime() -> MockRuntime { pub fn new_harness() -> (Harness, MockRuntime) { let mut rt = new_runtime(); - let h = Harness { root: *ROOT_ADDR }; + let h = Harness { root: ROOT_ADDR }; h.construct_and_verify(&mut rt, &h.root); (h, rt) } @@ -43,7 +40,7 @@ pub struct Harness { impl Harness { pub fn construct_and_verify(&self, rt: &mut MockRuntime, root_param: &Address) { - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( Method::Constructor as MethodNum, @@ -179,8 +176,8 @@ impl Harness { client: &Address, amount: &DataCap, ) -> Result<(), ActorError> { - rt.expect_validate_caller_addr(vec![*STORAGE_MARKET_ACTOR_ADDR]); - rt.set_caller(*MARKET_ACTOR_CODE_ID, *STORAGE_MARKET_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_MARKET_ACTOR_ADDR]); + rt.set_caller(*MARKET_ACTOR_CODE_ID, STORAGE_MARKET_ACTOR_ADDR); let params = UseBytesParams { address: *client, deal_size: amount.clone() }; let ret = rt.call::( Method::UseBytes as MethodNum, @@ -197,8 +194,8 @@ impl Harness { client: &Address, amount: &DataCap, ) -> Result<(), ActorError> { - rt.expect_validate_caller_addr(vec![*STORAGE_MARKET_ACTOR_ADDR]); - rt.set_caller(*MARKET_ACTOR_CODE_ID, *STORAGE_MARKET_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_MARKET_ACTOR_ADDR]); + rt.set_caller(*MARKET_ACTOR_CODE_ID, STORAGE_MARKET_ACTOR_ADDR); let params = RestoreBytesParams { address: *client, deal_size: amount.clone() }; let ret = rt.call::( Method::RestoreBytes as MethodNum, diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index c3be28044..92b7d813a 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -41,7 +41,7 @@ mod construction { #[test] fn construct_with_root_id() { let mut rt = new_runtime(); - let h = Harness { root: *ROOT_ADDR }; + let h = Harness { root: ROOT_ADDR }; h.construct_and_verify(&mut rt, &h.root); h.check_state(&rt); } @@ -49,7 +49,7 @@ mod construction { #[test] fn construct_resolves_non_id() { let mut rt = new_runtime(); - let h = Harness { root: *ROOT_ADDR }; + let h = Harness { root: ROOT_ADDR }; let root_pubkey = Address::new_bls(&[7u8; BLS_PUB_LEN]).unwrap(); rt.id_addresses.insert(root_pubkey, h.root); h.construct_and_verify(&mut rt, &root_pubkey); @@ -61,7 +61,7 @@ mod construction { let mut rt = new_runtime(); let root_pubkey = Address::new_bls(&[7u8; BLS_PUB_LEN]).unwrap(); - rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -517,8 +517,8 @@ mod datacap { #[test] fn consume_requires_market_actor_caller() { let (h, mut rt) = new_harness(); - rt.expect_validate_caller_addr(vec![*STORAGE_MARKET_ACTOR_ADDR]); - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_MARKET_ACTOR_ADDR]); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); let params = UseBytesParams { address: *CLIENT, deal_size: rt.policy.minimum_verified_deal_size.clone(), @@ -675,8 +675,8 @@ mod datacap { #[test] fn restore_requires_market_actor_caller() { let (h, mut rt) = new_harness(); - rt.expect_validate_caller_addr(vec![*STORAGE_MARKET_ACTOR_ADDR]); - rt.set_caller(*POWER_ACTOR_CODE_ID, *STORAGE_POWER_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![STORAGE_MARKET_ACTOR_ADDR]); + rt.set_caller(*POWER_ACTOR_CODE_ID, STORAGE_POWER_ACTOR_ADDR); let params = RestoreBytesParams { address: *CLIENT, deal_size: rt.policy.minimum_verified_deal_size.clone(), diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index d46232fee..3ab9d19e8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -27,6 +27,7 @@ multihash = { version = "0.16.1", default-features = false } serde_repr = "0.1.8" regex = "1" itertools = "0.10" +paste = "1.0.9" # A fake-proofs dependency but... we can't select on that feature here because we enable it from # build.rs. diff --git a/runtime/src/builtin/singletons.rs b/runtime/src/builtin/singletons.rs index ccaf4bc83..8c896e976 100644 --- a/runtime/src/builtin/singletons.rs +++ b/runtime/src/builtin/singletons.rs @@ -1,22 +1,31 @@ -// Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT use fvm_shared::address::Address; use fvm_shared::ActorID; -lazy_static! { - pub static ref SYSTEM_ACTOR_ADDR: Address = Address::new_id(0); - pub static ref INIT_ACTOR_ADDR: Address = Address::new_id(1); - pub static ref REWARD_ACTOR_ADDR: Address = Address::new_id(2); - pub static ref CRON_ACTOR_ADDR: Address = Address::new_id(3); - pub static ref STORAGE_POWER_ACTOR_ADDR: Address = Address::new_id(4); - pub static ref STORAGE_MARKET_ACTOR_ADDR: Address = Address::new_id(5); - pub static ref VERIFIED_REGISTRY_ACTOR_ADDR: Address = Address::new_id(6); +use paste::paste; - pub static ref CHAOS_ACTOR_ADDR: Address = Address::new_id(98); +macro_rules! define_singletons { + ($($name:ident = $id:literal,)*) => { + $( + paste! { + pub const [<$name _ID>]: ActorID = $id; + pub const [<$name _ADDR>]: Address = Address::new_id([<$name _ID>]); + } + )* + } +} - /// Distinguished AccountActor that is the destination of all burnt funds. - pub static ref BURNT_FUNDS_ACTOR_ADDR: Address = Address::new_id(99); +define_singletons! { + SYSTEM_ACTOR = 0, + INIT_ACTOR = 1, + REWARD_ACTOR = 2, + CRON_ACTOR = 3, + STORAGE_POWER_ACTOR = 4, + STORAGE_MARKET_ACTOR = 5, + VERIFIED_REGISTRY_ACTOR = 6, + CHAOS_ACTOR = 98, + BURNT_FUNDS_ACTOR = 99, } /// Defines first available ID address after builtin actors diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ce322f463..f48f9703f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,13 +1,6 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -// TODO: disable everything else when not using runtime-wasm - -#[macro_use] -extern crate lazy_static; -// workaround for a compiler bug, see https://github.com/rust-lang/rust/issues/55779 -extern crate serde; - use builtin::HAMT_BIT_WIDTH; use cid::Cid; use fvm_ipld_amt::Amt; diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 5db73860e..0ea68175e 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -32,7 +32,7 @@ use crate::runtime::{ }; use crate::{actor_error, ActorError, Runtime}; -lazy_static! { +lazy_static::lazy_static! { /// Cid of the empty array Cbor bytes (`EMPTY_ARR_BYTES`). pub static ref EMPTY_ARR_CID: Cid = { let empty = to_vec::<[(); 0]>(&[]).unwrap(); diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 40c7cc761..bffd3fd15 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -45,7 +45,7 @@ use crate::runtime::{ use crate::{actor_error, ActorError}; use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; -lazy_static! { +lazy_static::lazy_static! { pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system"); pub static ref INIT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/init"); pub static ref CRON_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/cron"); diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 24fc3ca2e..0085cfd03 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -143,56 +143,50 @@ impl<'bs> VM<'bs> { let sys_st = SystemState::new(store).unwrap(); let sys_head = v.put_store(&sys_st); let sys_value = faucet_total.clone(); // delegate faucet funds to system so we can construct faucet by sending to bls addr - v.set_actor(*SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value)); + v.set_actor(SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value)); // init let init_st = InitState::new(store, "integration-test".to_string()).unwrap(); let init_head = v.put_store(&init_st); - v.set_actor( - *INIT_ACTOR_ADDR, - actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero()), - ); + v.set_actor(INIT_ACTOR_ADDR, actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero())); // reward let reward_head = v.put_store(&RewardState::new(StoragePower::zero())); - v.set_actor(*REWARD_ACTOR_ADDR, actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total)); + v.set_actor(REWARD_ACTOR_ADDR, actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total)); // cron let builtin_entries = vec![ CronEntry { - receiver: *STORAGE_POWER_ACTOR_ADDR, + receiver: STORAGE_POWER_ACTOR_ADDR, method_num: MethodPower::OnEpochTickEnd as u64, }, CronEntry { - receiver: *STORAGE_MARKET_ACTOR_ADDR, + receiver: STORAGE_MARKET_ACTOR_ADDR, method_num: MarketMethod::CronTick as u64, }, ]; let cron_head = v.put_store(&CronState { entries: builtin_entries }); - v.set_actor( - *CRON_ACTOR_ADDR, - actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero()), - ); + v.set_actor(CRON_ACTOR_ADDR, actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero())); // power let power_head = v.put_store(&PowerState::new(&v.store).unwrap()); v.set_actor( - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero()), ); // market let market_head = v.put_store(&MarketState::new(&v.store).unwrap()); v.set_actor( - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero()), ); // verifreg // initialize verifreg root signer v.apply_message( - *INIT_ACTOR_ADDR, + INIT_ACTOR_ADDR, Address::new_bls(VERIFREG_ROOT_KEY).unwrap(), TokenAmount::zero(), METHOD_SEND, @@ -215,8 +209,8 @@ impl<'bs> VM<'bs> { .unwrap(); let msig_ctor_ret: ExecReturn = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *INIT_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + INIT_ACTOR_ADDR, TokenAmount::zero(), fil_actor_init::Method::Exec as u64, fil_actor_init::ExecParams { @@ -233,20 +227,20 @@ impl<'bs> VM<'bs> { // verifreg let verifreg_head = v.put_store(&VerifRegState::new(&v.store, root_msig_addr).unwrap()); v.set_actor( - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero()), ); // burnt funds - let burnt_funds_head = v.put_store(&AccountState { address: *BURNT_FUNDS_ACTOR_ADDR }); + let burnt_funds_head = v.put_store(&AccountState { address: BURNT_FUNDS_ACTOR_ADDR }); v.set_actor( - *BURNT_FUNDS_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero()), ); // create a faucet with 1 billion FIL for setting up test accounts v.apply_message( - *SYSTEM_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, Address::new_bls(FAUCET_ROOT_KEY).unwrap(), faucet_total, METHOD_SEND, @@ -285,9 +279,9 @@ impl<'bs> VM<'bs> { } pub fn get_network_stats(&self) -> NetworkStats { - let power_state = self.get_state::(*STORAGE_POWER_ACTOR_ADDR).unwrap(); - let reward_state = self.get_state::(*REWARD_ACTOR_ADDR).unwrap(); - let market_state = self.get_state::(*STORAGE_MARKET_ACTOR_ADDR).unwrap(); + let power_state = self.get_state::(STORAGE_POWER_ACTOR_ADDR).unwrap(); + let reward_state = self.get_state::(REWARD_ACTOR_ADDR).unwrap(); + let market_state = self.get_state::(STORAGE_MARKET_ACTOR_ADDR).unwrap(); NetworkStats { total_raw_byte_power: power_state.total_raw_byte_power, @@ -364,7 +358,7 @@ impl<'bs> VM<'bs> { } pub fn normalize_address(&self, addr: &Address) -> Option
{ - let st = self.get_state::(*INIT_ACTOR_ADDR).unwrap(); + let st = self.get_state::(INIT_ACTOR_ADDR).unwrap(); st.resolve_address::(self.store, addr).unwrap() } @@ -566,15 +560,15 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } _ => (), } - let mut st = self.v.get_state::(*INIT_ACTOR_ADDR).unwrap(); + let mut st = self.v.get_state::(INIT_ACTOR_ADDR).unwrap(); let target_id = st.map_address_to_new_id(self.v.store, target).unwrap(); let target_id_addr = Address::new_id(target_id); - let mut init_actor = self.v.get_actor(*INIT_ACTOR_ADDR).unwrap(); + let mut init_actor = self.v.get_actor(INIT_ACTOR_ADDR).unwrap(); init_actor.head = self.v.store.put_cbor(&st, Code::Blake2b256).unwrap(); - self.v.set_actor(*INIT_ACTOR_ADDR, init_actor); + self.v.set_actor(INIT_ACTOR_ADDR, init_actor); let new_actor_msg = InternalMessage { - from: *SYSTEM_ACTOR_ADDR, + from: SYSTEM_ACTOR_ADDR, to: target_id_addr, value: TokenAmount::zero(), method: METHOD_CONSTRUCTOR, diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 37bb756b2..eda71ee6c 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -109,7 +109,7 @@ pub fn create_miner( let res: CreateMinerReturn = v .apply_message( owner, - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, balance, PowerMethod::CreateMiner as u64, params, @@ -137,12 +137,12 @@ pub fn precommit_sectors( let invocs_common = || -> Vec { vec![ ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CurrentTotalPower as u64, ..Default::default() }, @@ -150,14 +150,14 @@ pub fn precommit_sectors( }; let invoc_first = || -> ExpectInvocation { ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::EnrollCronEvent as u64, ..Default::default() } }; let invoc_net_fee = |fee: TokenAmount| -> ExpectInvocation { ExpectInvocation { - to: *BURNT_FUNDS_ACTOR_ADDR, + to: BURNT_FUNDS_ACTOR_ADDR, method: METHOD_SEND, value: Some(fee), ..Default::default() @@ -269,27 +269,27 @@ pub fn prove_commit_sectors( params: Some(prove_commit_aggregate_params_ser), subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::ComputeDataCommitment as u64, ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CurrentTotalPower as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdatePledgeTotal as u64, ..Default::default() }, ExpectInvocation { - to: *BURNT_FUNDS_ACTOR_ADDR, + to: BURNT_FUNDS_ACTOR_ADDR, method: METHOD_SEND, ..Default::default() }, @@ -363,8 +363,8 @@ where let res = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -416,7 +416,7 @@ pub fn sector_info(v: &VM, m: Address, s: SectorNumber) -> SectorOnChainInfo { } pub fn miner_power(v: &VM, m: Address) -> PowerPair { - let st = v.get_state::(*STORAGE_POWER_ACTOR_ADDR).unwrap(); + let st = v.get_state::(STORAGE_POWER_ACTOR_ADDR).unwrap(); let claim = st.get_claim(v.store, &m).unwrap().unwrap(); PowerPair::new(claim.raw_byte_power, claim.quality_adj_power) } @@ -485,7 +485,7 @@ pub fn submit_windowed_post( ) .unwrap(); subinvocs = Some(vec![ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdateClaimedPower as u64, params: Some(update_power_params), ..Default::default() @@ -529,7 +529,7 @@ pub fn add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) { let add_verifier_params = VerifierParams { address: verifier, allowance: data_cap }; // root address is msig, send proposal from root key let proposal = ProposeParams { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, value: TokenAmount::zero(), method: VerifregMethod::AddVerifier as u64, params: serialize(&add_verifier_params, "verifreg add verifier params").unwrap(), @@ -544,7 +544,7 @@ pub fn add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) { proposal, ); let verifreg_invoc = ExpectInvocation { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::AddVerifier as u64, params: Some(serialize(&add_verifier_params, "verifreg add verifier params").unwrap()), subinvocs: Some(vec![]), @@ -595,7 +595,7 @@ pub fn publish_deal( let ret: PublishStorageDealsReturn = apply_ok( v, provider, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, TokenAmount::zero(), MarketMethod::PublishStorageDeals as u64, publish_params, @@ -610,25 +610,25 @@ pub fn publish_deal( ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CurrentTotalPower as u64, ..Default::default() }, ]; if verified_deal { expect_publish_invocs.push(ExpectInvocation { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::UseBytes as u64, ..Default::default() }) } ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::PublishStorageDeals as u64, subinvocs: Some(expect_publish_invocs), ..Default::default() diff --git a/test_vm/tests/batch_onboarding.rs b/test_vm/tests/batch_onboarding.rs index 38f5f606a..b9b4bcc3e 100644 --- a/test_vm/tests/batch_onboarding.rs +++ b/test_vm/tests/batch_onboarding.rs @@ -143,8 +143,8 @@ fn batch_onboarding() { apply_ok( &v, - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), diff --git a/test_vm/tests/commit_post_test.rs b/test_vm/tests/commit_post_test.rs index e2315a14d..9b3947bc9 100644 --- a/test_vm/tests/commit_post_test.rs +++ b/test_vm/tests/commit_post_test.rs @@ -85,12 +85,12 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { params: Some(prove_params_ser), subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::ComputeDataCommitment as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::SubmitPoRepForBulkVerify as u64, ..Default::default() }, @@ -100,8 +100,8 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { .matches(v.take_invocations().last().unwrap()); let res = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -109,15 +109,15 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { .unwrap(); assert_eq!(ExitCode::OK, res.code); ExpectInvocation { - to: *CRON_ACTOR_ADDR, + to: CRON_ACTOR_ADDR, method: CronMethod::EpochTick as u64, subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::OnEpochTickEnd as u64, subinvocs: Some(vec![ ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, @@ -125,14 +125,14 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { to: id_addr, method: MinerMethod::ConfirmSectorProofsValid as u64, subinvocs: Some(vec![ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdatePledgeTotal as u64, ..Default::default() }]), ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::UpdateNetworkKPI as u64, ..Default::default() }, @@ -140,7 +140,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, MinerInfo, SectorInfo) { ..Default::default() }, ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::CronTick as u64, ..Default::default() }, @@ -192,7 +192,7 @@ fn submit_post_succeeds() { ); let balances = v.get_miner_balance(miner_info.miner_id); assert!(balances.initial_pledge.is_positive()); - let p_st = v.get_state::(*STORAGE_POWER_ACTOR_ADDR).unwrap(); + let p_st = v.get_state::(STORAGE_POWER_ACTOR_ADDR).unwrap(); assert_eq!(sector_power.raw, p_st.total_bytes_committed); v.assert_state_invariants(); @@ -254,24 +254,24 @@ fn missed_first_post_deadline() { apply_ok( &v, - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), ); ExpectInvocation { - to: *CRON_ACTOR_ADDR, + to: CRON_ACTOR_ADDR, method: CronMethod::EpochTick as u64, params: None, subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::OnEpochTickEnd as u64, subinvocs: Some(vec![ ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, @@ -279,14 +279,14 @@ fn missed_first_post_deadline() { to: miner_info.miner_id, method: MinerMethod::OnDeferredCronEvent as u64, subinvocs: Some(vec![ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::EnrollCronEvent as u64, ..Default::default() }]), ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::UpdateNetworkKPI as u64, ..Default::default() }, @@ -294,7 +294,7 @@ fn missed_first_post_deadline() { ..Default::default() }, ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::CronTick as u64, ..Default::default() }, @@ -359,24 +359,24 @@ fn overdue_precommit() { // run cron which should clean up precommit apply_ok( &v, - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), ); ExpectInvocation { - to: *CRON_ACTOR_ADDR, + to: CRON_ACTOR_ADDR, method: CronMethod::EpochTick as u64, params: None, subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::OnEpochTickEnd as u64, subinvocs: Some(vec![ ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, @@ -386,7 +386,7 @@ fn overdue_precommit() { subinvocs: Some(vec![ ExpectInvocation { // The call to burnt funds indicates the overdue precommit has been penalized - to: *BURNT_FUNDS_ACTOR_ADDR, + to: BURNT_FUNDS_ACTOR_ADDR, method: METHOD_SEND, value: Option::from(precommit.pre_commit_deposit), ..Default::default() @@ -396,7 +396,7 @@ fn overdue_precommit() { ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::UpdateNetworkKPI as u64, ..Default::default() }, @@ -404,7 +404,7 @@ fn overdue_precommit() { ..Default::default() }, ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::CronTick as u64, ..Default::default() }, @@ -819,27 +819,27 @@ fn aggregate_one_precommit_expires() { params: Some(prove_params_ser), subinvocs: Some(vec![ ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::ComputeDataCommitment as u64, ..Default::default() }, ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CurrentTotalPower as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdatePledgeTotal as u64, ..Default::default() }, ExpectInvocation { - to: *BURNT_FUNDS_ACTOR_ADDR, + to: BURNT_FUNDS_ACTOR_ADDR, method: METHOD_SEND, ..Default::default() }, diff --git a/test_vm/tests/extend_sectors_test.rs b/test_vm/tests/extend_sectors_test.rs index e6a5dee1d..847a369da 100644 --- a/test_vm/tests/extend_sectors_test.rs +++ b/test_vm/tests/extend_sectors_test.rs @@ -65,7 +65,7 @@ fn extend_sector_with_deals() { apply_ok( &v, verifier, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::AddVerifiedClient as u64, add_client_params, @@ -76,7 +76,7 @@ fn extend_sector_with_deals() { apply_ok( &v, verified_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral.clone(), MarketMethod::AddBalance as u64, verified_client, @@ -85,7 +85,7 @@ fn extend_sector_with_deals() { apply_ok( &v, worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral, MarketMethod::AddBalance as u64, miner_id, @@ -153,8 +153,8 @@ fn extend_sector_with_deals() { // In the same epoch, trigger cron to validate prove commit apply_ok( &v, - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -242,7 +242,7 @@ fn extend_sector_with_deals() { to: miner_id, method: MinerMethod::ExtendSectorExpiration as u64, subinvocs: Some(vec![ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdateClaimedPower as u64, params: Some(expected_update_claimed_power_params_ser), ..Default::default() @@ -295,7 +295,7 @@ fn extend_sector_with_deals() { to: miner_id, method: MinerMethod::ExtendSectorExpiration as u64, subinvocs: Some(vec![ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdateClaimedPower as u64, params: Some(expected_update_claimed_power_params_ser), ..Default::default() diff --git a/test_vm/tests/market_miner_withdrawal_test.rs b/test_vm/tests/market_miner_withdrawal_test.rs index 632e8802d..dd9c37b2a 100644 --- a/test_vm/tests/market_miner_withdrawal_test.rs +++ b/test_vm/tests/market_miner_withdrawal_test.rs @@ -32,7 +32,7 @@ mod market_tests { three_fil.clone(), three_fil.clone(), three_fil, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, caller, ); } @@ -50,7 +50,7 @@ mod market_tests { two_fil.clone(), two_fil, three_fil, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, caller, ); } @@ -67,7 +67,7 @@ mod market_tests { TokenAmount::zero(), TokenAmount::zero(), three_fil, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, caller, ); } diff --git a/test_vm/tests/multisig_test.rs b/test_vm/tests/multisig_test.rs index 8cc5e2591..8f5c07f61 100644 --- a/test_vm/tests/multisig_test.rs +++ b/test_vm/tests/multisig_test.rs @@ -26,14 +26,14 @@ fn test_proposal_hash() { let addrs = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); let alice = addrs[0]; let bob = addrs[1]; - let sys_act_start_bal = v.get_actor(*SYSTEM_ACTOR_ADDR).unwrap().balance; + let sys_act_start_bal = v.get_actor(SYSTEM_ACTOR_ADDR).unwrap().balance; let msig_addr = create_msig(&v, addrs, 2); // fund msig and propose send funds to system actor let fil_delta = TokenAmount::from_nano(3); let propose_send_sys_params = ProposeParams { - to: *SYSTEM_ACTOR_ADDR, + to: SYSTEM_ACTOR_ADDR, value: fil_delta.clone(), method: METHOD_SEND, params: RawBytes::default(), @@ -48,7 +48,7 @@ fn test_proposal_hash() { ); let wrong_tx = Transaction { - to: *SYSTEM_ACTOR_ADDR, + to: SYSTEM_ACTOR_ADDR, value: &fil_delta - TokenAmount::from_atto(1), // incorrect send amount not consistent with proposal method: METHOD_SEND, approved: vec![alice], @@ -67,7 +67,7 @@ fn test_proposal_hash() { ); let correct_tx = Transaction { - to: *SYSTEM_ACTOR_ADDR, + to: SYSTEM_ACTOR_ADDR, value: fil_delta.clone(), method: METHOD_SEND, approved: vec![alice], @@ -89,12 +89,12 @@ fn test_proposal_hash() { method: MsigMethod::Approve as u64, subinvocs: Some(vec![ // Tx goes through to fund the system actor - ExpectInvocation { to: *SYSTEM_ACTOR_ADDR, method: METHOD_SEND, ..Default::default() }, + ExpectInvocation { to: SYSTEM_ACTOR_ADDR, method: METHOD_SEND, ..Default::default() }, ]), ..Default::default() }; expect.matches(v.take_invocations().last().unwrap()); - assert_eq!(sys_act_start_bal + fil_delta, v.get_actor(*SYSTEM_ACTOR_ADDR).unwrap().balance); + assert_eq!(sys_act_start_bal + fil_delta, v.get_actor(SYSTEM_ACTOR_ADDR).unwrap().balance); v.assert_state_invariants(); } @@ -288,7 +288,7 @@ fn create_msig(v: &VM, signers: Vec
, threshold: u64) -> Address { let msig_ctor_ret: ExecReturn = apply_ok( v, signers[0], - *INIT_ACTOR_ADDR, + INIT_ACTOR_ADDR, TokenAmount::zero(), fil_actor_init::Method::Exec as u64, fil_actor_init::ExecParams { diff --git a/test_vm/tests/power_scenario_tests.rs b/test_vm/tests/power_scenario_tests.rs index 7512b1faa..fd4dbba70 100644 --- a/test_vm/tests/power_scenario_tests.rs +++ b/test_vm/tests/power_scenario_tests.rs @@ -52,7 +52,7 @@ fn create_miner_test() { let res = v .apply_message( owner, - *STORAGE_POWER_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, TokenAmount::from_atto(1000u32), PowerMethod::CreateMiner as u64, params.clone(), @@ -61,14 +61,14 @@ fn create_miner_test() { let expect = ExpectInvocation { // send to power actor - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CreateMiner as u64, params: Some(serialize(¶ms, "power create miner params").unwrap()), ret: Some(res.ret), subinvocs: Some(vec![ // request init actor construct miner ExpectInvocation { - to: *INIT_ACTOR_ADDR, + to: INIT_ACTOR_ADDR, method: InitMethod::Exec as u64, subinvocs: Some(vec![ExpectInvocation { // init then calls miner constructor @@ -151,8 +151,8 @@ fn test_cron_tick() { // run cron and expect a call to miner and a call to update reward actor params apply_ok( &v, - *CRON_ACTOR_ADDR, - *STORAGE_POWER_ACTOR_ADDR, + CRON_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, TokenAmount::zero(), PowerMethod::OnEpochTickEnd as u64, RawBytes::default(), @@ -161,20 +161,20 @@ fn test_cron_tick() { // expect miner call to be missing ExpectInvocation { // original send to storage power actor - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::OnEpochTickEnd as u64, subinvocs: Some(vec![ // get data from reward actor for any eventual calls to confirmsectorproofsparams ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, // expect call to reward to update kpi ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::UpdateNetworkKPI as u64, - from: Some(*STORAGE_POWER_ACTOR_ADDR), + from: Some(STORAGE_POWER_ACTOR_ADDR), ..Default::default() }, ]), @@ -188,8 +188,8 @@ fn test_cron_tick() { // run cron and expect a call to miner and a a call to update reward actor params apply_ok( &v, - *CRON_ACTOR_ADDR, - *STORAGE_POWER_ACTOR_ADDR, + CRON_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, TokenAmount::zero(), PowerMethod::OnEpochTickEnd as u64, RawBytes::default(), @@ -198,7 +198,7 @@ fn test_cron_tick() { let sub_invocs = vec![ // get data from reward and power for any eventual calls to confirmsectorproofsvalid ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, @@ -206,15 +206,15 @@ fn test_cron_tick() { ExpectInvocation { to: id_addr, method: MinerMethod::OnDeferredCronEvent as u64, - from: Some(*STORAGE_POWER_ACTOR_ADDR), + from: Some(STORAGE_POWER_ACTOR_ADDR), value: Some(TokenAmount::zero()), ..Default::default() }, // expect call to reward to update kpi ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::UpdateNetworkKPI as u64, - from: Some(*STORAGE_POWER_ACTOR_ADDR), + from: Some(STORAGE_POWER_ACTOR_ADDR), ..Default::default() }, ]; @@ -222,7 +222,7 @@ fn test_cron_tick() { // expect call to miner ExpectInvocation { // original send to storage power actor - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::OnEpochTickEnd as u64, subinvocs: Some(sub_invocs), ..Default::default() diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 0a0565364..b0a9432ba 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -71,7 +71,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { apply_ok( &v, verifier, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::AddVerifiedClient as u64, add_client_params, @@ -81,7 +81,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { apply_ok( &v, client1, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, client_collateral.clone(), MarketMethod::AddBalance as u64, client1, @@ -89,7 +89,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { apply_ok( &v, client2, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, client_collateral.clone(), MarketMethod::AddBalance as u64, client2, @@ -97,7 +97,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { apply_ok( &v, verified_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, client_collateral, MarketMethod::AddBalance as u64, verified_client, @@ -107,7 +107,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { apply_ok( &v, worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, miner_collateral, MarketMethod::AddBalance as u64, maddr, @@ -229,7 +229,7 @@ fn psd_not_enought_client_lockup_for_batch() { apply_ok( &v, a.cheap_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, one_lifetime_cost, MarketMethod::AddBalance as u64, a.cheap_client, @@ -269,7 +269,7 @@ fn psd_not_enough_provider_lockup_for_batch() { apply_ok( &v, cheap_worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, default_provider_collateral, MarketMethod::AddBalance as u64, cheap_maddr, @@ -392,7 +392,7 @@ fn psd_random_assortment_of_failures() { apply_ok( &v, a.cheap_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, one_lifetime_cost, MarketMethod::AddBalance as u64, a.cheap_client, @@ -611,7 +611,7 @@ impl<'bs> DealBatcher<'bs> { let ret: PublishStorageDealsReturn = apply_ok( self.v, sender, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, TokenAmount::zero(), MarketMethod::PublishStorageDeals as u64, publish_params, @@ -635,7 +635,7 @@ impl<'bs> DealBatcher<'bs> { .v .apply_message( sender, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, TokenAmount::zero(), MarketMethod::PublishStorageDeals as u64, publish_params, diff --git a/test_vm/tests/replica_update_test.rs b/test_vm/tests/replica_update_test.rs index a99e36dc0..b93940a54 100644 --- a/test_vm/tests/replica_update_test.rs +++ b/test_vm/tests/replica_update_test.rs @@ -886,8 +886,8 @@ fn deal_included_in_multiple_sectors_failure() { // In the same epoch, trigger cron to validate prove commit apply_ok( &v, - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -1061,8 +1061,8 @@ fn create_sector( ); let res = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -1110,7 +1110,7 @@ fn create_deals( apply_ok( v, client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral.clone(), MarketMethod::AddBalance as u64, client, @@ -1118,7 +1118,7 @@ fn create_deals( apply_ok( v, worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral, MarketMethod::AddBalance as u64, maddr, diff --git a/test_vm/tests/terminate_test.rs b/test_vm/tests/terminate_test.rs index 36ddff82e..14561fa0a 100644 --- a/test_vm/tests/terminate_test.rs +++ b/test_vm/tests/terminate_test.rs @@ -64,7 +64,7 @@ fn terminate_sectors() { apply_ok( &v, verifier, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::AddVerifiedClient as u64, add_client_params, @@ -75,7 +75,7 @@ fn terminate_sectors() { apply_ok( &v, unverified_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral.clone(), MarketMethod::AddBalance as u64, unverified_client, @@ -83,7 +83,7 @@ fn terminate_sectors() { apply_ok( &v, verified_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, collateral, MarketMethod::AddBalance as u64, verified_client, @@ -93,7 +93,7 @@ fn terminate_sectors() { apply_ok( &v, worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, miner_collateral.clone(), MarketMethod::AddBalance as u64, id_addr, @@ -147,15 +147,15 @@ fn terminate_sectors() { let res = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), ) .unwrap(); assert_eq!(ExitCode::OK, res.code); - let st = v.get_state::(*STORAGE_MARKET_ACTOR_ADDR).unwrap(); + let st = v.get_state::(STORAGE_MARKET_ACTOR_ADDR).unwrap(); let deal_states = DealMetaArray::load(&st.states, v.store).unwrap(); for id in deal_ids.iter() { // deals are pending and don't yet have deal states @@ -194,8 +194,8 @@ fn terminate_sectors() { ); let res = v .apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -211,8 +211,8 @@ fn terminate_sectors() { let v = v.with_epoch(dline_info.last()); v.apply_message( - *SYSTEM_ACTOR_ADDR, - *CRON_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, TokenAmount::zero(), CronMethod::EpochTick as u64, RawBytes::default(), @@ -232,7 +232,7 @@ fn terminate_sectors() { ); // market cron updates deal states indication deals are no longer pending - let st = v.get_state::(*STORAGE_MARKET_ACTOR_ADDR).unwrap(); + let st = v.get_state::(STORAGE_MARKET_ACTOR_ADDR).unwrap(); let deal_states = DealMetaArray::load(&st.states, v.store).unwrap(); for id in deal_ids.iter() { let state = deal_states.get(*id).unwrap().unwrap(); @@ -260,32 +260,32 @@ fn terminate_sectors() { method: MinerMethod::TerminateSectors as u64, subinvocs: Some(vec![ ExpectInvocation { - to: *REWARD_ACTOR_ADDR, + to: REWARD_ACTOR_ADDR, method: RewardMethod::ThisEpochReward as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::CurrentTotalPower as u64, ..Default::default() }, ExpectInvocation { - to: *BURNT_FUNDS_ACTOR_ADDR, + to: BURNT_FUNDS_ACTOR_ADDR, method: METHOD_SEND, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdatePledgeTotal as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::OnMinerSectorsTerminate as u64, ..Default::default() }, ExpectInvocation { - to: *STORAGE_POWER_ACTOR_ADDR, + to: STORAGE_POWER_ACTOR_ADDR, method: PowerMethod::UpdateClaimedPower as u64, ..Default::default() }, @@ -298,7 +298,7 @@ fn terminate_sectors() { assert!(miner_balances.initial_pledge.is_zero()); assert!(miner_balances.pre_commit_deposit.is_zero()); - let pow_st = v.get_state::(*STORAGE_POWER_ACTOR_ADDR).unwrap(); + let pow_st = v.get_state::(STORAGE_POWER_ACTOR_ADDR).unwrap(); assert_eq!(0, pow_st.miner_above_min_power_count); assert!(pow_st.total_raw_byte_power.is_zero()); assert!(pow_st.total_quality_adj_power.is_zero()); @@ -308,7 +308,7 @@ fn terminate_sectors() { // termination slashes deals in market state let termination_epoch = v.get_epoch(); - let st = v.get_state::(*STORAGE_MARKET_ACTOR_ADDR).unwrap(); + let st = v.get_state::(STORAGE_MARKET_ACTOR_ADDR).unwrap(); let deal_states = DealMetaArray::load(&st.states, v.store).unwrap(); for id in deal_ids.iter() { let state = deal_states.get(*id).unwrap().unwrap(); @@ -328,13 +328,13 @@ fn terminate_sectors() { apply_ok( &v, verified_client, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, TokenAmount::zero(), MarketMethod::WithdrawBalance as u64, WithdrawBalanceParams { provider_or_client: verified_client, amount: withdrawal.clone() }, ); ExpectInvocation { - to: *STORAGE_MARKET_ACTOR_ADDR, + to: STORAGE_MARKET_ACTOR_ADDR, method: MarketMethod::WithdrawBalance as u64, subinvocs: Some(vec![ExpectInvocation { to: verified_client, @@ -349,7 +349,7 @@ fn terminate_sectors() { apply_ok( &v, worker, - *STORAGE_MARKET_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, TokenAmount::zero(), MarketMethod::WithdrawBalance as u64, WithdrawBalanceParams { provider_or_client: id_addr, amount: miner_collateral }, diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index c32607197..6228c5ab8 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -43,14 +43,14 @@ fn remove_datacap_simple_successful_path() { apply_ok( &v, verifier1, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::AddVerifiedClient as u64, add_verified_client_params, ); ExpectInvocation { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::AddVerifiedClient as u64, params: Some(add_verified_client_params_ser), subinvocs: Some(vec![]), @@ -59,7 +59,7 @@ fn remove_datacap_simple_successful_path() { .matches(v.take_invocations().last().unwrap()); // state checks on the 2 verifiers and the client - let mut v_st = v.get_state::(*VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); + let mut v_st = v.get_state::(VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); let verifiers = make_map_with_root_and_bitwidth::<_, BigIntDe>(&v_st.verifiers, &store, HAMT_BIT_WIDTH) .unwrap(); @@ -140,7 +140,7 @@ fn remove_datacap_simple_successful_path() { let remove_datacap_ret: RemoveDataCapReturn = apply_ok( &v, TEST_VERIFREG_ROOT_ADDR, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::RemoveVerifiedClientDataCap as u64, remove_datacap_params, @@ -149,7 +149,7 @@ fn remove_datacap_simple_successful_path() { .unwrap(); ExpectInvocation { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::RemoveVerifiedClientDataCap as u64, params: Some(remove_datacap_params_ser), subinvocs: Some(vec![]), @@ -160,7 +160,7 @@ fn remove_datacap_simple_successful_path() { assert_eq!(verified_client_id_addr, remove_datacap_ret.verified_client); assert_eq!(allowance_to_remove, remove_datacap_ret.data_cap_removed); - v_st = v.get_state::(*VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); + v_st = v.get_state::(VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); // confirm client's allowance has fallen by half verified_clients = make_map_with_root_and_bitwidth::<_, BigIntDe>( @@ -234,7 +234,7 @@ fn remove_datacap_simple_successful_path() { let remove_datacap_ret: RemoveDataCapReturn = apply_ok( &v, TEST_VERIFREG_ROOT_ADDR, - *VERIFIED_REGISTRY_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, TokenAmount::zero(), VerifregMethod::RemoveVerifiedClientDataCap as u64, remove_datacap_params, @@ -243,7 +243,7 @@ fn remove_datacap_simple_successful_path() { .unwrap(); ExpectInvocation { - to: *VERIFIED_REGISTRY_ACTOR_ADDR, + to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::RemoveVerifiedClientDataCap as u64, params: Some(remove_datacap_params_ser), subinvocs: Some(vec![]), @@ -256,7 +256,7 @@ fn remove_datacap_simple_successful_path() { // confirm client has been removed entirely - v_st = v.get_state::(*VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); + v_st = v.get_state::(VERIFIED_REGISTRY_ACTOR_ADDR).unwrap(); verified_clients = make_map_with_root_and_bitwidth::<_, BigIntDe>( &v_st.verified_clients, &store, From 3faab342e4ce7df75e1dedd6e50929c9fd106448 Mon Sep 17 00:00:00 2001 From: Swift Liu <74490266+lyswifter@users.noreply.github.com> Date: Fri, 16 Sep 2022 17:04:45 +0800 Subject: [PATCH 060/339] Replace actors cids to builtin.types in mockruntime (#629) Co-authored-by: lyswifter --- actors/market/tests/activate_deal_failures.rs | 9 +++--- .../market/tests/compute_data_commitment.rs | 15 +++++---- .../market/tests/cron_tick_timedout_deals.rs | 6 ++-- actors/market/tests/harness.rs | 22 ++++++------- actors/market/tests/market_actor_test.rs | 32 +++++++++---------- .../tests/on_miner_sectors_terminate.rs | 3 +- .../tests/publish_storage_deals_failures.rs | 15 +++++---- .../tests/verify_deals_for_activation_test.rs | 13 ++++---- actors/miner/tests/miner_actor_test_wpost.rs | 7 ++-- actors/miner/tests/util.rs | 7 ++-- actors/multisig/src/lib.rs | 10 +++--- actors/multisig/tests/multisig_actor_test.rs | 6 ++-- actors/multisig/tests/util.rs | 8 ++--- actors/paych/tests/paych_actor_test.rs | 12 ++++--- actors/power/src/lib.rs | 4 +-- actors/power/tests/harness/mod.rs | 15 +++++---- actors/power/tests/power_actor_tests.rs | 19 +++++------ runtime/src/test_utils.rs | 18 +++-------- 18 files changed, 113 insertions(+), 108 deletions(-) diff --git a/actors/market/tests/activate_deal_failures.rs b/actors/market/tests/activate_deal_failures.rs index c3e59a779..6ab720455 100644 --- a/actors/market/tests/activate_deal_failures.rs +++ b/actors/market/tests/activate_deal_failures.rs @@ -3,6 +3,7 @@ use fil_actor_market::{ActivateDealsParams, Actor as MarketActor, Method}; use fil_actors_runtime::network::EPOCHS_IN_DAY; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -25,7 +26,7 @@ fn fail_when_caller_is_not_the_provider_of_the_deal() { let params = ActivateDealsParams { deal_ids: vec![deal_id], sector_expiry }; - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); expect_abort( ExitCode::USR_FORBIDDEN, @@ -39,7 +40,7 @@ fn fail_when_caller_is_not_the_provider_of_the_deal() { #[test] fn fail_when_caller_is_not_a_storage_miner_actor() { let mut rt = setup(); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, PROVIDER_ADDR); let params = ActivateDealsParams { deal_ids: vec![], sector_expiry: 0 }; @@ -57,7 +58,7 @@ fn fail_when_deal_has_not_been_published_before() { let mut rt = setup(); let params = ActivateDealsParams { deal_ids: vec![DealID::from(42u32)], sector_expiry: 0 }; - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); expect_abort( ExitCode::USR_NOT_FOUND, @@ -84,7 +85,7 @@ fn fail_when_deal_has_already_been_activated() { ); activate_deals(&mut rt, sector_expiry, PROVIDER_ADDR, 0, &[deal_id]); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); let params = ActivateDealsParams { deal_ids: vec![deal_id], sector_expiry }; expect_abort( diff --git a/actors/market/tests/compute_data_commitment.rs b/actors/market/tests/compute_data_commitment.rs index a310b6c9c..6b431a9b5 100644 --- a/actors/market/tests/compute_data_commitment.rs +++ b/actors/market/tests/compute_data_commitment.rs @@ -6,6 +6,7 @@ use fil_actor_market::{ SectorDataSpec, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::error::ExitCode; @@ -64,7 +65,7 @@ mod compute_data_commitment { ExitCode::OK, ); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let ret: ComputeDataCommitmentReturn = rt .call::( @@ -97,7 +98,7 @@ mod compute_data_commitment { ExitCode::OK, ); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let ret: ComputeDataCommitmentReturn = rt .call::( @@ -171,7 +172,7 @@ mod compute_data_commitment { ExitCode::OK, ); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let ret: ComputeDataCommitmentReturn = rt .call::( @@ -199,7 +200,7 @@ mod compute_data_commitment { }; let param = ComputeDataCommitmentParams { inputs: vec![input] }; rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -239,7 +240,7 @@ mod compute_data_commitment { ExitCode::USR_ILLEGAL_ARGUMENT, ); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -285,7 +286,7 @@ mod compute_data_commitment { ExitCode::OK, ); // first sector is computed rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -336,7 +337,7 @@ mod compute_data_commitment { ExitCode::USR_ILLEGAL_ARGUMENT, ); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index 94c4c74bd..71b7d348b 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -7,7 +7,9 @@ use fil_actor_market::{ }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR}; +use fil_actors_runtime::{ + BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, VERIFIED_REGISTRY_ACTOR_ADDR, +}; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; @@ -83,7 +85,7 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l let client_deal_proposal = ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig.clone() }; let params = PublishStorageDealsParams { deals: vec![client_deal_proposal] }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index c63c0cba7..d9aad32cb 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -19,10 +19,10 @@ use fil_actor_reward::Method as RewardMethod; use fil_actor_verifreg::UseBytesParams; use fil_actors_runtime::{ network::EPOCHS_IN_DAY, - runtime::{Policy, Runtime}, + runtime::{builtins::Type, Policy, Runtime}, test_utils::*, - ActorError, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, REWARD_ACTOR_ADDR, - STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + ActorError, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, + REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_encoding::{to_vec, RawBytes}; @@ -156,7 +156,7 @@ pub fn add_provider_funds(rt: &mut MockRuntime, amount: TokenAmount, addrs: &Min rt.set_value(amount.clone()); rt.set_address_actor_type(addrs.provider, *MINER_ACTOR_CODE_ID); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.owner); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(rt, addrs.provider, addrs.owner, addrs.worker); @@ -177,7 +177,7 @@ pub fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenA rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addr); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(addr).unwrap()) @@ -276,7 +276,7 @@ pub fn activate_deals_raw( deal_ids: &[DealID], ) -> Result { rt.set_caller(*MINER_ACTOR_CODE_ID, provider); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = ActivateDealsParams { deal_ids: deal_ids.to_vec(), sector_expiry }; @@ -434,7 +434,7 @@ pub fn publish_deals( addrs: &MinerAddresses, publish_deals: &[DealProposal], ) -> Vec { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let return_value = ext::miner::GetControlAddressesReturnParams { owner: addrs.owner, @@ -516,7 +516,7 @@ pub fn publish_deals_expect_abort( proposal: DealProposal, expected_exit_code: ExitCode, ) { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address( rt, miner_addresses.provider, @@ -695,7 +695,7 @@ pub fn assert_deal_failure( rt.set_epoch(current_epoch); post_setup(&mut rt, &mut deal_proposal); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -903,7 +903,7 @@ pub fn terminate_deals_raw( deal_ids: &[DealID], ) -> Result { rt.set_caller(*MINER_ACTOR_CODE_ID, miner_addr); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = OnMinerSectorsTerminateParams { epoch: rt.epoch, deal_ids: deal_ids.to_vec() }; @@ -924,7 +924,7 @@ pub fn verify_deals_for_activation( sector_deals: Vec, ) -> VerifyDealsForActivationReturn { let param = VerifyDealsForActivationParams { sectors: sector_deals }; - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, provider); let ret: VerifyDealsForActivationReturn = rt diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 1ff23fb1a..8a8d133ba 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -11,11 +11,11 @@ use fil_actor_market::{ use fil_actor_verifreg::UseBytesParams; use fil_actors_runtime::cbor::deserialize; use fil_actors_runtime::network::EPOCHS_IN_DAY; -use fil_actors_runtime::runtime::{Policy, Runtime}; +use fil_actors_runtime::runtime::{builtins::Type, Policy, Runtime}; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ - make_empty_map, ActorError, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, - VERIFIED_REGISTRY_ACTOR_ADDR, + make_empty_map, ActorError, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, + SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_amt::Amt; use fvm_ipld_encoding::{to_vec, RawBytes}; @@ -192,7 +192,7 @@ fn adds_to_provider_escrow_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -369,7 +369,7 @@ fn fails_unless_called_by_an_account_actor() { let mut rt = setup(); rt.set_value(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); assert_eq!( @@ -404,7 +404,7 @@ fn adds_to_non_provider_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); assert_eq!( RawBytes::default(), @@ -748,7 +748,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // add funds for provider using it's BLS address -> will be resolved and persisted rt.value_received = deal.provider_collateral.clone(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -765,7 +765,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // publish deal using the BLS addresses rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1282,7 +1282,7 @@ fn cannot_publish_the_same_deal_twice_before_a_cron_tick() { let params = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal: d2.clone(), client_signature: sig.clone() }], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -1318,7 +1318,7 @@ fn fail_when_current_epoch_greater_than_start_epoch_of_deal() { end_epoch, ); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); rt.set_epoch(start_epoch + 1); let params = ActivateDealsParams { deal_ids: vec![deal_id], sector_expiry }; @@ -1345,7 +1345,7 @@ fn fail_when_end_epoch_of_deal_greater_than_sector_expiry() { end_epoch, ); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); let params = ActivateDealsParams { deal_ids: vec![deal_id], sector_expiry: end_epoch - 1 }; expect_abort( @@ -1382,7 +1382,7 @@ fn fail_to_activate_all_deals_if_one_deal_fails() { end_epoch + 1, ); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); let params = ActivateDealsParams { deal_ids: vec![deal_id1, deal_id2], sector_expiry }; expect_abort( @@ -1640,7 +1640,7 @@ fn insufficient_client_balance_in_a_batch() { deal1.provider_balance_requirement().add(deal2.provider_balance_requirement()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(provider_funds); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1671,7 +1671,7 @@ fn insufficient_client_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.expect_verify_signature(ExpectedVerifySig { @@ -1736,7 +1736,7 @@ fn insufficient_provider_balance_in_a_batch() { // Provider has enough for only the second deal rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(deal2.provider_balance_requirement().clone()); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1770,7 +1770,7 @@ fn insufficient_provider_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.expect_verify_signature(ExpectedVerifySig { diff --git a/actors/market/tests/on_miner_sectors_terminate.rs b/actors/market/tests/on_miner_sectors_terminate.rs index a150711ea..030a36f0a 100644 --- a/actors/market/tests/on_miner_sectors_terminate.rs +++ b/actors/market/tests/on_miner_sectors_terminate.rs @@ -5,6 +5,7 @@ use std::convert::TryInto; use fil_actor_market::{Actor as MarketActor, Method, OnMinerSectorsTerminateParams}; use fil_actors_runtime::network::EPOCHS_IN_DAY; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; @@ -298,7 +299,7 @@ fn do_not_terminate_deal_if_end_epoch_is_equal_to_or_less_than_current_epoch() { #[test] fn fail_when_caller_is_not_a_storage_miner_actor() { let mut rt = setup(); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, PROVIDER_ADDR); let params = OnMinerSectorsTerminateParams { epoch: rt.epoch, deal_ids: vec![] }; diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index c67f09bd2..da449ac74 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -9,6 +9,7 @@ use fil_actor_market::{ use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; @@ -252,7 +253,7 @@ fn fail_when_provider_has_some_funds_but_not_enough_for_a_deal() { deals: vec![ClientDealProposal { proposal: deal1.clone(), client_signature: sig.clone() }], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -301,7 +302,7 @@ fn fail_when_deals_have_different_providers() { ], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -347,7 +348,7 @@ fn fail_when_caller_is_not_of_signable_type() { }; let w = Address::new_id(1000); rt.set_caller(*MINER_ACTOR_CODE_ID, w); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_abort( ExitCode::USR_FORBIDDEN, rt.call::( @@ -363,7 +364,7 @@ fn fail_when_no_deals_in_params() { let mut rt = setup(); let params = PublishStorageDealsParams { deals: vec![] }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -388,7 +389,7 @@ fn fail_to_resolve_provider_address() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -411,7 +412,7 @@ fn caller_is_not_the_same_as_the_worker_address_for_miner() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(999)); expect_abort( @@ -440,7 +441,7 @@ fn fails_if_provider_is_not_a_storage_miner_actor() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, diff --git a/actors/market/tests/verify_deals_for_activation_test.rs b/actors/market/tests/verify_deals_for_activation_test.rs index 96970a1ba..fce15edbe 100644 --- a/actors/market/tests/verify_deals_for_activation_test.rs +++ b/actors/market/tests/verify_deals_for_activation_test.rs @@ -4,6 +4,7 @@ mod harness; use fil_actor_market::policy::detail::deal_weight; use fil_actor_market::{Actor as MarketActor, Method, SectorDeals, VerifyDealsForActivationParams}; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::test_utils::{ expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, }; @@ -126,7 +127,7 @@ fn fail_when_caller_is_not_a_storage_miner_actor() { generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = VerifyDealsForActivationParams { sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }], @@ -151,7 +152,7 @@ fn fail_when_deal_proposal_is_not_found() { sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![1] }], }; rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -171,7 +172,7 @@ fn fail_when_caller_is_not_the_provider() { generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH); rt.set_caller(*MINER_ACTOR_CODE_ID, Address::new_id(205)); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = VerifyDealsForActivationParams { sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }], @@ -196,7 +197,7 @@ fn fail_when_current_epoch_is_greater_than_proposal_start_epoch() { rt.set_epoch(START_EPOCH + 1); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = VerifyDealsForActivationParams { sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }], @@ -220,7 +221,7 @@ fn fail_when_deal_end_epoch_is_greater_than_sector_expiration() { generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = VerifyDealsForActivationParams { sectors: vec![SectorDeals { sector_expiry: END_EPOCH - 1, deal_ids: vec![deal_id] }], @@ -244,7 +245,7 @@ fn fail_when_the_same_deal_id_is_passed_multiple_times() { generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH); rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = VerifyDealsForActivationParams { sectors: vec![SectorDeals { diff --git a/actors/miner/tests/miner_actor_test_wpost.rs b/actors/miner/tests/miner_actor_test_wpost.rs index f6047a844..2786c0720 100644 --- a/actors/miner/tests/miner_actor_test_wpost.rs +++ b/actors/miner/tests/miner_actor_test_wpost.rs @@ -4,6 +4,7 @@ use fil_actor_miner as miner; use fil_actor_miner::PowerPair; use fil_actors_runtime::runtime::DomainSeparationTag; use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_bitfield::BitField; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; @@ -1027,7 +1028,7 @@ fn cannot_dispute_posts_when_the_challenge_window_is_open() { let params = miner::DisputeWindowedPoStParams { deadline: dlinfo.index, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); h.expect_query_network_info(&mut rt); let result = rt.call::( @@ -1088,7 +1089,7 @@ fn can_dispute_up_till_window_end_but_not_after() { // Now try to dispute. let params = miner::DisputeWindowedPoStParams { deadline: dlidx, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); h.expect_query_network_info(&mut rt); @@ -1121,7 +1122,7 @@ fn cant_dispute_up_with_an_invalid_deadline() { let params = miner::DisputeWindowedPoStParams { deadline: 50, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let result = rt.call::( miner::Method::DisputeWindowedPoSt as u64, diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 262462850..aa4fc3c7e 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -35,7 +35,8 @@ use fil_actors_runtime::runtime::{DomainSeparationTag, Policy, Runtime, RuntimeP use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ ActorDowncast, ActorError, Array, DealWeight, MessageAccumulator, BURNT_FUNDS_ACTOR_ADDR, - INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, }; use fvm_ipld_amt::Amt; use fvm_shared::bigint::Zero; @@ -1282,7 +1283,7 @@ impl ActorHarness { expect_success: Option, ) { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); self.expect_query_network_info(rt); @@ -1728,7 +1729,7 @@ impl ActorHarness { from: Address, fault: Option, ) -> Result<(), ActorError> { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).clone()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, from); let params = ReportConsensusFaultParams { header1: vec![], header2: vec![], header_extra: vec![] }; diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 869ff71ee..594197dec 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -4,10 +4,10 @@ use std::collections::BTreeSet; use fil_actors_runtime::cbor::serialize_vec; -use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; +use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorDowncast, - ActorError, Map, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, + ActorError, Map, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -134,7 +134,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; let proposer: Address = rt.message().caller(); if params.value.is_negative() { @@ -196,7 +196,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; let approver: Address = rt.message().caller(); let id = params.id; @@ -236,7 +236,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; let caller_addr: Address = rt.message().caller(); rt.transaction(|st: &mut State, rt| { diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index f14750f68..462fce168 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -6,7 +6,7 @@ use fil_actor_multisig::{ use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; @@ -1550,7 +1550,7 @@ mod approval_tests { RawBytes::default(), ExitCode::OK, ); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let params = TxnIDParams { id: TxnID(0), proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .unwrap(); @@ -1612,7 +1612,7 @@ mod approval_tests { h.construct_and_verify(&mut rt, 1, 0, 0, signers); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, bob); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let params = TxnIDParams { id: dne_tx_id, proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .expect_err("should fail on approve of non existent tx id"); diff --git a/actors/multisig/tests/util.rs b/actors/multisig/tests/util.rs index 1ff3bba74..fdb96a94f 100644 --- a/actors/multisig/tests/util.rs +++ b/actors/multisig/tests/util.rs @@ -5,8 +5,8 @@ use fil_actor_multisig::{ }; use fil_actor_multisig::{ChangeNumApprovalsThresholdParams, LockBalanceParams}; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::INIT_ACTOR_ADDR; use fil_actors_runtime::{make_map_with_root, ActorError}; +use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; @@ -125,7 +125,7 @@ impl ActorHarness { method: MethodNum, params: RawBytes, ) -> Result { - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let propose_params = ProposeParams { to, value, method, params }; let ret = rt.call::(Method::Propose as u64, &RawBytes::serialize(propose_params).unwrap()); @@ -139,7 +139,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let approve_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = @@ -154,7 +154,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let cancel_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index f306915c9..ffd633013 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -12,6 +12,7 @@ use fil_actor_paych::{ Actor as PaychActor, ConstructorParams, LaneState, Merge, Method, ModVerifyParams, SignedVoucher, State as PState, UpdateChannelStateParams, MAX_LANE, SETTLE_DELAY, }; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::INIT_ACTOR_ADDR; @@ -67,6 +68,7 @@ fn check_state(rt: &MockRuntime) { } mod paych_constructor { + use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::METHOD_CONSTRUCTOR; use fvm_shared::METHOD_SEND; use num_traits::Zero; @@ -106,7 +108,7 @@ mod paych_constructor { #[test] fn actor_doesnt_exist_test() { let mut rt = construct_runtime(); - rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), from: Address::new_id(TEST_PAYER_ADDR), @@ -185,7 +187,7 @@ mod paych_constructor { ..Default::default() }; - rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: test_case.to_addr, from: test_case.from_addr }; expect_abort( &mut rt, @@ -224,7 +226,7 @@ mod paych_constructor { ExitCode::OK, ); - rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: non_id_addr, to: to_addr }; expect_abort( &mut rt, @@ -261,7 +263,7 @@ mod paych_constructor { ExitCode::OK, ); - rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: from_addr, to: non_id_addr }; expect_abort( &mut rt, @@ -1199,7 +1201,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche fn construct_and_verify(rt: &mut MockRuntime, sender: Address, receiver: Address) { let params = ConstructorParams { from: sender, to: receiver }; - rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Init]); call(rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap()); rt.verify(); let sender_id = rt.id_addresses.get(&sender).unwrap_or(&sender); diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 4b6c4b5b2..93f022613 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -10,7 +10,7 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_map_with_root_and_bitwidth, ActorDowncast, ActorError, Multimap, - CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -92,7 +92,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; let value = rt.message().value_received(); let constructor_params = RawBytes::serialize(ext::miner::MinerConstructorParams { diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 5a6ad643d..c168403bc 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -13,6 +13,7 @@ use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; +use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fil_actors_runtime::CRON_ACTOR_ADDR; use fil_actors_runtime::REWARD_ACTOR_ADDR; use fvm_ipld_blockstore::Blockstore; @@ -44,10 +45,10 @@ use fil_actor_power::{ UpdateClaimedPowerParams, }; use fil_actors_runtime::builtin::HAMT_BIT_WIDTH; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::{ - MockRuntime, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, - SYSTEM_ACTOR_CODE_ID, + MockRuntime, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{ make_map_with_root_and_bitwidth, ActorError, Map, INIT_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, @@ -140,7 +141,7 @@ impl Harness { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *owner); rt.set_value(value.clone()); rt.set_balance(value.clone()); - rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let miner_ctor_params = MinerConstructorParams { owner: *owner, @@ -245,7 +246,7 @@ impl Harness { payload: &RawBytes, ) -> Result<(), ActorError> { rt.set_caller(*MINER_ACTOR_CODE_ID, miner_address.to_owned()); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = RawBytes::serialize(EnrollCronEventParams { event_epoch: epoch, payload: payload.clone(), @@ -287,7 +288,7 @@ impl Harness { let prev = st.total_pledge_collateral; rt.set_caller(*MINER_ACTOR_CODE_ID, miner); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.call::( Method::UpdatePledgeTotal as MethodNum, &RawBytes::serialize(delta).unwrap(), @@ -324,7 +325,7 @@ impl Harness { quality_adjusted_delta: qa_delta.clone(), }; rt.set_caller(*MINER_ACTOR_CODE_ID, miner); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.call::( Method::UpdateClaimedPower as MethodNum, &RawBytes::serialize(params).unwrap(), @@ -448,7 +449,7 @@ impl Harness { seal_info: SealVerifyInfo, ) -> Result<(), ActorError> { rt.expect_gas_charge(GAS_ON_SUBMIT_VERIFY_SEAL); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, miner_address); rt.call::( Method::SubmitPoRepForBulkVerify as u64, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index c090725bc..5be4416bd 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -1,10 +1,11 @@ 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, ACCOUNT_ACTOR_CODE_ID, CALLER_TYPES_SIGNABLE, - MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, + SYSTEM_ACTOR_CODE_ID, }; -use fil_actors_runtime::{runtime::Policy, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{runtime::Policy, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, RawBytes}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntSer; @@ -90,7 +91,7 @@ fn create_miner_given_caller_is_not_of_signable_type_should_fail() { }; rt.set_caller(*MINER_ACTOR_CODE_ID, *OWNER); - rt.expect_validate_caller_type(CALLER_TYPES_SIGNABLE.to_vec()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_abort( ExitCode::USR_FORBIDDEN, rt.call::( @@ -121,7 +122,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *OWNER); rt.value_received = TokenAmount::from_atto(10); rt.set_balance(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type(CALLER_TYPES_SIGNABLE.to_vec()); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let message_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, @@ -166,7 +167,7 @@ fn claimed_power_given_caller_is_not_storage_miner_should_fail() { }; rt.set_caller(*SYSTEM_ACTOR_CODE_ID, *MINER); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_FORBIDDEN, @@ -190,7 +191,7 @@ fn claimed_power_given_claim_does_not_exist_should_fail() { }; rt.set_caller(*MINER_ACTOR_CODE_ID, *MINER); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort( ExitCode::USR_NOT_FOUND, @@ -447,7 +448,7 @@ fn enroll_cron_epoch_given_negative_epoch_should_fail() { let (h, mut rt) = setup(); rt.set_caller(*MINER_ACTOR_CODE_ID, *MINER); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); let params = EnrollCronEventParams { event_epoch: -1, @@ -598,7 +599,7 @@ fn given_no_miner_claim_update_pledge_total_should_abort() { h.delete_claim(&mut rt, &*MINER); rt.set_caller(*MINER_ACTOR_CODE_ID, *MINER); - rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]); + rt.expect_validate_caller_type(vec![Type::Miner]); expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "unknown miner", diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index bffd3fd15..ad8ff80ac 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -89,8 +89,6 @@ lazy_static::lazy_static! { ] .into_iter() .collect(); - pub static ref CALLER_TYPES_SIGNABLE: Vec = - vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]; pub static ref NON_SINGLETON_CODES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*ACCOUNT_ACTOR_CODE_ID, ()); @@ -153,7 +151,7 @@ pub struct MockRuntime { pub struct Expectations { pub expect_validate_caller_any: bool, pub expect_validate_caller_addr: Option>, - pub expect_validate_caller_type: Option>, + pub expect_validate_caller_type: Option>, pub expect_sends: VecDeque, pub expect_create_actor: Option, pub expect_delete_actor: Option
, @@ -531,7 +529,7 @@ impl MockRuntime { } #[allow(dead_code)] - pub fn expect_validate_caller_type(&mut self, types: Vec) { + pub fn expect_validate_caller_type(&mut self, types: Vec) { assert!(!types.is_empty(), "addrs must be non-empty"); self.expectations.borrow_mut().expect_validate_caller_type = Some(types); } @@ -757,14 +755,7 @@ impl Runtime> for MockRuntime { "unexpected validate caller code" ); - let find_by_type = |typ| { - (*ACTOR_TYPES) - .iter() - .find_map(|(cid, t)| if t == typ { Some(cid) } else { None }) - .cloned() - .unwrap() - }; - let types: Vec = types.into_iter().map(find_by_type).collect(); + let types: Vec = types.into_iter().copied().collect(); let expected_caller_type = self.expectations.borrow_mut().expect_validate_caller_type.clone().unwrap(); assert_eq!( @@ -773,8 +764,9 @@ impl Runtime> for MockRuntime { types, expected_caller_type, ); + let call_type = self.resolve_builtin_actor_type(&self.caller_type).unwrap(); for expected in &types { - if &self.caller_type == expected { + if &call_type == expected { self.expectations.borrow_mut().expect_validate_caller_type = None; return Ok(()); } From 0a4dfe1e3ff5d4bb37f5eea0961f8835913b55d9 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 16 Sep 2022 17:58:59 -0700 Subject: [PATCH 061/339] EVM: Fix Precompile Failure Modes & Implement Remaining Precompile Tests (#654) * bn pair tests and errors * add ec_recover test, fix broken implementation * add modexp test, add more test cases for ec_recover * only check lower bound for ec_recover * Fix broken failure modes in precompiles - ec_recover - modexp - ec_add - ec_mul Change error enum to include field error types + conversions - forked bn to derive Eq on bn Errors Add helper for reading padded bytes from input. * rustfmt & clippy * Update precompiles.rs * use cow instead of custom container * remove fork of bn * remove bn for dep * allocate ahead of time when reading sets of * lazy_static secp256k1 lower bound * left pad ecrecover * hex literal and cargo lock * add padding test * space * dont panic on modexp len * few remaining nits * rustfmt * better read logic of bigints for modexp * add notes to reading bigint lengths & refactor read group for more clarity * rustfmt --- Cargo.lock | 97 ++-- actors/evm/Cargo.toml | 3 +- actors/evm/src/interpreter/precompiles.rs | 599 ++++++++++++++-------- actors/evm/src/interpreter/uints.rs | 8 + 4 files changed, 448 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cb5bacf2..2146d11ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arrayref" @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.20" +version = "3.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" +checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" dependencies = [ "atty", "bitflags", @@ -695,6 +695,7 @@ dependencies = [ "hex", "hex-literal", "impl-serde", + "lazy_static", "log", "multihash", "near-blake2", @@ -1117,9 +1118,9 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1587207c7455ea70a762d4360f502b6728ea6e2dfbec2b66b6d4d943ee29ae" +checksum = "97dfb38431e301bf063a412f870a3d9ebb541d581d1884f95e921f5ea823d29d" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1350,9 +1351,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" dependencies = [ "either", ] @@ -1365,9 +1366,9 @@ checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -1653,9 +1654,9 @@ checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "pest" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4" +checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048" dependencies = [ "thiserror", "ucd-trie", @@ -1663,9 +1664,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91" +checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1" dependencies = [ "pest", "pest_generator", @@ -1673,9 +1674,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad" +checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c" dependencies = [ "pest", "pest_meta", @@ -1686,13 +1687,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04" +checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6" dependencies = [ "once_cell", "pest", - "sha-1", + "sha1", ] [[package]] @@ -1803,9 +1804,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -1881,9 +1882,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "serde" @@ -1979,10 +1980,10 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.10.0" +name = "sha1" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" dependencies = [ "cfg-if", "cpufeatures", @@ -2201,18 +2202,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" +checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" +checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ "proc-macro2", "quote", @@ -2254,15 +2255,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsigned-varint" @@ -2300,9 +2301,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2310,9 +2311,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", @@ -2325,9 +2326,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -2337,9 +2338,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2347,9 +2348,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -2360,15 +2361,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index f5c6ed1cc..f56448343 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -38,12 +38,13 @@ fixed-hash = { version = "0.7.0", default-features = false } impl-serde = { version = "0.3.2", default-features = false } arrayvec = { version = "0.7.2", features = ["serde"] } hex = "0.4.3" +hex-literal = "0.3.4" substrate-bn = { version = "0.6.0", default-features = false } near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } +lazy_static = "1.4.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } -hex-literal = "0.3.4" etk-asm = "^0.2.1" [features] diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 7bf55a9c0..f6d3fd19e 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, ops::Mul}; +use std::{borrow::Cow, convert::TryInto, marker::PhantomData}; use super::U256; use fil_actors_runtime::runtime::{Primitives, Runtime}; @@ -7,17 +7,43 @@ use fvm_shared::{ bigint::BigUint, crypto::{ hash::SupportedHashes, - signature::{SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, + 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 uint::byteorder::{ByteOrder, LE}; -#[derive(Debug, Eq, PartialEq)] +pub use substrate_bn::{CurveError, FieldError, GroupError}; + +lazy_static::lazy_static! { + pub(crate) static ref SECP256K1: BigUint = BigUint::from_bytes_be(&hex_literal::hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); +} + +#[derive(Debug)] pub enum PrecompileError { - EcErr, + EcErr(CurveError), + EcGroupErr(GroupError), IncorrectInputSize, + OutOfGas, +} + +impl From for PrecompileError { + fn from(src: CurveError) -> Self { + PrecompileError::EcErr(src) + } +} + +impl From for PrecompileError { + fn from(src: FieldError) -> Self { + PrecompileError::EcErr(src.into()) + } +} + +impl From for PrecompileError { + fn from(src: GroupError) -> Self { + PrecompileError::EcGroupErr(src) + } } pub type PrecompileFn = fn(&RT, &[u8]) -> PrecompileResult; @@ -57,79 +83,127 @@ impl> Precompiles { } } -/// read 32 bytes (u256) from buffer or error -fn read_u256(buf: &[u8], start: usize) -> Result { - let slice = buf.get(start..start + 32).ok_or(PrecompileError::IncorrectInputSize)?; - Ok(U256::from_big_endian(slice)) -} - -/// read 32 bytes (u256) from buffer, pass in exit reason that is desired -/// returns 0 if failed to read -fn read_u256_infalliable(buf: &[u8], start: usize) -> U256 { - let slice = buf.get(start..start + 32).unwrap_or(&[0u8; 32]); - U256::from_big_endian(slice) +// It is uncomfortable how much Eth pads everything... +/// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/common/bytes.go#L108 +fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { + let mut input: Cow<[u8]> = input.into(); + let input_len = input.len(); + if len > input_len { + input.to_mut().resize(len, 0); + } + input } +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L165 +/// recover a secp256k1 pubkey from a hash, recovery byte, and a signature fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { - let mut buf = [0u8; 128]; - buf[..input.len().min(128)].copy_from_slice(&input[..input.len().min(128)]); + let input = read_right_pad(input, 128); - let mut hash = [0u8; SECP_SIG_MESSAGE_HASH_SIZE]; - let mut sig = [0u8; SECP_SIG_LEN]; + let hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE] = input[0..32].try_into().unwrap(); + let r = BigUint::from_bytes_be(&input[64..96]); + let s = BigUint::from_bytes_be(&input[96..128]); + let recovery_byte = input[63]; - hash.copy_from_slice(&input[..32]); - sig.copy_from_slice(&input[64..]); // TODO this assumes input is exactly 65 bytes which would panic if incorrect + // recovery byte is a single byte value but is represented with 32 bytes, sad + let v = if input[32..63] == [0u8; 31] && matches!(recovery_byte, 27 | 28) { + recovery_byte - 27 + } else { + return Ok(Vec::new()); + }; + + let valid = if r <= BigUint::one() || s <= BigUint::one() { + false + } else { + r <= *SECP256K1 && s <= *SECP256K1 && (v == 0 || v == 1) + }; - // recovery byte means a single byte value is 32 bytes long, sad - if input[32..63] != [0u8; 31] || !matches!(input[63], 23 | 28) { + if !valid { return Ok(Vec::new()); } - sig[64] = input[63] - 27; - let recovered = rt.recover_secp_public_key(&hash, &sig).unwrap_or([0u8; SECP_PUB_LEN]); + let mut sig: [u8; SECP_SIG_LEN] = [0u8; 65]; + sig[..64].copy_from_slice(&input[64..128]); + sig[64] = v; + + let pubkey = if let Ok(key) = rt.recover_secp_public_key(hash, &sig) { + key + } else { + return Ok(Vec::new()); + }; + + let mut address = rt.hash(SupportedHashes::Keccak256, &pubkey[1..]); + address[..12].copy_from_slice(&[0u8; 12]); - Ok(recovered.to_vec()) + Ok(address) } +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L206 +/// hash with sha2-256 fn sha256(rt: &RT, input: &[u8]) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Sha2_256, input)) } +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L221 +/// hash with ripemd160 fn ripemd160(rt: &RT, input: &[u8]) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Ripemd160, input)) } +/// data copy fn identity(_: &RT, input: &[u8]) -> PrecompileResult { Ok(Vec::from(input)) } +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L363 // https://eips.ethereum.org/EIPS/eip-198 +/// modulus exponent a number fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { - let base_len = read_u256(input, 0)?.as_usize(); - let exponent_len = read_u256(input, 32)?.as_usize(); - let mod_len = read_u256(input, 64)?.as_usize(); + let input = read_right_pad(input, 96); + + // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. + // We also need to try converting into u32 since we are running in WASM, and since we don't have any complexity + // functions or specific gas measurements of modexp in FEVM, we let values be whatever and have FEVM gas accounting + // be the one responsible for keeping things within reasonable limits. + // We _also_ will default with 0 (though this is already done with right padding above) since that is expected to be fine. + // Eth really relies heavily on gas checking being correct and safe for nodes... + fn read_bigint_len(input: &[u8], size: usize) -> Result { + let digits = BigUint::from_bytes_be(&input[size..size + 32]); + let mut digits = digits.iter_u64_digits(); + // truncate to 64 bits + digits + .next() + .or(Some(0)) + // wont ever error here, just a type conversion + .ok_or(PrecompileError::OutOfGas) + .and_then(|d| u32::try_from(d).map_err(|_| PrecompileError::OutOfGas)) + .map(|d| d as usize) + } - if mod_len == 0 { + let base_len = read_bigint_len(&input, 0)?; + let exponent_len = read_bigint_len(&input, 32)?; + let mod_len = read_bigint_len(&input, 64)?; + + if base_len == 0 && mod_len == 0 { return Ok(Vec::new()); } + let input = if input.len() > 96 { &input[96..] } else { &[] }; + let input = read_right_pad(input, base_len + exponent_len + mod_len); - // TODO bounds checking - let mut start = 96; - let base = BigUint::from_bytes_be(&input[start..start + base_len]); - start += base_len; - let exponent = BigUint::from_bytes_be(&input[start..start + exponent_len]); - start += exponent_len; - let modulus = BigUint::from_bytes_be(&input[start..start + mod_len]); + let base = BigUint::from_bytes_be(&input[0..base_len]); + let exponent = BigUint::from_bytes_be(&input[base_len..exponent_len + base_len]); + let modulus = + BigUint::from_bytes_be(&input[base_len + exponent_len..mod_len + base_len + exponent_len]); - let mut output = if modulus.is_zero() || modulus.is_one() { - BigUint::zero().to_bytes_be() - } else { - base.modpow(&exponent, &modulus).to_bytes_be() - }; + 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.extend(core::iter::repeat(0).take(mod_len - output.len())); + ret.resize(mod_len - output.len(), 0); // left padding ret.extend_from_slice(&output); output = ret; } @@ -137,94 +211,88 @@ fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { Ok(output) } -/// converts 2 byte arrays (U256) into a point on a field -/// exits with OutOfGas for any failed operation -fn uint_to_point(x: U256, y: U256) -> Result { - let x = Fq::from_u256(x.0.into()).map_err(|_| PrecompileError::EcErr)?; - let y = Fq::from_u256(y.0.into()).map_err(|_| PrecompileError::EcErr)?; +/// reads a point from 2, 32 byte coordinates with right padding +/// exits with EcErr for any failed operation +/// panics if input.len() < 64 +fn curve_point(input: &[u8]) -> Result { + let x = Fq::from_u256(U256::from_big_endian(&input[0..32]).into())?; + let y = Fq::from_u256(U256::from_big_endian(&input[32..64]).into())?; - Ok(if x.is_zero() && y.is_zero() { - G1::zero() - } else { - AffineG1::new(x, y).map_err(|_| PrecompileError::EcErr)?.into() - }) + Ok(if x.is_zero() && y.is_zero() { G1::zero() } else { AffineG1::new(x, y)?.into() }) } -/// add 2 points together on `alt_bn128` -fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { - let x1 = read_u256_infalliable(input, 0); - let y1 = read_u256_infalliable(input, 32); - let point1 = uint_to_point(x1, y1)?; - - let x2 = read_u256_infalliable(input, 64); - let y2 = read_u256_infalliable(input, 96); - let point2 = uint_to_point(x2, y2)?; +fn curve_to_vec(curve: G1) -> Vec { + AffineG1::from_jacobian(curve) + .map(|product| { + let mut output = vec![0; 64]; + product.x().to_big_endian(&mut output[0..32]).unwrap(); + product.y().to_big_endian(&mut output[32..64]).unwrap(); + output + }) + .unwrap_or_else(|| vec![0; 64]) +} - let output = AffineG1::from_jacobian(point1 + point2).map_or(vec![0; 64], |sum| { - let mut output = vec![0; 64]; - sum.x().to_big_endian(&mut output[..32]).unwrap(); - sum.y().to_big_endian(&mut output[32..]).unwrap(); - output - }); +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L413 +/// add 2 points together on an elliptic curve +fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { + let input = read_right_pad(input, 128); + let point1 = curve_point(&input)?; + let point2 = curve_point(&input[64..128])?; - Ok(output) + Ok(curve_to_vec(point1 + point2)) } -/// multiply a scalar and a point on `alt_bn128` +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L455 +/// multiply a point on an elliptic curve by a scalar value fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { - let x = read_u256_infalliable(input, 0); - let y = read_u256_infalliable(input, 32); - let point = uint_to_point(x, y)?; + let input = read_right_pad(input, 96); + let point = curve_point(&input)?; - let scalar = if let Some(scalar) = input.get(64..96) { - Fr::from_slice(scalar).map_err(|_| PrecompileError::EcErr)? - } else { - return Ok(vec![0; 64]); + let scalar = { + let data = U256::from_big_endian(&input[64..96]); + Fr::new_mul_factor(data.into()) }; - AffineG1::from_jacobian(point.mul(scalar)).ok_or(PrecompileError::EcErr).map(|product| { - let mut output = vec![0; 64]; - product.x().to_big_endian(&mut output[..32]).unwrap(); - product.y().to_big_endian(&mut output[32..]).unwrap(); - output - }) + Ok(curve_to_vec(point * scalar)) } +// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L504 +/// pairs multple groups of twisted bn curves fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { - let x1 = read_u256(input, 0)?; - let y1 = read_u256(input, 32)?; - - let y2 = read_u256(input, 64)?; - let x2 = read_u256(input, 96)?; - let y3 = read_u256(input, 128)?; - let x3 = read_u256(input, 160)?; - - // TODO errs - let ax = Fq::from_u256(x1.0.into()).unwrap(); - let ay = Fq::from_u256(y1.0.into()).unwrap(); + /// read 32 bytes (u256) from buffer or error + fn read_u256(input: &[u8], start: usize) -> Result { + let slice = input.get(start..start + 32).ok_or(PrecompileError::IncorrectInputSize)?; + Ok(U256::from_big_endian(slice)) + } - let twisted_ax = Fq::from_u256(x2.0.into()).unwrap(); - let twisted_ay = Fq::from_u256(y2.0.into()).unwrap(); - let twisted_bx = Fq::from_u256(x3.0.into()).unwrap(); - let twisted_by = Fq::from_u256(y3.0.into()).unwrap(); + let x = Fq::from_u256(read_u256(input, 0)?.into())?; + let y = Fq::from_u256(read_u256(input, 32)?.into())?; - let twisted_a = Fq2::new(twisted_ax, twisted_ay); - let twisted_b = Fq2::new(twisted_bx, twisted_by); + let twisted_x = { + let b = Fq::from_u256(read_u256(input, 64)?.into())?; + let a = Fq::from_u256(read_u256(input, 96)?.into())?; + Fq2::new(a, b) + }; + let twisted_y = { + let b = Fq::from_u256(read_u256(input, 128)?.into())?; + let a = Fq::from_u256(read_u256(input, 160)?.into())?; + Fq2::new(a, b) + }; let twisted = { - if twisted_a.is_zero() && twisted_b.is_zero() { + if twisted_x.is_zero() && twisted_y.is_zero() { G2::zero() } else { - AffineG2::new(twisted_a, twisted_b).unwrap().into() + AffineG2::new(twisted_x, twisted_y)?.into() } }; let a = { - if ax.is_zero() && ay.is_zero() { + if x.is_zero() && y.is_zero() { substrate_bn::G1::zero() } else { - AffineG1::new(ax, ay).unwrap().into() + AffineG1::new(x, y)?.into() } }; @@ -245,26 +313,26 @@ fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { let accumulated = pairing_batch(&groups); - let output = if accumulated == Gt::one() { U256::one() } else { U256::zero() }; - let mut buf = [0u8; 32]; - output.to_big_endian(&mut buf); - Ok(buf.to_vec()) + let paring_success = if accumulated == Gt::one() { U256::one() } else { U256::zero() }; + let mut ret = [0u8; 32]; + paring_success.to_big_endian(&mut ret); + Ok(ret.to_vec()) } -// https://eips.ethereum.org/EIPS/eip-152 +/// https://eips.ethereum.org/EIPS/eip-152 +/// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L581 fn blake2f(_: &RT, input: &[u8]) -> PrecompileResult { if input.len() != 213 { return Err(PrecompileError::IncorrectInputSize); } let mut hasher = near_blake2::VarBlake2b::default(); - let mut rounds = [0u8; 4]; - // 4 bytes let mut start = 0; + + // 4 bytes rounds.copy_from_slice(&input[..4]); start += 4; - // 64 bytes let h = &input[start..start + 64]; start += 64; @@ -310,6 +378,60 @@ mod tests { use fil_actors_runtime::test_utils::MockRuntime; use hex_literal::hex; + #[test] + fn padding() { + let input = b"foo bar boxy"; + let mut input = Vec::from(*input); + for i in 12..64 { + let mut expected = input.clone(); + expected.resize(i, 0); + + let res = read_right_pad(&input, i); + assert_eq!(&*res, &expected); + + input.push(0); + } + } + + #[test] + fn bn_recover() { + let rt = MockRuntime::default(); + + let input = &hex!( + "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) + "000000000000000000000000000000000000000000000000000000000000001c" // v (recovery byte) + // signature + "9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608" // r + "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s + ); + + let expected = hex!("0000000000000000000000007156526fbd7a3c72969b54f64e42c10fbb768c8a"); + let res = ec_recover(&rt, input).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(&rt, input).unwrap(); + assert_eq!(res, Vec::new()); + + let input = &hex!( + "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) + "000000000000000000000000000000000000000000000000000000000000000a" // v (recovery byte) + // signature + "0000005bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608" // r + "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s + ); + // invalid recovery byte + let res = ec_recover(&rt, input).unwrap(); + assert_eq!(res, Vec::new()); + } + #[test] fn sha256() { use super::sha256 as hash; @@ -334,13 +456,67 @@ mod tests { 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 rt = MockRuntime::default(); + + let expected = hex!("08"); + let res = modexp(&rt, input).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(&rt, input).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(&rt, input).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(&rt, input).unwrap(); + assert_eq!(res, Vec::new()); + } + // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { - use super::MockRuntime; - use crate::interpreter::precompiles::{ec_add, ec_mul, PrecompileError}; + use super::{GroupError, MockRuntime}; + use crate::interpreter::precompiles::{ec_add, ec_mul, ec_pairing, PrecompileError}; #[test] - fn bn128_add() { + fn bn_add() { let rt = MockRuntime::default(); let input = hex::decode( @@ -397,11 +573,11 @@ mod tests { ) .unwrap(); let res = ec_add(&rt, &input); - assert!(matches!(res, Err(PrecompileError::EcErr))); + assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } #[test] - fn bn128_mul() { + fn bn_mul() { let rt = MockRuntime::default(); let input = hex::decode( @@ -428,8 +604,8 @@ mod tests { 0200000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input); - assert_eq!(res, Err(PrecompileError::EcErr)); + let res = ec_mul(&rt, &input).unwrap(); + assert_eq!(&res, &vec![0; 64]); // no input test let input = [0u8; 0]; @@ -450,85 +626,88 @@ mod tests { ) .unwrap(); let res = ec_mul(&rt, &input); - assert!(matches!(res, Err(PrecompileError::EcErr))); + assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } - // #[test] - // fn bn128_pair() { - // 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 = - // Bn128Pair::::run(&input, 260_000, &new_context(), false).unwrap().output; - // 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 = Bn128Pair::::run(&input, 259_999, &new_context(), false); - // assert!(matches!(res, Err(Return::OutOfGas))); - // // no input test - // let input = [0u8; 0]; - // let expected = - // hex::decode("0000000000000000000000000000000000000000000000000000000000000001") - // .unwrap(); - // let res = - // Bn128Pair::::run(&input, 260_000, &new_context(), false).unwrap().output; - // assert_eq!(res, expected); - // // point not on curve fail - // let input = hex::decode( - // "\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111", - // ) - // .unwrap(); - // let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); - // assert!(matches!(res, Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_A"))))); - // // invalid input length - // let input = hex::decode( - // "\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 1111111111111111111111111111111111111111111111111111111111111111\ - // 111111111111111111111111111111\ - // ", - // ) - // .unwrap(); - // let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); - // assert!(matches!(res, Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_LEN",))))); - // } + #[test] + fn bn_pair() { + let rt = MockRuntime::default(); + + 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(&rt, &input).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(&rt, &input).unwrap(); + assert_eq!(res, expected); + // no input test + let input = [0u8; 0]; + let expected = + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + let res = ec_pairing(&rt, &input).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(&rt, &input); + assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); + // invalid input length + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111\ + ", + ) + .unwrap(); + let res = ec_pairing(&rt, &input); + assert!(matches!(res, Err(PrecompileError::IncorrectInputSize))); + } } // https://eips.ethereum.org/EIPS/eip-152#test-cases @@ -557,7 +736,7 @@ mod tests { // } // T0 invalid input len - assert_eq!(blake2f(&rt, &[]), Err(PrecompileError::IncorrectInputSize)); + assert!(matches!(blake2f(&rt, &[]), Err(PrecompileError::IncorrectInputSize))); // T1 too small let input = &hex!( @@ -568,7 +747,7 @@ mod tests { "0000000000000000" "02" ); - assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); // T2 too large let input = &hex!( @@ -579,7 +758,7 @@ mod tests { "0000000000000000" "02" ); - assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); // T3 final block indicator invalid let input = &hex!( @@ -590,12 +769,12 @@ mod tests { "0000000000000000" "02" ); - assert_eq!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize)); + assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); // outputs // T4 - let expected = &hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); let input = &hex!( "00000000" "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" @@ -604,7 +783,7 @@ mod tests { "0000000000000000" "01" ); - assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); // T5 let expected = &hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); @@ -616,7 +795,7 @@ mod tests { "0000000000000000" "01" ); - assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); // T6 let expected = &hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); @@ -628,7 +807,7 @@ mod tests { "0000000000000000" "00" ); - assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); // T7 let expected = &hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); @@ -640,14 +819,14 @@ mod tests { "0000000000000000" "01" ); - assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); // T8 // NOTE: - // original test case ran ffffffff rounds of blake2b - // with an output of fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615 - // I ran this sucessfully 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. + // original test case ran ffffffff rounds of blake2b + // with an expected output of fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615 + // I ran this sucessfully 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" @@ -657,6 +836,6 @@ mod tests { "0000000000000000" "01" ); - assert_eq!(blake2f(&rt, input), Ok(expected.to_vec())); + assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); } } diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index afe3787c4..cac7538ab 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -3,6 +3,8 @@ // see https://github.com/paritytech/parity-common/issues/660 #![allow(clippy::ptr_offset_with_cast, clippy::assign_op_pattern)] +use substrate_bn::arith; + use { fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, impl_serde::impl_uint_serde, std::cmp::Ordering, uint::construct_uint, @@ -18,6 +20,12 @@ impl From<&TokenAmount> for U256 { } } +impl From for arith::U256 { + fn from(src: U256) -> arith::U256 { + arith::U256::from(src.0) + } +} + impl From<&U256> for TokenAmount { fn from(ui: &U256) -> TokenAmount { let mut bits = [0u8; 32]; From be69f54cb26ba8c1d91172b5c039610a65a4444d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Sep 2022 17:15:56 -0700 Subject: [PATCH 062/339] feat: add an embryo actor (#667) The embryo actor: - Has no constructor (the system is expected to simply "set" the embryo code CID, not construct it). - Immediately aborts when called with a non-zero method. We could, alternatively, use some well-known "id" address. But it's nice to make this actual valid wasm. --- Cargo.lock | 9 +++++++++ Cargo.toml | 1 + actors/embryo/Cargo.toml | 20 ++++++++++++++++++++ actors/embryo/src/lib.rs | 8 ++++++++ build.rs | 1 + runtime/src/runtime/builtins.rs | 4 +++- runtime/src/test_utils.rs | 6 ++++++ state/src/check.rs | 1 + test_vm/src/lib.rs | 3 +++ 9 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 actors/embryo/Cargo.toml create mode 100644 actors/embryo/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2146d11ea..3c1c8ad58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,6 +676,14 @@ dependencies = [ "serde", ] +[[package]] +name = "fil_actor_embryo" +version = "10.0.0-alpha.1" +dependencies = [ + "fvm_sdk", + "fvm_shared", +] + [[package]] name = "fil_actor_evm" version = "10.0.0-alpha.1" @@ -929,6 +937,7 @@ dependencies = [ "fil_actor_account", "fil_actor_bundler", "fil_actor_cron", + "fil_actor_embryo", "fil_actor_evm", "fil_actor_init", "fil_actor_market", diff --git a/Cargo.toml b/Cargo.toml index 12b42995b..7ffa36fba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ fil_actor_miner = { version = "10.0.0-alpha.1", path = "./actors/miner", feature fil_actor_reward = { version = "10.0.0-alpha.1", path = "./actors/reward", features = ["fil-actor"] } fil_actor_system = { version = "10.0.0-alpha.1", path = "./actors/system", features = ["fil-actor"] } fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } +fil_actor_embryo = { version = "10.0.0-alpha.1", path = "./actors/embryo", features = ["fil-actor"] } fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] } [build-dependencies] diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml new file mode 100644 index 000000000..d7d678133 --- /dev/null +++ b/actors/embryo/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "fil_actor_embryo" +description = "Builtin embryo actor for Filecoin" +version = "10.0.0-alpha.1" +license = "MIT OR Apache-2.0" +authors = ["Protocol Labs", "Filecoin Core Devs"] +edition = "2021" +keywords = ["filecoin", "web3", "wasm"] + +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + +[dependencies] +fvm_sdk = { version = "3.0.0-alpha.2", optional = true } +fvm_shared = { version = "3.0.0-alpha.1", optional = true } + +[features] +fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/embryo/src/lib.rs b/actors/embryo/src/lib.rs new file mode 100644 index 000000000..d33f2bfd8 --- /dev/null +++ b/actors/embryo/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "fil-actor")] +#[no_mangle] +pub extern "C" fn invoke(_: u32) -> u32 { + fvm_sdk::vm::abort( + fvm_shared::error::ExitCode::USR_UNHANDLED_MESSAGE.value(), + Some("embryo actors may only receive messages on method 0"), + ) +} diff --git a/build.rs b/build.rs index c7b7b7c28..09d28893c 100644 --- a/build.rs +++ b/build.rs @@ -25,6 +25,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("multisig", "multisig"), ("reward", "reward"), ("verifreg", "verifiedregistry"), + ("embryo", "embryo"), ("evm", "evm"), ]; diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index 93245926b..adc93f046 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -19,7 +19,8 @@ pub enum Type { Multisig = 9, Reward = 10, VerifiedRegistry = 11, - EVM = 12, + Embryo = 12, + EVM = 13, } impl Type { @@ -36,6 +37,7 @@ impl Type { Type::Multisig => "multisig", Type::Reward => "reward", Type::VerifiedRegistry => "verifiedregistry", + Type::Embryo => "embryo", Type::EVM => "evm", } } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index ad8ff80ac..d3b4df38f 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -57,6 +57,7 @@ lazy_static::lazy_static! { pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/multisig"); pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward"); pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry"); + pub static ref EMBRYO_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/embryo"); pub static ref EVM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/evm"); pub static ref ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); @@ -71,6 +72,7 @@ lazy_static::lazy_static! { map.insert(*MULTISIG_ACTOR_CODE_ID, Type::Multisig); map.insert(*REWARD_ACTOR_CODE_ID, Type::Reward); map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); + map.insert(*EMBRYO_ACTOR_CODE_ID, Type::EVM); map.insert(*EVM_ACTOR_CODE_ID, Type::EVM); map }; @@ -86,6 +88,8 @@ lazy_static::lazy_static! { (Type::Multisig, *MULTISIG_ACTOR_CODE_ID), (Type::Reward, *REWARD_ACTOR_CODE_ID), (Type::VerifiedRegistry, *VERIFREG_ACTOR_CODE_ID), + (Type::Embryo, *EMBRYO_ACTOR_CODE_ID), + (Type::EVM, *EVM_ACTOR_CODE_ID), ] .into_iter() .collect(); @@ -95,6 +99,8 @@ lazy_static::lazy_static! { map.insert(*PAYCH_ACTOR_CODE_ID, ()); map.insert(*MULTISIG_ACTOR_CODE_ID, ()); map.insert(*MINER_ACTOR_CODE_ID, ()); + map.insert(*EMBRYO_ACTOR_CODE_ID, ()); + map.insert(*EVM_ACTOR_CODE_ID, ()); map }; } diff --git a/state/src/check.rs b/state/src/check.rs index 032a49b5d..331f0e89c 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -204,6 +204,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( acc.with_prefix("verifreg: ").add_all(&msgs); verifreg_summary = Some(summary); } + Some(Type::Embryo) => {} Some(Type::EVM) => {} None => { bail!("unexpected actor code CID {} for address {}", actor.code, key); diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 0085cfd03..55dddb7be 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -661,6 +661,9 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { Type::Power => PowerActor::invoke_method(self, self.msg.method, ¶ms), Type::PaymentChannel => PaychActor::invoke_method(self, self.msg.method, ¶ms), Type::VerifiedRegistry => VerifregActor::invoke_method(self, self.msg.method, ¶ms), + Type::Embryo => { + Err(ActorError::unhandled_message("embryo actors only handle method 0".into())) + } Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, ¶ms), }; if res.is_err() { From 97d501fdb5bf1f86af45f2fed4bd10556cfbea33 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Sep 2022 17:31:49 -0700 Subject: [PATCH 063/339] fix: make the empty array CID const and export it (#668) Otherwise, we'll perform a hash in WASM when we first dereference (max once per call) which is just wasteful. This also fixes the integration tests to use the "correct" empty object. Previously, they were using `()` which mapped to null, not `[]`. --- runtime/Cargo.toml | 4 ++-- runtime/src/runtime/empty.rs | 37 ++++++++++++++++++++++++++++++++++++ runtime/src/runtime/fvm.rs | 14 ++++---------- runtime/src/runtime/mod.rs | 3 +++ test_vm/src/lib.rs | 10 +++------- 5 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 runtime/src/runtime/empty.rs diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3ab9d19e8..a42ca8162 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -14,7 +14,7 @@ fvm_shared = { version = "3.0.0-alpha.1", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } -lazy_static = "1.4.0" +lazy_static = { version = "1.4.0", optional = true } unsigned-varint = "0.7.1" byteorder = "1.4.3" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } @@ -86,4 +86,4 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng"] +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static"] diff --git a/runtime/src/runtime/empty.rs b/runtime/src/runtime/empty.rs new file mode 100644 index 000000000..4885a09f8 --- /dev/null +++ b/runtime/src/runtime/empty.rs @@ -0,0 +1,37 @@ +use std::mem; + +use cid::multihash::Multihash; +use cid::Cid; +use fvm_ipld_encoding::DAG_CBOR; +use fvm_shared::crypto::hash::SupportedHashes; + +const fn const_unwrap(r: Result) -> T { + let v = match r { + Ok(r) => r, + Err(_) => panic!(), + }; + mem::forget(r); + v +} + +// 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 +const EMPTY_ARR_HASH_DIGEST: &[u8] = &[ + 0x45, 0xb0, 0xcf, 0xc2, 0x20, 0xce, 0xec, 0x5b, 0x7c, 0x1c, 0x62, 0xc4, 0xd4, 0x19, 0x3d, 0x38, + 0xe4, 0xeb, 0xa4, 0x8e, 0x88, 0x15, 0x72, 0x9c, 0xe7, 0x5f, 0x9c, 0x0a, 0xb0, 0xe4, 0xc1, 0xc0, +]; + +// bafy2bzacebc3bt6cedhoyw34drrmjvazhu4oj25er2ebk4u445pzycvq4ta4a +pub const EMPTY_ARR_CID: Cid = Cid::new_v1( + DAG_CBOR, + const_unwrap(Multihash::wrap(SupportedHashes::Blake2b256 as u64, EMPTY_ARR_HASH_DIGEST)), +); + +#[test] +fn test_empty_arr_cid() { + use cid::multihash::{Code, MultihashDigest}; + use fvm_ipld_encoding::to_vec; + + let empty = to_vec::<[(); 0]>(&[]).unwrap(); + let expected = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(&empty)); + assert_eq!(EMPTY_ARR_CID, expected); +} diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 0ea68175e..544328f31 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Error}; -use cid::multihash::{Code, MultihashDigest}; +use cid::multihash::Code; use cid::Cid; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::{to_vec, Cbor, CborStore, RawBytes, DAG_CBOR}; +use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; use fvm_sdk::NO_DATA_BLOCK_ID; use fvm_shared::address::Address; @@ -32,13 +32,7 @@ use crate::runtime::{ }; use crate::{actor_error, ActorError, Runtime}; -lazy_static::lazy_static! { - /// Cid of the empty array Cbor bytes (`EMPTY_ARR_BYTES`). - pub static ref EMPTY_ARR_CID: Cid = { - let empty = to_vec::<[(); 0]>(&[]).unwrap(); - Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(&empty)) - }; -} +use super::EMPTY_ARR_CID; /// A runtime that bridges to the FVM environment through the FVM SDK. pub struct FvmRuntime { @@ -239,7 +233,7 @@ where fn create(&mut self, obj: &C) -> Result<(), ActorError> { let root = fvm::sself::root()?; - if root != *EMPTY_ARR_CID { + if root != EMPTY_ARR_CID { return Err( actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root), ); diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 1970b7116..be45e7c15 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -37,6 +37,9 @@ mod actor_blockstore; #[cfg(feature = "fil-actor")] pub mod fvm; +pub(crate) mod empty; +pub use empty::EMPTY_ARR_CID; + /// Runtime is the VM's internal runtime object. /// this is everything that is accessible to actors, beyond parameters. pub trait Runtime: Primitives + Verifier + RuntimePolicy { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 55dddb7be..0cbb238f2 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -18,7 +18,7 @@ use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy, - Verifier, + Verifier, EMPTY_ARR_CID, }; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::MessageAccumulator; @@ -70,7 +70,6 @@ pub struct VM<'bs> { total_fil: TokenAmount, actors_dirty: RefCell, actors_cache: RefCell>, - empty_obj_cid: Cid, network_version: NetworkVersion, curr_epoch: ChainEpoch, invocations: RefCell>, @@ -115,14 +114,12 @@ pub const FIRST_TEST_USER_ADDR: ActorID = FIRST_NON_SINGLETON_ADDR + 3; impl<'bs> VM<'bs> { pub fn new(store: &'bs MemoryBlockstore) -> VM<'bs> { let mut actors = Hamt::<&'bs MemoryBlockstore, Actor, BytesKey, Sha256>::new(store); - let empty = store.put_cbor(&(), Code::Blake2b256).unwrap(); VM { store, state_root: RefCell::new(actors.flush().unwrap()), total_fil: TokenAmount::zero(), actors_dirty: RefCell::new(false), actors_cache: RefCell::new(HashMap::new()), - empty_obj_cid: empty, network_version: NetworkVersion::V16, curr_epoch: ChainEpoch::zero(), invocations: RefCell::new(vec![]), @@ -260,7 +257,6 @@ impl<'bs> VM<'bs> { total_fil: self.total_fil, actors_dirty: RefCell::new(false), actors_cache: RefCell::new(HashMap::new()), - empty_obj_cid: self.empty_obj_cid, network_version: self.network_version, curr_epoch: epoch, invocations: RefCell::new(vec![]), @@ -691,7 +687,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat "attempt to create new actor at existing address".to_string(), )); } - let a = actor(code_id, self.v.empty_obj_cid, 0, TokenAmount::zero()); + let a = actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero()); self.v.set_actor(addr, a); Ok(()) } @@ -846,7 +842,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat "failed to create state".to_string(), )), Some(mut act) => { - if act.head != self.v.empty_obj_cid { + if act.head != EMPTY_ARR_CID { Err(ActorError::unchecked( ExitCode::SYS_ASSERTION_FAILED, "failed to construct state: already initialized".to_string(), From d5e40c0555fbb74f09055b7d72ed2d39e8564402 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Sep 2022 23:34:31 -0700 Subject: [PATCH 064/339] chore: rev codecov cache (#688) --- .github/actions/rust-cargo-run/action.yml | 4 ++-- .github/workflows/ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml index 9d9ae02b6..ee2aa8cff 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: test + default: v2-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: v2-${{ inputs.cache_name }} # change this to invalidate sccache for this job + shared-key: ${{ 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 f27745098..46b577aa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: command: version components: llvm-tools-preview github_token: ${{ secrets.GITHUB_TOKEN }} - cache_name: cov + cache_name: v3-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 From 047560d6d2ef22d33120a484c1da8b4f2f24c94e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Sep 2022 07:50:17 -0700 Subject: [PATCH 065/339] test: correctly compute the f2 address in the test vm (#686) Previously, we weren't taking the nonce into account which prevented us from testing multiple messages from the same account. --- test_vm/src/lib.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 0cbb238f2..ad50ccc6f 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -391,7 +391,7 @@ impl<'bs> VM<'bs> { // make top level context with internal context let top = TopCtx { originator_stable_addr: from, - _originator_call_seq: call_seq, + originator_call_seq: call_seq, new_actor_addr_count: RefCell::new(0), circ_supply: TokenAmount::from_whole(1_000_000_000), }; @@ -490,7 +490,7 @@ impl<'bs> VM<'bs> { #[derive(Clone)] pub struct TopCtx { originator_stable_addr: Address, - _originator_call_seq: u64, + originator_call_seq: u64, new_actor_addr_count: RefCell, circ_supply: TokenAmount, } @@ -877,15 +877,12 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat } fn new_actor_address(&mut self) -> Result { - let osa_bytes = self.top.originator_stable_addr.to_bytes(); - let mut seq_num_bytes = self.top.originator_stable_addr.to_bytes(); - let cnt = self.top.new_actor_addr_count.take(); - self.top.new_actor_addr_count.replace(cnt + 1); - let mut cnt_bytes = serialize(&cnt, "count failed").unwrap().to_vec(); - let mut out = osa_bytes; - out.append(&mut seq_num_bytes); - out.append(&mut cnt_bytes); - Ok(Address::new_actor(out.as_slice())) + let mut b = self.top.originator_stable_addr.to_bytes(); + b.extend_from_slice(&self.top.originator_call_seq.to_be_bytes()); + b.extend_from_slice( + &self.top.new_actor_addr_count.replace_with(|old| *old + 1).to_be_bytes(), + ); + Ok(Address::new_actor(&b)) } fn delete_actor(&mut self, _beneficiary: &Address) -> Result<(), ActorError> { From 023dd20d460234a8ec370a599df5a6c0672c567e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Sep 2022 09:04:09 -0700 Subject: [PATCH 066/339] feat: basic f4 support (#684) - Adds a create4 method to the init actor. - Adds embryo actor creation to the test vm. --- Cargo.lock | 56 ++++++++--------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/init/src/lib.rs | 64 +++++++++++++++++++- actors/init/src/state.rs | 48 ++++++++++++++- actors/init/src/testing.rs | 29 +++++++-- actors/init/src/types.rs | 14 ++++- actors/init/tests/init_actor_test.rs | 88 ++++++++++++++++++++++++--- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- runtime/src/builtin/singletons.rs | 1 + runtime/src/runtime/fvm.rs | 1 + state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 53 +++++++++++------ test_vm/tests/init_test.rs | 89 ++++++++++++++++++++++++++++ test_vm/tests/test_vm_test.rs | 65 +++++++++++++++++++- 27 files changed, 457 insertions(+), 83 deletions(-) create mode 100644 test_vm/tests/init_test.rs diff --git a/Cargo.lock b/Cargo.lock index 3c1c8ad58..beeeeb29c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.21" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -923,7 +923,7 @@ dependencies = [ "regex", "serde", "serde_repr", - "sha2 0.10.5", + "sha2 0.10.6", "thiserror", "unsigned-varint", ] @@ -1199,7 +1199,7 @@ dependencies = [ "multihash", "once_cell", "serde", - "sha2 0.10.5", + "sha2 0.10.6", "thiserror", ] @@ -1220,9 +1220,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.1" +version = "3.0.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261314c3de2e9d6d479977dfba985d38e05cacf59de1dc412e92aab6ccd61d51" +checksum = "d92bd2ab8e01c771bb1e8ab1e6a68980b1592d48d8cc7b5fb1449f9f1c5f19ba" dependencies = [ "anyhow", "blake2b_simd", @@ -1408,9 +1408,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.132" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libipld-core" @@ -1510,12 +1510,12 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.3", + "digest 0.10.5", "multihash-derive", "ripemd", "serde", "serde-big-array", - "sha2 0.10.5", + "sha2 0.10.6", "sha3", "unsigned-varint", ] @@ -1839,11 +1839,11 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "ripemd" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1facec54cb5e0dc08553501fa740091086d0259ad0067e0d4103448e4cb22ed3" +checksum = "74e2ee464e763f6527991a6d532142e3c2016eb9907cc081401c11862c26a840" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -1990,13 +1990,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2014,22 +2014,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] name = "sha3" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaedf34ed289ea47c2b741bb72e5357a209512d67bcd4bda44359e5bf0470f56" +checksum = "e2904bea16a1ae962b483322a1c7b81d976029203aea1f461e51cd7705db7ba9" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", "keccak", ] @@ -2132,9 +2132,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", @@ -2205,9 +2205,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 00b1c63dd..00fa25516 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 9a1049244..5fc30f610 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index d7d678133..134fd0289 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fvm_sdk = { version = "3.0.0-alpha.2", optional = true } -fvm_shared = { version = "3.0.0-alpha.1", optional = true } +fvm_shared = { version = "3.0.0-alpha.2", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index f56448343..338c45fb2 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index e991179b7..dc581ebb8 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index eef66440b..2bfe3816d 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -1,8 +1,11 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use std::iter; + use cid::Cid; use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::EAM_ACTOR_ADDR; use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -28,8 +31,9 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, Exec = 2, + Exec4 = 3, #[cfg(feature = "m2-native")] - InstallCode = 3, + InstallCode = 4, } /// Init actor @@ -85,7 +89,7 @@ impl Actor { log::trace!("robust address: {:?}", &robust_address); // Allocate an ID for this actor. - // Store mapping of pubkey or actor address to actor ID + // Store mapping of actor addresses to the actor ID. let id_address: ActorID = rt.transaction(|s: &mut State, rt| { s.map_address_to_new_id(rt.store(), &robust_address).map_err(|e| { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to allocate ID address") @@ -107,6 +111,58 @@ impl Actor { Ok(ExecReturn { id_address: Address::new_id(id_address), robust_address }) } + /// Exec init actor + pub fn exec4(rt: &mut RT, params: Exec4Params) -> Result + where + BS: Blockstore, + RT: Runtime, + { + if cfg!(feature = "m2-native") { + rt.validate_immediate_caller_accept_any()?; + } else { + rt.validate_immediate_caller_is(iter::once(&EAM_ACTOR_ADDR))?; + } + + // Compute the f4 address. + let caller_id = rt.message().caller().id().unwrap(); + let delegated_address = + Address::new_delegated(caller_id, ¶ms.subaddress).map_err(|e| { + ActorError::illegal_argument(format!("invalid delegated address: {}", e)) + })?; + + log::trace!("delegated address: {:?}", &delegated_address); + + // Compute a re-org-stable address. + // This address exists for use by messages coming from outside the system, in order to + // stably address the newly created actor even if a chain re-org causes it to end up with + // a different ID. + let robust_address = rt.new_actor_address()?; + + log::trace!("robust address: {:?}", &robust_address); + + // Allocate an ID for this actor. + // Store mapping of actor addresses to the actor ID. + let id_address: ActorID = rt.transaction(|s: &mut State, rt| { + s.map_address_to_f4(rt.store(), &robust_address, &delegated_address).map_err(|e| { + e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to allocate ID address") + }) + })?; + + // Create an empty actor + rt.create_actor(params.code_cid, id_address)?; + + // Invoke constructor + rt.send( + &Address::new_id(id_address), + METHOD_CONSTRUCTOR, + params.constructor_params, + rt.message().value_received(), + ) + .map_err(|err| err.wrap("constructor failed"))?; + + Ok(Exec4Return { id_address: Address::new_id(id_address), robust_address }) + } + #[cfg(feature = "m2-native")] pub fn install(rt: &mut RT, params: InstallParams) -> Result where @@ -176,6 +232,10 @@ impl ActorCode for Actor { let res = Self::exec(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::Exec4) => { + let res = Self::exec4(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } #[cfg(feature = "m2-native")] Some(Method::InstallCode) => { let res = Self::install(rt, cbor::deserialize_params(params)?)?; diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index 5e256789b..1b5225dad 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -5,8 +5,9 @@ use anyhow::anyhow; #[cfg(feature = "m2-native")] use cid::multihash::Code; use cid::Cid; +use fil_actors_runtime::actor_error; use fil_actors_runtime::{ - actor_error, make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, + make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -45,7 +46,8 @@ impl State { /// Allocates a new ID address and stores a mapping of the argument address to it. /// Fails if the argument address is already present in the map to facilitate a tombstone /// for when the predictable robust address generation is implemented. - /// Returns the newly-allocated address. + /// + /// Returns the newly-allocated actor ID. pub fn map_address_to_new_id( &mut self, store: &BS, @@ -70,6 +72,48 @@ impl State { Ok(id) } + /// Allocates a new ID address and stores a mapping of the argument addresses to it. + /// Returns the newly-allocated actor ID. + pub fn map_address_to_f4( + &mut self, + store: &BS, + addr: &Address, + f4addr: &Address, + ) -> anyhow::Result + where + BS: Blockstore, + { + let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; + + // Assign a new ID address, or use the one currently mapped to the f4 address. We don't + // bother checking if the target actor is an embryo here, the FVM will check that when we go to create the actor. + let f4addr_key = f4addr.to_bytes().into(); + let id: u64 = match map.get(&f4addr_key)? { + Some(id) => *id, + None => { + let id = self.next_id; + self.next_id += 1; + map.set(f4addr_key, id)?; + id + } + }; + + // Then go ahead and assign the f2 address. + let is_new = map.set_if_absent(addr.to_bytes().into(), id)?; + if !is_new { + // this is impossible today as the robust address is a hash of unique inputs + // but in close future predictable address generation will make this possible + return Err(anyhow!(actor_error!( + forbidden, + "robust address {} is already allocated in the address map", + addr + ))); + } + self.address_map = map.flush()?; + + Ok(id) + } + /// ResolveAddress resolves an address to an ID-address, if possible. /// If the provided address is an ID address, it is returned as-is. /// This means that mapped ID-addresses (which should only appear as values, not keys) and diff --git a/actors/init/src/testing.rs b/actors/init/src/testing.rs index 88c094593..9fc069c6a 100644 --- a/actors/init/src/testing.rs +++ b/actors/init/src/testing.rs @@ -29,7 +29,8 @@ pub fn check_state_invariants( let mut init_summary = StateSummary { ids_by_address: HashMap::new(), next_id: state.next_id }; - let mut address_by_id = HashMap::::new(); + let mut stable_address_by_id = HashMap::::new(); + let mut delegated_address_by_id = HashMap::::new(); match Map::<_, ActorID>::load(&state.address_map, store) { Ok(address_map) => { let ret = address_map.for_each(|key, actor_id| { @@ -44,11 +45,29 @@ pub fn check_state_invariants( format!("unexpected singleton ID value {actor_id}"), ); - if let Some(duplicate) = address_by_id.insert(*actor_id, key_address) { - acc.add(format!( - "duplicate mapping to ID {actor_id}: {key_address} {duplicate}" - )); + match key_address.protocol() { + Protocol::ID => { + acc.add(format!("key {key_address} is an ID address")); + } + Protocol::Delegated => { + if let Some(duplicate) = + delegated_address_by_id.insert(*actor_id, key_address) + { + acc.add(format!( + "duplicate mapping to ID {actor_id}: {key_address} {duplicate}" + )); + } + } + _ => { + if let Some(duplicate) = stable_address_by_id.insert(*actor_id, key_address) + { + acc.add(format!( + "duplicate mapping to ID {actor_id}: {key_address} {duplicate}" + )); + } + } } + init_summary.ids_by_address.insert(key_address, *actor_id); Ok(()) diff --git a/actors/init/src/types.rs b/actors/init/src/types.rs index 07b08505f..fe2afcb02 100644 --- a/actors/init/src/types.rs +++ b/actors/init/src/types.rs @@ -20,7 +20,7 @@ pub struct ExecParams { } /// Init actor Exec Return value -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] pub struct ExecReturn { /// ID based address for created actor pub id_address: Address, @@ -28,8 +28,20 @@ pub struct ExecReturn { pub robust_address: Address, } +/// Init actor Exec4 Params +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct Exec4Params { + pub code_cid: Cid, + pub constructor_params: RawBytes, + pub subaddress: RawBytes, +} + +/// Init actor Exec4 Return value +pub type Exec4Return = ExecReturn; + impl Cbor for ExecReturn {} impl Cbor for ExecParams {} +impl Cbor for Exec4Params {} /// Init actor Install Params #[cfg(feature = "m2-native")] diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 74538d430..d968eb239 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -4,10 +4,11 @@ use cid::Cid; use fil_actor_init::testing::check_state_invariants; use fil_actor_init::{ - Actor as InitActor, ConstructorParams, ExecParams, ExecReturn, Method, State, + Actor as InitActor, ConstructorParams, Exec4Params, Exec4Return, ExecParams, ExecReturn, + 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, }; @@ -80,7 +81,6 @@ 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(); - let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).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); @@ -142,7 +142,6 @@ fn create_2_payment_channels() { ); let exec_ret = exec_and_verify(&mut rt, *PAYCH_ACTOR_CODE_ID, &fake_params).unwrap(); - let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).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"); @@ -184,8 +183,6 @@ fn create_storage_miner() { ); let exec_ret = exec_and_verify(&mut rt, *MINER_ACTOR_CODE_ID, &fake_params).unwrap(); - - let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).unwrap(); assert_eq!(unique_address, exec_ret.robust_address); assert_eq!(expected_id_addr, exec_ret.id_address); @@ -236,7 +233,6 @@ 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(); - let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).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); @@ -287,6 +283,51 @@ fn sending_constructor_failure() { check_state(&rt); } +#[test] +fn call_exec4() { + let mut rt = construct_runtime(); + construct_and_verify(&mut rt); + + // Assign addresses + let unique_address = Address::new_actor(b"test"); + rt.new_actor_addr = Some(unique_address); + + // Next id + let expected_id = 100; + let expected_id_addr = Address::new_id(expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + + let fake_params = ConstructorParams { network_name: String::from("fake_param") }; + // Expect a send to the multisig actor constructor + rt.expect_send( + expected_id_addr, + METHOD_CONSTRUCTOR, + RawBytes::serialize(&fake_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + let subaddr = b"foobar"; + let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); + + // Return should have been successful. Check the returned addresses + let exec_ret = + exec4_and_verify(&mut rt, subaddr, *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 that we assigned the right f4 address. + let init_state: State = rt.get_state(); + let resolved_id = init_state + .resolve_address(rt.store(), &f4_addr) + .ok() + .flatten() + .expect("failed to lookup f4 address"); + assert_eq!(expected_id_addr, resolved_id, "f4 address not assigned to the right actor"); +} + fn construct_and_verify(rt: &mut MockRuntime) { rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let params = ConstructorParams { network_name: "mock".to_string() }; @@ -312,7 +353,7 @@ fn exec_and_verify( rt: &mut MockRuntime, code_id: Cid, params: &S, -) -> Result +) -> Result where S: Serialize, { @@ -325,5 +366,34 @@ where rt.verify(); check_state(rt); - ret + ret.and_then(|v| v.deserialize().map_err(|e| e.into())) +} + +fn exec4_and_verify( + rt: &mut MockRuntime, + subaddr: &[u8], + code_id: Cid, + params: &S, +) -> Result +where + S: Serialize, +{ + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_ADDR); + 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(), + subaddress: subaddr.to_owned().into(), + }; + + let ret = + rt.call::(Method::Exec4 as u64, &RawBytes::serialize(&exec_params).unwrap()); + + rt.verify(); + check_state(rt); + ret.and_then(|v| v.deserialize().map_err(|e| e.into())) } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 6eaed859d..46328c384 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_bitfield = "0.5.2" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index bf7790a35..95a567a3c 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_hamt = "0.5.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index e53d0f796..e61761f8b 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 31092f923..d259c5683 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index ced852ff9..60f448780 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 9db5a2e01..dce1395ea 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 6b00c044f..addf5b99b 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 73e8f0964..0337e94c3 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a42ca8162..a5d3a5579 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.5.1" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/runtime/src/builtin/singletons.rs b/runtime/src/builtin/singletons.rs index 8c896e976..ebcb05ea1 100644 --- a/runtime/src/builtin/singletons.rs +++ b/runtime/src/builtin/singletons.rs @@ -24,6 +24,7 @@ define_singletons! { STORAGE_POWER_ACTOR = 4, STORAGE_MARKET_ACTOR = 5, VERIFIED_REGISTRY_ACTOR = 6, + EAM_ACTOR = 10, CHAOS_ACTOR = 98, BURNT_FUNDS_ACTOR = 99, } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 544328f31..71d2ef043 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -361,6 +361,7 @@ where ErrorNumber::IllegalArgument => { ActorError::illegal_argument("failed to create actor".into()) } + ErrorNumber::Forbidden => ActorError::forbidden("actor already exists".into()), _ => actor_error!(assertion_failed; "create failed with unknown error: {}", e), }) } diff --git a/state/Cargo.toml b/state/Cargo.toml index abd7b401f..1643149e9 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 7ad2d77ae..8e58180af 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,7 +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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.1", default-features = false } +fvm_shared = { version = "3.0.0-alpha.2", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index ad50ccc6f..961ef1686 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -33,8 +33,8 @@ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; +use fvm_shared::address::Address; use fvm_shared::address::Payload; -use fvm_shared::address::{Address, Protocol}; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; @@ -546,16 +546,22 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } }; // Address does not yet exist, create it - let protocol = target.protocol(); - match protocol { - Protocol::Actor | Protocol::ID => { + let is_account = match target.payload() { + Payload::Secp256k1(_) | Payload::BLS(_) => true, + Payload::Delegated(da) + // Validate that there's an actor at the target ID (we don't care what is there, + // just that something is there). + if self.v.get_actor(Address::new_id(da.namespace())).is_some() => + { + false + } + _ => { return Err(ActorError::unchecked( ExitCode::SYS_INVALID_RECEIVER, - format!("cannot create account for address {} type {}", target, protocol), + format!("cannot create account for address {} type {}", target, target.protocol()), )); } - _ => (), - } + }; let mut st = self.v.get_state::(INIT_ACTOR_ADDR).unwrap(); let target_id = st.map_address_to_new_id(self.v.store, target).unwrap(); let target_id_addr = Address::new_id(target_id); @@ -580,13 +586,17 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { policy: self.policy, subinvocations: RefCell::new(vec![]), }; - new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id).unwrap(); - let res = new_ctx.invoke(); - let invoc = new_ctx.gather_trace(res); - RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { - subinvocs.push(invoc); - subinvocs - }); + if is_account { + new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id).unwrap(); + let res = new_ctx.invoke(); + let invoc = new_ctx.gather_trace(res); + RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { + subinvocs.push(invoc); + subinvocs + }); + } else { + new_ctx.create_actor(*EMBRYO_ACTOR_CODE_ID, target_id).unwrap(); + } } Ok((self.v.get_actor(target_id_addr).unwrap(), target_id_addr)) @@ -681,11 +691,16 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat } } let addr = Address::new_id(actor_id); - if self.v.get_actor(addr).is_some() { - return Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, - "attempt to create new actor at existing address".to_string(), - )); + match self.v.get_actor(addr) { + Some(act) if act.code == *EMBRYO_ACTOR_CODE_ID => (), + None => (), + _ => { + // can happen if an actor is deployed to an f4 address. + return Err(ActorError::unchecked( + ExitCode::USR_FORBIDDEN, + "attempt to create new actor at existing address".to_string(), + )); + } } let a = actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero()); self.v.set_actor(addr, a); diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs new file mode 100644 index 000000000..7d1c2409c --- /dev/null +++ b/test_vm/tests/init_test.rs @@ -0,0 +1,89 @@ +use fil_actor_init::Exec4Return; +use fil_actors_runtime::{ + cbor::serialize, + runtime::EMPTY_ARR_CID, + test_utils::{ACCOUNT_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, MULTISIG_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_embryo_actor(exp_bal: TokenAmount, v: &VM, addr: Address) { + let act = v.get_actor(addr).unwrap(); + assert_eq!(EMPTY_ARR_CID, act.head); + assert_eq!(*EMBRYO_ACTOR_CODE_ID, act.code); + assert_eq!(exp_bal, act.balance); +} + +#[test] +fn embryo_deploy() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + // Create a "fake" eam. + v.set_actor( + EAM_ACTOR_ADDR, + actor(*ACCOUNT_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero()), + ); + + // Create an embryo. + + 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, + RawBytes::default(), + ) + .unwrap() + .code + .is_success()); + let expect_id_addr = Address::new_id(FIRST_TEST_USER_ADDR); + assert_embryo_actor(TokenAmount::from_atto(42u8), &v, expect_id_addr); + + // Deploy a multisig to the embryo. + 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, + 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.deserialize().unwrap(); + + assert_eq!( + expect_id_addr, msig_ctor_ret.id_address, + "expected actor to be deployed over embryo" + ); + + // 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 1e17fe49c..5fb0c01f2 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -1,5 +1,8 @@ use fil_actor_account::State as AccountState; -use fil_actors_runtime::test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID}; +use fil_actors_runtime::runtime::EMPTY_ARR_CID; +use fil_actors_runtime::test_utils::{ + make_builtin, ACCOUNT_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, +}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -115,6 +118,66 @@ fn test_sent() { v.assert_state_invariants(); } +fn assert_embryo_actor(exp_bal: TokenAmount, v: &VM, addr: Address) { + let act = v.get_actor(addr).unwrap(); + assert_eq!(EMPTY_ARR_CID, act.head); + assert_eq!(*EMBRYO_ACTOR_CODE_ID, act.code); + assert_eq!(exp_bal, act.balance); +} + +#[test] +fn test_sent_f4() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + // send to an f4 actor where the address manager exists. + let addr1 = Address::new_delegated(0, &[]).unwrap(); + assert!(v + .apply_message( + TEST_FAUCET_ADDR, + addr1, + TokenAmount::from_atto(42u8), + METHOD_SEND, + RawBytes::default(), + ) + .unwrap() + .code + .is_success()); + let expect_id_addr1 = Address::new_id(FIRST_TEST_USER_ADDR); + assert_embryo_actor(TokenAmount::from_atto(42u8), &v, expect_id_addr1); + + // Send to an f4 actor where the previous send created the "manager". Importantly, we don't have to deploy code first. + let addr2 = Address::new_delegated(FIRST_TEST_USER_ADDR, &[]).unwrap(); + assert!(v + .apply_message( + TEST_FAUCET_ADDR, + addr2, + TokenAmount::from_atto(2u8), + METHOD_SEND, + RawBytes::default(), + ) + .unwrap() + .code + .is_success()); + let expect_id_addr2 = Address::new_id(FIRST_TEST_USER_ADDR + 1); + assert_embryo_actor(TokenAmount::from_atto(2u8), &v, expect_id_addr2); + + // Fail to send to to an f4 actor where the "manager" doesn't exist. + let addr2 = Address::new_delegated(FIRST_TEST_USER_ADDR + 2, &[]).unwrap(); + assert_eq!( + v.apply_message( + TEST_FAUCET_ADDR, + addr2, + TokenAmount::from_atto(2u8), + METHOD_SEND, + RawBytes::default(), + ) + .unwrap() + .code, + fvm_shared::error::ExitCode::SYS_INVALID_RECEIVER + ); +} + #[test] fn test_pk_gen() { let addrs = pk_addrs_from(5, 2); From 07dd60263f5c4189ea5cbdd2c1e0819a7edb94b5 Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Fri, 2 Sep 2022 08:02:25 +1000 Subject: [PATCH 067/339] Traits and impls for adding context and exit codes to results. (#589) Co-authored-by: dignifiedquire --- actors/init/src/lib.rs | 68 +++--- actors/init/src/state.rs | 83 +++++--- actors/multisig/src/lib.rs | 208 ++++++------------- actors/multisig/src/state.rs | 34 ++- actors/multisig/tests/multisig_actor_test.rs | 2 +- actors/paych/src/lib.rs | 7 +- actors/paych/tests/paych_actor_test.rs | 4 +- actors/system/src/lib.rs | 13 +- actors/verifreg/src/lib.rs | 71 +------ actors/verifreg/tests/verifreg_actor_test.rs | 4 +- runtime/src/actor_error.rs | 94 +++++++++ runtime/src/builtin/shared.rs | 11 +- 12 files changed, 284 insertions(+), 315 deletions(-) diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 2bfe3816d..5587d0dbd 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -5,12 +5,12 @@ use std::iter; use cid::Cid; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::EAM_ACTOR_ADDR; -use fil_actors_runtime::{actor_error, cbor, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{ + actor_error, cbor, ActorContext, ActorError, EAM_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, +}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; -use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; @@ -47,10 +47,7 @@ impl Actor { { let sys_ref: &Address = &SYSTEM_ACTOR_ADDR; rt.validate_immediate_caller_is(std::iter::once(sys_ref))?; - let state = State::new(rt.store(), params.network_name).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct init actor state") - })?; - + let state = State::new(rt.store(), params.network_name)?; rt.create(&state)?; Ok(()) @@ -91,9 +88,8 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. let id_address: ActorID = rt.transaction(|s: &mut State, rt| { - s.map_address_to_new_id(rt.store(), &robust_address).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to allocate ID address") - }) + s.map_address_to_new_id(rt.store(), &robust_address) + .context("failed to allocate ID address") })?; // Create an empty actor @@ -106,7 +102,7 @@ impl Actor { params.constructor_params, rt.message().value_received(), ) - .map_err(|err| err.wrap("constructor failed"))?; + .context("constructor failed")?; Ok(ExecReturn { id_address: Address::new_id(id_address), robust_address }) } @@ -143,9 +139,8 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. let id_address: ActorID = rt.transaction(|s: &mut State, rt| { - s.map_address_to_f4(rt.store(), &robust_address, &delegated_address).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to allocate ID address") - }) + s.map_address_to_f4(rt.store(), &robust_address, &delegated_address) + .context("constructor failed") })?; // Create an empty actor @@ -170,42 +165,35 @@ impl Actor { RT: Runtime, { use cid::multihash::Code; + use fil_actors_runtime::AsActorError; use fvm_ipld_blockstore::Block; + 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)).map_err(|e| { - e.downcast_default( - ExitCode::USR_SERIALIZATION, - "failed to put code into the bockstore", - ) - })?; - - if st.is_installed_actor(rt.store(), &code_cid).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to check state for installed actor", - ) - })? { + 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).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - "failed to check state for installed actor", - ) - })?; + 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).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to add installed actor to state", - ) - })?; + 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)) })?; diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index 1b5225dad..7fd043fc8 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -1,13 +1,12 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; #[cfg(feature = "m2-native")] use cid::multihash::Code; use cid::Cid; -use fil_actors_runtime::actor_error; use fil_actors_runtime::{ - make_empty_map, make_map_with_root_and_bitwidth, FIRST_NON_SINGLETON_ADDR, + actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, AsActorError, + FIRST_NON_SINGLETON_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -15,6 +14,7 @@ use fvm_ipld_encoding::Cbor; #[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}; /// State is reponsible for creating @@ -28,12 +28,15 @@ pub struct State { } impl State { - pub fn new(store: &BS, network_name: String) -> anyhow::Result { + pub fn new(store: &BS, network_name: String) -> Result { let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) .flush() - .map_err(|e| anyhow!("failed to create empty map: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?; #[cfg(feature = "m2-native")] - let installed_actors = store.put_cbor(&Vec::::new(), Code::Blake2b256)?; + 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, @@ -52,22 +55,26 @@ impl State { &mut self, store: &BS, addr: &Address, - ) -> anyhow::Result { + ) -> Result { let id = self.next_id; self.next_id += 1; - let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; - let is_new = map.set_if_absent(addr.to_bytes().into(), id)?; + let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; + let is_new = map + .set_if_absent(addr.to_bytes().into(), id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?; if !is_new { // this is impossible today as the robust address is a hash of unique inputs // but in close future predictable address generation will make this possible - return Err(anyhow!(actor_error!( + return Err(actor_error!( forbidden, "robust address {} is already allocated in the address map", addr - ))); + )); } - self.address_map = map.flush()?; + self.address_map = + map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; Ok(id) } @@ -79,37 +86,45 @@ impl State { store: &BS, addr: &Address, f4addr: &Address, - ) -> anyhow::Result + ) -> Result where BS: Blockstore, { - let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; + let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; // Assign a new ID address, or use the one currently mapped to the f4 address. We don't // bother checking if the target actor is an embryo here, the FVM will check that when we go to create the actor. let f4addr_key = f4addr.to_bytes().into(); - let id: u64 = match map.get(&f4addr_key)? { + let id: u64 = match map + .get(&f4addr_key) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup f4 address in map")? + { Some(id) => *id, None => { let id = self.next_id; self.next_id += 1; - map.set(f4addr_key, id)?; + map.set(f4addr_key, id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set f4 address in map")?; id } }; // Then go ahead and assign the f2 address. - let is_new = map.set_if_absent(addr.to_bytes().into(), id)?; + let is_new = map + .set_if_absent(addr.to_bytes().into(), id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?; if !is_new { // this is impossible today as the robust address is a hash of unique inputs // but in close future predictable address generation will make this possible - return Err(anyhow!(actor_error!( + return Err(actor_error!( forbidden, "robust address {} is already allocated in the address map", addr - ))); + )); } - self.address_map = map.flush()?; + self.address_map = + map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; Ok(id) } @@ -128,14 +143,18 @@ impl State { &self, store: &BS, addr: &Address, - ) -> anyhow::Result> { + ) -> Result, ActorError> { if addr.protocol() == Protocol::ID { return Ok(Some(*addr)); } - let map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)?; + let map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; - Ok(map.get(&addr.to_bytes())?.copied().map(Address::new_id)) + let found = map + .get(&addr.to_bytes()) + .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 @@ -144,8 +163,11 @@ impl State { &self, store: &BS, cid: &Cid, - ) -> anyhow::Result { - let installed: Vec = match store.get_cbor(&self.installed_actors)? { + ) -> 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(), }; @@ -158,13 +180,18 @@ impl State { &mut self, store: &BS, cid: Cid, - ) -> anyhow::Result<()> { - let mut installed: Vec = match store.get_cbor(&self.installed_actors)? { + ) -> 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)?; + 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/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 594197dec..9851322e4 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -6,8 +6,8 @@ use std::collections::BTreeSet; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorDowncast, - ActorError, Map, INIT_ACTOR_ADDR, + actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorContext, + ActorError, AsActorError, Map, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -29,8 +29,6 @@ mod state; pub mod testing; mod types; -// * Updated to specs-actors commit: 845089a6d2580e46055c24415a6c32ee688e5186 (v3.0.0) - /// Multisig actor methods available #[derive(FromPrimitive)] #[repr(u64)] @@ -73,12 +71,7 @@ impl Actor { let mut resolved_signers = Vec::with_capacity(params.signers.len()); let mut dedup_signers = BTreeSet::new(); for signer in ¶ms.signers { - let resolved = resolve_to_actor_id(rt, signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", signer), - ) - })?; + let resolved = resolve_to_actor_id(rt, signer)?; if !dedup_signers.insert(resolved) { return Err( actor_error!(illegal_argument; "duplicate signer not allowed: {}", signer), @@ -101,10 +94,9 @@ impl Actor { return Err(actor_error!(illegal_argument; "negative unlock duration disallowed")); } - let empty_root = - make_empty_map::<_, ()>(rt.store(), HAMT_BIT_WIDTH).flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create empty map") - })?; + let empty_root = make_empty_map::<_, ()>(rt.store(), HAMT_BIT_WIDTH) + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?; let mut st: State = State { signers: resolved_signers, @@ -150,12 +142,8 @@ impl Actor { return Err(actor_error!(forbidden, "{} is not a signer", proposer)); } - let mut ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let mut ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let t_id = st.next_tx_id; st.next_tx_id.0 += 1; @@ -168,19 +156,15 @@ impl Actor { approved: Vec::new(), }; - ptx.set(t_id.key(), txn.clone()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to put transaction for propose", - ) - })?; + ptx.set(t_id.key(), txn.clone()).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to put transaction for propose", + )?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok((t_id, txn)) })?; @@ -205,12 +189,8 @@ impl Actor { return Err(actor_error!(forbidden; "{} is not a signer", approver)); } - let ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let txn = get_transaction(rt, &ptx, params.id, params.proposal_hash)?; @@ -245,20 +225,12 @@ impl Actor { } let mut ptx = make_map_with_root::<_, Transaction>(&st.pending_txs, rt.store()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; let (_, tx) = ptx .delete(¶ms.id.key()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to pop transaction {:?} for cancel", params.id), - ) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to pop transaction {:?} for cancel", params.id) })? .ok_or_else(|| { actor_error!(not_found, "no such transaction {:?} to cancel", params.id) @@ -269,23 +241,19 @@ impl Actor { return Err(actor_error!(forbidden; "Cannot cancel another signers transaction")); } - let calculated_hash = compute_proposal_hash(&tx, rt).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to compute proposal hash for (tx: {:?})", params.id), - ) - })?; + let calculated_hash = compute_proposal_hash(&tx, rt) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to compute proposal hash for (tx: {:?})", params.id) + })?; if !params.proposal_hash.is_empty() && params.proposal_hash != calculated_hash { return Err(actor_error!(illegal_state, "hash does not match proposal params")); } - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok(()) }) @@ -299,12 +267,7 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_new_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.signer), - ) - })?; + let resolved_new_signer = resolve_to_actor_id(rt, ¶ms.signer)?; rt.transaction(|st: &mut State, _| { if st.signers.len() >= SIGNERS_MAX { @@ -336,12 +299,7 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let resolved_old_signer = resolve_to_actor_id(rt, ¶ms.signer).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.signer), - ) - })?; + let resolved_old_signer = resolve_to_actor_id(rt, ¶ms.signer)?; rt.transaction(|st: &mut State, rt| { if !st.is_signer(&Address::new_id(resolved_old_signer)) { @@ -374,12 +332,8 @@ impl Actor { } // Remove approvals from removed signer - st.purge_approvals(rt.store(), &Address::new_id(resolved_old_signer)).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to purge approvals of removed signer", - ) - })?; + st.purge_approvals(rt.store(), &Address::new_id(resolved_old_signer)) + .context("failed to purge approvals of removed signer")?; st.signers.retain(|s| s != &Address::new_id(resolved_old_signer)); Ok(()) @@ -396,18 +350,8 @@ impl Actor { { let receiver = rt.message().receiver(); rt.validate_immediate_caller_is(std::iter::once(&receiver))?; - let from_resolved = resolve_to_actor_id(rt, ¶ms.from).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.from), - ) - })?; - let to_resolved = resolve_to_actor_id(rt, ¶ms.to).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", params.to), - ) - })?; + let from_resolved = resolve_to_actor_id(rt, ¶ms.from)?; + let to_resolved = resolve_to_actor_id(rt, ¶ms.to)?; rt.transaction(|st: &mut State, rt| { if !st.is_signer(&Address::new_id(from_resolved)) { @@ -424,12 +368,7 @@ impl Actor { // Add new signer st.signers.push(Address::new_id(to_resolved)); - st.purge_approvals(rt.store(), &Address::new_id(from_resolved)).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to purge approvals of removed signer", - ) - })?; + st.purge_approvals(rt.store(), &Address::new_id(from_resolved))?; Ok(()) })?; @@ -510,29 +449,21 @@ impl Actor { } let st = rt.transaction(|st: &mut State, rt| { - let mut ptx = make_map_with_root(&st.pending_txs, rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + let mut ptx = make_map_with_root(&st.pending_txs, rt.store()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; // update approved on the transaction txn.approved.push(rt.message().caller()); - ptx.set(tx_id.key(), txn.clone()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to put transaction {} for approval", tx_id.0), - ) - })?; + ptx.set(tx_id.key(), txn.clone()) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to put transaction {} for approval", tx_id.0) + })?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; // Go implementation holds reference to state after transaction so this must be cloned // to match to handle possible exit code inconsistency @@ -558,8 +489,7 @@ where let mut applied = false; let threshold_met = txn.approved.len() as u64 >= st.num_approvals_threshold; if threshold_met { - st.check_available(rt.current_balance(), &txn.value, rt.curr_epoch()) - .map_err(|e| actor_error!(insufficient_funds, "insufficient funds unlocked: {}", e))?; + st.check_available(rt.current_balance(), &txn.value, rt.curr_epoch())?; match rt.send(&txn.to, txn.method, txn.params.clone(), txn.value.clone()) { Ok(ser) => { @@ -573,26 +503,17 @@ where rt.transaction(|st: &mut State, rt| { let mut ptx = make_map_with_root::<_, Transaction>(&st.pending_txs, rt.store()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pending transactions", - ) - })?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending transactions")?; - ptx.delete(&txn_id.key()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to delete transaction for cleanup", - ) - })?; + ptx.delete(&txn_id.key()).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to delete transaction for cleanup", + )?; - st.pending_txs = ptx.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending transactions", - ) - })?; + st.pending_txs = ptx.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending transactions", + )?; Ok(()) })?; } @@ -612,21 +533,16 @@ where { let txn = ptx .get(&txn_id.key()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load transaction {:?} for approval", txn_id), - ) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load transaction {:?} for approval", txn_id) })? .ok_or_else(|| actor_error!(not_found, "no such transaction {:?} for approval", txn_id))?; if !proposal_hash.is_empty() { - let calculated_hash = compute_proposal_hash(txn, rt).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to compute proposal hash for (tx: {:?})", txn_id), - ) - })?; + let calculated_hash = compute_proposal_hash(txn, rt) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to compute proposal hash for (tx: {:?})", txn_id) + })?; if proposal_hash != calculated_hash { return Err(actor_error!( diff --git a/actors/multisig/src/state.rs b/actors/multisig/src/state.rs index 67d387503..d77af3f12 100644 --- a/actors/multisig/src/state.rs +++ b/actors/multisig/src/state.rs @@ -1,8 +1,8 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; use cid::Cid; +use fil_actors_runtime::{actor_error, ActorError, AsActorError}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; @@ -11,6 +11,7 @@ use fvm_shared::bigint::BigInt; use fvm_shared::bigint::Integer; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; use indexmap::IndexMap; use num_traits::Zero; @@ -75,8 +76,9 @@ impl State { &mut self, store: &BS, addr: &Address, - ) -> anyhow::Result<()> { - let mut txns = make_map_with_root(&self.pending_txs, store)?; + ) -> Result<(), ActorError> { + let mut txns = make_map_with_root(&self.pending_txs, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load txn map")?; // Identify transactions that need updating let mut txn_ids_to_purge = IndexMap::new(); @@ -87,20 +89,24 @@ impl State { } } Ok(()) - })?; + }) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to scan txns")?; // Update or remove those transactions. for (tx_id, mut txn) in txn_ids_to_purge { txn.approved.retain(|approver| approver != addr); if !txn.approved.is_empty() { - txns.set(tx_id.into(), txn)?; + txns.set(tx_id.into(), txn) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to update entry")?; } else { - txns.delete(&tx_id)?; + txns.delete(&tx_id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to delete entry")?; } } - self.pending_txs = txns.flush()?; + self.pending_txs = + txns.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store entries")?; Ok(()) } @@ -110,12 +116,17 @@ impl State { balance: TokenAmount, amount_to_spend: &TokenAmount, curr_epoch: ChainEpoch, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { if amount_to_spend.is_negative() { - return Err(anyhow!("amount to spend {} less than zero", amount_to_spend)); + return Err(actor_error!( + illegal_argument, + "amount to spend {} less than zero", + amount_to_spend + )); } if &balance < amount_to_spend { - return Err(anyhow!( + return Err(actor_error!( + insufficient_funds, "current balance {} less than amount to spend {}", balance, amount_to_spend @@ -131,7 +142,8 @@ impl State { let remaining_balance = balance - amount_to_spend; let amount_locked = self.amount_locked(curr_epoch - self.start_epoch); if remaining_balance < amount_locked { - return Err(anyhow!( + return Err(actor_error!( + insufficient_funds, "actor balance {} if spent {} would be less than required locked amount {}", remaining_balance, amount_to_spend, diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 462fce168..2c1adb30f 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -227,7 +227,7 @@ mod constructor_tests { ); rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( Method::Constructor as u64, &RawBytes::serialize(¶ms).unwrap(), diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index cd16267fd..226030a1f 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -75,12 +75,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let resolved = resolve_to_actor_id(rt, raw).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve address {} to ID", raw), - ) - })?; + let resolved = resolve_to_actor_id(rt, raw)?; let code_cid = rt .get_actor_code_cid(&resolved) diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index ffd633013..f250fe277 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -232,7 +232,7 @@ mod paych_constructor { &mut rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap(), - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, ); } @@ -269,7 +269,7 @@ mod paych_constructor { &mut rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap(), - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, ); } } diff --git a/actors/system/src/lib.rs b/actors/system/src/lib.rs index c9676b63d..f3a062791 100644 --- a/actors/system/src/lib.rs +++ b/actors/system/src/lib.rs @@ -1,6 +1,5 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::anyhow; use cid::{multihash, Cid}; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -12,13 +11,11 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorDowncast, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR}; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); -// * Updated to specs-actors commit: 845089a6d2580e46055c24415a6c32ee688e5186 (v3.0.0) - /// System actor methods. #[derive(FromPrimitive)] #[repr(u64)] @@ -35,10 +32,10 @@ pub struct State { impl Cbor for State {} impl State { - pub fn new(store: &BS) -> anyhow::Result { + pub fn new(store: &BS) -> Result { let c = store .put_cbor(&Vec::<(String, Cid)>::new(), multihash::Code::Blake2b256) - .map_err(|e| anyhow!("failed to put system state to store: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store system state")?; Ok(Self { builtin_actors: c }) } @@ -65,9 +62,7 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; - let state = State::new(rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") - })?; + let state = State::new(rt.store()).context("failed to construct state")?; rt.create(&state)?; Ok(()) } diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index a106fe373..4cd27ac34 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -79,12 +79,7 @@ impl Actor { )); } - let verifier = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; + let verifier = resolve_to_actor_id(rt, ¶ms.address)?; let verifier = Address::new_id(verifier); @@ -145,13 +140,7 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let verifier = resolve_to_actor_id(rt, &verifier_addr).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", verifier_addr), - ) - })?; - + let verifier = resolve_to_actor_id(rt, &verifier_addr)?; let verifier = Address::new_id(verifier); let state: State = rt.state()?; @@ -204,13 +193,7 @@ impl Actor { )); } - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); let st: State = rt.state()?; @@ -327,13 +310,7 @@ impl Actor { { rt.validate_immediate_caller_is(std::iter::once(&STORAGE_MARKET_ACTOR_ADDR))?; - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); if params.deal_size < rt.policy().minimum_verified_deal_size { @@ -437,13 +414,7 @@ impl Actor { )); } - let client = resolve_to_actor_id(rt, ¶ms.address).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to resolve addr {} to ID", params.address), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.address)?; let client = Address::new_id(client); let st: State = rt.state()?; @@ -520,39 +491,13 @@ impl Actor { BS: Blockstore, RT: Runtime, { - let client = resolve_to_actor_id(rt, ¶ms.verified_client_to_remove).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!("failed to resolve client addr {} to ID", params.verified_client_to_remove), - ) - })?; - + let client = resolve_to_actor_id(rt, ¶ms.verified_client_to_remove)?; let client = Address::new_id(client); - let verifier_1 = - resolve_to_actor_id(rt, ¶ms.verifier_request_1.verifier).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to resolve verifier addr {} to ID", - params.verifier_request_1.verifier - ), - ) - })?; - + let verifier_1 = resolve_to_actor_id(rt, ¶ms.verifier_request_1.verifier)?; let verifier_1 = Address::new_id(verifier_1); - let verifier_2 = - resolve_to_actor_id(rt, ¶ms.verifier_request_2.verifier).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to resolve verifier addr {} to ID", - params.verifier_request_2.verifier - ), - ) - })?; - + let verifier_2 = resolve_to_actor_id(rt, ¶ms.verifier_request_2.verifier)?; let verifier_2 = Address::new_id(verifier_2); if verifier_1 == verifier_2 { diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index 92b7d813a..8bbb13581 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -150,7 +150,7 @@ mod verifiers { ExitCode::OK, ); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, h.add_verifier(&mut rt, &verifier_key_address, &allowance), ); h.check_state(&rt); @@ -335,7 +335,7 @@ mod clients { ); expect_abort( - ExitCode::USR_ILLEGAL_STATE, + ExitCode::USR_ILLEGAL_ARGUMENT, h.add_client(&mut rt, &VERIFIER, &client, &allowance_client, &allowance_client), ); h.check_state(&rt); diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 899d63956..8ef17f147 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use fvm_shared::error::ExitCode; use thiserror::Error; @@ -108,3 +110,95 @@ macro_rules! actor_error { $crate::actor_error!($code; $msg $(, $ex)*) }; } + +// Adds context to an actor error's descriptive message. +pub trait ActorContext { + fn context(self, context: C) -> Result + where + C: Display + 'static; + + fn with_context(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; +} + +impl ActorContext for Result { + fn context(self, context: C) -> Result + where + C: Display + 'static, + { + self.map_err(|mut err| { + err.msg = format!("{}: {}", context, err.msg); + err + }) + } + + fn with_context(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.map_err(|mut err| { + err.msg = format!("{}: {}", f(), err.msg); + err + }) + } +} + +// Adapts a target into an actor error. +pub trait AsActorError: Sized { + fn exit_code(self, code: ExitCode) -> Result; + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static; + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; +} + +// Note: E should be std::error::Error, revert to this after anyhow:Error is no longer used. +impl AsActorError for Result { + fn exit_code(self, code: ExitCode) -> Result { + self.map_err(|err| ActorError { exit_code: code, msg: err.to_string() }) + } + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static, + { + self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", context, err) }) + } + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", f(), err) }) + } +} + +impl AsActorError for Option { + fn exit_code(self, code: ExitCode) -> Result { + self.ok_or_else(|| ActorError { exit_code: code, msg: "None".to_string() }) + } + + fn context_code(self, code: ExitCode, context: C) -> Result + where + C: Display + 'static, + { + self.ok_or_else(|| ActorError { exit_code: code, msg: context.to_string() }) + } + + fn with_context_code(self, code: ExitCode, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| ActorError { exit_code: code, msg: f().to_string() }) + } +} diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 4b5b0d14e..04555d20b 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -1,7 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::Ok; +use crate::{actor_error, ActorContext, ActorError}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::address::Address; use fvm_shared::ActorID; @@ -20,7 +20,7 @@ pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; /// ResolveToActorID resolves the given address to it's actor ID. /// If an actor ID for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. -pub fn resolve_to_actor_id(rt: &mut RT, address: &Address) -> anyhow::Result +pub fn resolve_to_actor_id(rt: &mut RT, address: &Address) -> Result where BS: Blockstore, RT: Runtime, @@ -32,14 +32,11 @@ where // send 0 balance to the account so an ID address for it is created and then try to resolve rt.send(address, METHOD_SEND, Default::default(), Default::default()) - .map_err(|e| e.wrap(&format!("failed to send zero balance to address {}", address)))?; + .with_context(|| format!("failed to send zero balance to address {}", address))?; if let Some(id) = rt.resolve_address(address) { return Ok(id); } - Err(anyhow::anyhow!( - "failed to resolve address {} to ID even after sending zero balance", - address, - )) + Err(actor_error!(illegal_argument, "failed to resolve or initialize address {}", address)) } From 99b7a4ca81c73359dd621e24cb72702c7b3879e4 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Wed, 21 Sep 2022 10:53:38 +0100 Subject: [PATCH 068/339] EVM-887: Storage footprint test contract (#689) * EVM-887: Makefile to generate Solidity compilation artifacts. * EVM-887: Use solc 0.8.15 * EVM-887: Storage test contract * EVM-887: Fix contract name. * EVM-887: Just return the sum without writing it to the counter. --- actors/evm/Makefile | 25 ++++++ actors/evm/README.md | 11 +++ actors/evm/tests/basic.rs | 2 +- actors/evm/tests/contracts/Selfdestruct.abi | 1 + actors/evm/tests/contracts/Selfdestruct.bin | 1 + .../tests/contracts/Selfdestruct.signatures | 2 + .../tests/contracts/Selfdestruct_storage.json | 1 + actors/evm/tests/contracts/SimpleCoin.abi | 1 + actors/evm/tests/contracts/SimpleCoin.bin | 1 + .../evm/tests/contracts/SimpleCoin.signatures | 7 ++ .../tests/contracts/SimpleCoin_storage.json | 1 + .../evm/tests/contracts/StorageFootprint.abi | 1 + .../evm/tests/contracts/StorageFootprint.bin | 1 + .../evm/tests/contracts/StorageFootprint.hex | 1 + .../contracts/StorageFootprint.signatures | 9 ++ .../evm/tests/contracts/StorageFootprint.sol | 90 +++++++++++++++++++ .../contracts/StorageFootprint_storage.json | 1 + .../tests/{ => contracts}/selfdestruct.hex | 2 +- actors/evm/tests/contracts/selfdestruct.sol | 10 +++ .../evm/tests/{ => contracts}/simplecoin.hex | 2 +- actors/evm/tests/contracts/simplecoin.sol | 31 +++++++ actors/evm/tests/selfdestruct.rs | 2 +- actors/evm/tests/selfdestruct.sol | 7 -- actors/evm/tests/simplecoin.sol | 28 ------ 24 files changed, 199 insertions(+), 39 deletions(-) create mode 100644 actors/evm/Makefile create mode 100644 actors/evm/README.md create mode 100644 actors/evm/tests/contracts/Selfdestruct.abi create mode 100644 actors/evm/tests/contracts/Selfdestruct.bin create mode 100644 actors/evm/tests/contracts/Selfdestruct.signatures create mode 100644 actors/evm/tests/contracts/Selfdestruct_storage.json create mode 100644 actors/evm/tests/contracts/SimpleCoin.abi create mode 100644 actors/evm/tests/contracts/SimpleCoin.bin create mode 100644 actors/evm/tests/contracts/SimpleCoin.signatures create mode 100644 actors/evm/tests/contracts/SimpleCoin_storage.json create mode 100644 actors/evm/tests/contracts/StorageFootprint.abi create mode 100644 actors/evm/tests/contracts/StorageFootprint.bin create mode 100644 actors/evm/tests/contracts/StorageFootprint.hex create mode 100644 actors/evm/tests/contracts/StorageFootprint.signatures create mode 100644 actors/evm/tests/contracts/StorageFootprint.sol create mode 100644 actors/evm/tests/contracts/StorageFootprint_storage.json rename actors/evm/tests/{ => contracts}/selfdestruct.hex (70%) create mode 100644 actors/evm/tests/contracts/selfdestruct.sol rename actors/evm/tests/{ => contracts}/simplecoin.hex (95%) create mode 100644 actors/evm/tests/contracts/simplecoin.sol delete mode 100644 actors/evm/tests/selfdestruct.sol delete mode 100644 actors/evm/tests/simplecoin.sol diff --git a/actors/evm/Makefile b/actors/evm/Makefile new file mode 100644 index 000000000..d0813d5bb --- /dev/null +++ b/actors/evm/Makefile @@ -0,0 +1,25 @@ +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) + +.PHONY: all +all: test-contracts + +.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 + +# 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 $< | tail -n +4 | tr -d '\n' > $@ + solc --bin --abi --storage-layout --hashes --overwrite $< -o $(TEST_CONTRACTS_DIR) 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/tests/basic.rs b/actors/evm/tests/basic.rs index 2b48aea0e..9a5dd3929 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -16,7 +16,7 @@ fn basic_contract_construction_and_invocation() { let contract = Address::new_id(100); let params = evm::ConstructorParams { - bytecode: hex::decode(include_str!("simplecoin.hex")).unwrap().into(), + bytecode: hex::decode(include_str!("contracts/simplecoin.hex")).unwrap().into(), }; // invoke constructor diff --git a/actors/evm/tests/contracts/Selfdestruct.abi b/actors/evm/tests/contracts/Selfdestruct.abi new file mode 100644 index 000000000..f50f1486a --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"die","outputs":[],"stateMutability":"nonpayable","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..14bba8f84 --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.bin @@ -0,0 +1 @@ +6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea264697066735822122036b3878598bb1b4b4b1c9f4835387cf15c45df30d5e4811ac8f52533a1ec3a4664736f6c634300080f0033 \ 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..27716607f --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.signatures @@ -0,0 +1,2 @@ +Function signatures: +35f46994: die() diff --git a/actors/evm/tests/contracts/Selfdestruct_storage.json b/actors/evm/tests/contracts/Selfdestruct_storage.json new file mode 100644 index 000000000..325d0043f --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct_storage.json @@ -0,0 +1 @@ +{"storage":[]} \ 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..d541d643d --- /dev/null +++ b/actors/evm/tests/contracts/SimpleCoin.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220bae09d124baa1fa30544534027cf2c2de125781c15a22bba297d7013f7c662b464736f6c634300080f0033 \ 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..42f788daf --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506107d2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105ee565b610171565b005b6100b16101ed565b005b6100cd60048036038101906100c8919061061b565b6102ee565b005b6100d761036a565b005b6100f360048036038101906100ee919061066e565b6103ab565b60405161010091906106bd565b60405180910390f35b610123600480360381019061011e919061061b565b610438565b005b61013f600480360381019061013a91906105ee565b6104b4565b005b61015b6004803603810190610156919061066e565b610530565b60405161016891906106bd565b60405180910390f35b60005b8163ffffffff168163ffffffff1610156101e95760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e190610707565b915050610174565b5050565b60016000808282829054906101000a900463ffffffff1661020e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff1661024e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff1661028e9190610733565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102ce9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103009190610733565b63ffffffff168163ffffffff1610156103645781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035c90610707565b9150506102f4565b50505050565b60016000808282829054906101000a900463ffffffff1661038b9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103be9190610733565b63ffffffff168163ffffffff1610156104315760018163ffffffff16815481106103eb576103ea61076d565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041c9190610733565b9150808061042990610707565b9150506103b2565b5092915050565b60008390505b828461044a9190610733565b63ffffffff168163ffffffff1610156104ae5781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a690610707565b91505061043e565b50505050565b60005b8163ffffffff168163ffffffff16101561052c5760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052490610707565b9150506104b7565b5050565b6000808390505b82846105439190610733565b63ffffffff168163ffffffff1610156105a657600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105919190610733565b9150808061059e90610707565b915050610537565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105cb816105b2565b81146105d657600080fd5b50565b6000813590506105e8816105c2565b92915050565b600060208284031215610604576106036105ad565b5b6000610612848285016105d9565b91505092915050565b600080600060608486031215610634576106336105ad565b5b6000610642868287016105d9565b9350506020610653868287016105d9565b9250506040610664868287016105d9565b9150509250925092565b60008060408385031215610685576106846105ad565b5b6000610693858286016105d9565b92505060206106a4858286016105d9565b9150509250929050565b6106b7816105b2565b82525050565b60006020820190506106d260008301846106ae565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610712826105b2565b915063ffffffff8203610728576107276106d8565b5b600182019050919050565b600061073e826105b2565b9150610749836105b2565b92508263ffffffff03821115610762576107616106d8565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea2646970667358221220b56ff0edba0990b947a69ab604b1e9f1c9a8932103796b2ab50b891f9aedaa2564736f6c634300080f0033 \ 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..42f788daf --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506107d2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105ee565b610171565b005b6100b16101ed565b005b6100cd60048036038101906100c8919061061b565b6102ee565b005b6100d761036a565b005b6100f360048036038101906100ee919061066e565b6103ab565b60405161010091906106bd565b60405180910390f35b610123600480360381019061011e919061061b565b610438565b005b61013f600480360381019061013a91906105ee565b6104b4565b005b61015b6004803603810190610156919061066e565b610530565b60405161016891906106bd565b60405180910390f35b60005b8163ffffffff168163ffffffff1610156101e95760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e190610707565b915050610174565b5050565b60016000808282829054906101000a900463ffffffff1661020e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff1661024e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff1661028e9190610733565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102ce9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103009190610733565b63ffffffff168163ffffffff1610156103645781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035c90610707565b9150506102f4565b50505050565b60016000808282829054906101000a900463ffffffff1661038b9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103be9190610733565b63ffffffff168163ffffffff1610156104315760018163ffffffff16815481106103eb576103ea61076d565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041c9190610733565b9150808061042990610707565b9150506103b2565b5092915050565b60008390505b828461044a9190610733565b63ffffffff168163ffffffff1610156104ae5781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a690610707565b91505061043e565b50505050565b60005b8163ffffffff168163ffffffff16101561052c5760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052490610707565b9150506104b7565b5050565b6000808390505b82846105439190610733565b63ffffffff168163ffffffff1610156105a657600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105919190610733565b9150808061059e90610707565b915050610537565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105cb816105b2565b81146105d657600080fd5b50565b6000813590506105e8816105c2565b92915050565b600060208284031215610604576106036105ad565b5b6000610612848285016105d9565b91505092915050565b600080600060608486031215610634576106336105ad565b5b6000610642868287016105d9565b9350506020610653868287016105d9565b9250506040610664868287016105d9565b9150509250925092565b60008060408385031215610685576106846105ad565b5b6000610693858286016105d9565b92505060206106a4858286016105d9565b9150509250929050565b6106b7816105b2565b82525050565b60006020820190506106d260008301846106ae565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610712826105b2565b915063ffffffff8203610728576107276106d8565b5b600182019050919050565b600061073e826105b2565b9150610749836105b2565b92508263ffffffff03821115610762576107616106d8565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea2646970667358221220b56ff0edba0990b947a69ab604b1e9f1c9a8932103796b2ab50b891f9aedaa2564736f6c634300080f0033 \ 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..929940446 --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.sol @@ -0,0 +1,90 @@ +// 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 { + for (uint32 i = 0; 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 = 0; 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/selfdestruct.hex b/actors/evm/tests/contracts/selfdestruct.hex similarity index 70% rename from actors/evm/tests/selfdestruct.hex rename to actors/evm/tests/contracts/selfdestruct.hex index 4e74e5897..14bba8f84 100644 --- a/actors/evm/tests/selfdestruct.hex +++ b/actors/evm/tests/contracts/selfdestruct.hex @@ -1 +1 @@ -6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212205539162e3dd1c84b3cf9043b55e3890bbe09c5726899ad6003650e8f1ac698b564736f6c63430008070033 \ No newline at end of file +6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea264697066735822122036b3878598bb1b4b4b1c9f4835387cf15c45df30d5e4811ac8f52533a1ec3a4664736f6c634300080f0033 \ 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..203873576 --- /dev/null +++ b/actors/evm/tests/contracts/selfdestruct.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Selfdestruct { + function die() public { + selfdestruct( + payable(address(0xFF000000000000000000000000000000000003E9)) + ); + } +} diff --git a/actors/evm/tests/simplecoin.hex b/actors/evm/tests/contracts/simplecoin.hex similarity index 95% rename from actors/evm/tests/simplecoin.hex rename to actors/evm/tests/contracts/simplecoin.hex index 7cdca3db7..d541d643d 100644 --- a/actors/evm/tests/simplecoin.hex +++ b/actors/evm/tests/contracts/simplecoin.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220d6b7446ff7b783a98dd1d68f516949438f2d578a05c05722e283b81a7774246564736f6c634300080e0033 \ No newline at end of file +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220bae09d124baa1fa30544534027cf2c2de125781c15a22bba297d7013f7c662b464736f6c634300080f0033 \ 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..dc4564075 --- /dev/null +++ b/actors/evm/tests/contracts/simplecoin.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.25 <=0.8.15; + +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/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index 3f5d85a51..721f9de93 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -11,7 +11,7 @@ fn test_selfdestruct() { let beneficiary = Address::new_id(1001); let params = evm::ConstructorParams { - bytecode: hex::decode(include_str!("selfdestruct.hex")).unwrap().into(), + bytecode: hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap().into(), }; // invoke constructor diff --git a/actors/evm/tests/selfdestruct.sol b/actors/evm/tests/selfdestruct.sol deleted file mode 100644 index 9bb0fda5b..000000000 --- a/actors/evm/tests/selfdestruct.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma solidity ^0.8.0; - -contract Selfdestruct { - function die() public { - selfdestruct(payable(address(0xFF000000000000000000000000000000000003E9))); - } -} \ No newline at end of file diff --git a/actors/evm/tests/simplecoin.sol b/actors/evm/tests/simplecoin.sol deleted file mode 100644 index 6ea41c76d..000000000 --- a/actors/evm/tests/simplecoin.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.25 <= 0.8.15; - -contract SimpleCoin { - mapping (address => uint) balances; - - event Transfer(address indexed _from, address indexed _to, uint256 _value); - - constructor() { - balances[tx.origin] = 10000; - } - - function sendCoin(address receiver, uint 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(uint){ - return getBalance(addr) * 2; - } - - function getBalance(address addr) public view returns(uint) { - return balances[addr]; - } -} From 4f700837f262d5018cbfd893d7a187c112215592 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 22 Sep 2022 01:26:20 -0700 Subject: [PATCH 069/339] fix: keep the embryo balance on deploy (#694) --- test_vm/src/lib.rs | 14 ++++++++------ test_vm/tests/init_test.rs | 6 ++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 961ef1686..0b0d19d7e 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -691,9 +691,12 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat } } let addr = Address::new_id(actor_id); - match self.v.get_actor(addr) { - Some(act) if act.code == *EMBRYO_ACTOR_CODE_ID => (), - None => (), + let actor = match self.v.get_actor(addr) { + Some(mut act) if act.code == *EMBRYO_ACTOR_CODE_ID => { + act.code = code_id; + act + } + None => actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero()), _ => { // can happen if an actor is deployed to an f4 address. return Err(ActorError::unchecked( @@ -701,9 +704,8 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat "attempt to create new actor at existing address".to_string(), )); } - } - let a = actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero()); - self.v.set_actor(addr, a); + }; + self.v.set_actor(addr, actor); Ok(()) } diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs index 7d1c2409c..ae9b5a440 100644 --- a/test_vm/tests/init_test.rs +++ b/test_vm/tests/init_test.rs @@ -47,6 +47,9 @@ fn embryo_deploy() { let expect_id_addr = Address::new_id(FIRST_TEST_USER_ADDR); assert_embryo_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 embryo. let msig_ctor_params = serialize( &fil_actor_multisig::ConstructorParams { @@ -83,6 +86,9 @@ fn embryo_deploy() { "expected actor to be deployed over embryo" ); + // 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); From 2070f4d3539c901093d74644b71c8f7fea6b184c Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Sat, 1 Oct 2022 12:23:55 +0100 Subject: [PATCH 070/339] EVM-888: Storage footprint baseline test (#697) * EVM-888: ABI bindings and a default instance of it. * EVM-888: TestEnv with deploy. * EVM-888: Test that a call can be invoked and the results parsed. * EVM-888: Move TestEnv out. * EVM-888: Static init for contract. * EVM-888: Capture storage stats. * EVM-888: Push at least 1, otherwise storage size is constant. * EVM-888: Export stats. * EVM-888: Add two series. * EVM-888: Fix clippy warnings. * EVM-888: Test mapping add. * EVM-888: Measure array read. * EVM-888: Change i to go from 0 instead of 1 * EVM-888: Measure mapping read. * EVM-888: Assert sums. * EVM-888: Counter increments. * EVM-888: Counter after fill. * EVM-888: Just consume the call, lose the closure. * EVM-888: Overwrite * EVM-888: Rename back to inc * EVM-888: Use the existing TrackingBlockstore * EVM-888: Undo line breaks in toml * EVM-888: Use std::env! * EVM-888: Convert to EthAddress first. --- Cargo.lock | 1812 ++++++++++++++++- actors/evm/Cargo.toml | 2 + actors/evm/src/interpreter/address.rs | 6 + .../evm/tests/contracts/StorageFootprint.bin | 2 +- .../evm/tests/contracts/StorageFootprint.hex | 2 +- .../evm/tests/contracts/StorageFootprint.sol | 5 +- actors/evm/tests/env.rs | 75 + .../tests/measurements/array_push_n1.jsonline | 200 ++ .../measurements/array_push_n100.jsonline | 200 ++ .../tests/measurements/array_read_n1.jsonline | 100 + .../measurements/array_read_n100.jsonline | 100 + .../measurements/inc_after_fill.jsonline | 200 ++ .../measurements/inc_one_vs_all.jsonline | 20 + .../measurements/mapping_add_n1.jsonline | 200 ++ .../measurements/mapping_add_n100.jsonline | 200 ++ .../measurements/mapping_overwrite.jsonline | 200 ++ .../measurements/mapping_read_n1.jsonline | 100 + .../measurements/mapping_read_n100.jsonline | 100 + actors/evm/tests/storage_footprint.rs | 243 +++ runtime/src/test_utils.rs | 28 +- 20 files changed, 3726 insertions(+), 69 deletions(-) create mode 100644 actors/evm/tests/env.rs create mode 100644 actors/evm/tests/measurements/array_push_n1.jsonline create mode 100644 actors/evm/tests/measurements/array_push_n100.jsonline create mode 100644 actors/evm/tests/measurements/array_read_n1.jsonline create mode 100644 actors/evm/tests/measurements/array_read_n100.jsonline create mode 100644 actors/evm/tests/measurements/inc_after_fill.jsonline create mode 100644 actors/evm/tests/measurements/inc_one_vs_all.jsonline create mode 100644 actors/evm/tests/measurements/mapping_add_n1.jsonline create mode 100644 actors/evm/tests/measurements/mapping_add_n100.jsonline create mode 100644 actors/evm/tests/measurements/mapping_overwrite.jsonline create mode 100644 actors/evm/tests/measurements/mapping_read_n1.jsonline create mode 100644 actors/evm/tests/measurements/mapping_read_n100.jsonline create mode 100644 actors/evm/tests/storage_footprint.rs diff --git a/Cargo.lock b/Cargo.lock index beeeeb29c..09a90e811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,28 @@ # 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", + "cpufeatures", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.6" @@ -155,6 +177,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.0.0" @@ -172,6 +205,18 @@ dependencies = [ "winapi", ] +[[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" @@ -184,24 +229,104 @@ 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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest 0.10.5", +] + [[package]] name = "blake2b_simd" version = "1.0.0" @@ -237,13 +362,25 @@ dependencies = [ "constant_time_eq", ] +[[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]] @@ -252,7 +389,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]] @@ -269,12 +415,30 @@ dependencies = [ "once_cell", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + [[package]] name = "bumpalo" version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "byte-slice-cast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.4.3" @@ -296,6 +460,37 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +[[package]] +name = "camino" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cbor4ii" version = "0.2.13" @@ -317,6 +512,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "cid" version = "0.8.6" @@ -331,6 +536,15 @@ 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 = "clap" version = "3.2.22" @@ -370,6 +584,63 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "coins-bip32" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" +dependencies = [ + "bincode", + "bs58", + "coins-core", + "digest 0.10.5", + "getrandom", + "hmac", + "k256", + "lazy_static", + "serde", + "sha2 0.10.6", + "thiserror", +] + +[[package]] +name = "coins-bip39" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" +dependencies = [ + "bitvec 0.17.4", + "coins-bip32", + "getrandom", + "hex", + "hmac", + "pbkdf2 0.11.0", + "rand", + "sha2 0.10.6", + "thiserror", +] + +[[package]] +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.5", + "generic-array 0.14.6", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.6", + "sha3", + "thiserror", +] + [[package]] name = "concurrent-queue" version = "1.2.4" @@ -379,6 +650,12 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "const-oid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -391,6 +668,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + [[package]] name = "core2" version = "0.4.0" @@ -425,13 +708,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2b443d17d49dad5ef0ede301c3179cc923b8822f3393b4d2c28c269dd4a122" +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", ] @@ -441,7 +736,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array", + "generic-array 0.14.6", "subtle", ] @@ -464,6 +759,15 @@ dependencies = [ "syn", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + [[package]] name = "darling" version = "0.12.4" @@ -525,6 +829,16 @@ dependencies = [ "syn", ] +[[package]] +name = "der" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_builder" version = "0.10.2" @@ -562,20 +876,29 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", "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]] @@ -586,6 +909,7 @@ checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer 0.10.3", "crypto-common", + "subtle", ] [[package]] @@ -595,59 +919,434 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "either" -version = "1.8.0" +name = "dunce" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" [[package]] -name = "etk-asm" -version = "0.2.1" +name = "ecdsa" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f930978593dfe8eb61901b68b083f5b08898f45ed7387ca627ede2f036b426a" +checksum = "85789ce7dfbd0f0624c07ef653a08bb2ebf43d3e16531361f46d36dd54334fed" dependencies = [ - "hex", - "num-bigint", - "pest", - "pest_derive", - "rand", - "sha3", - "snafu", + "der", + "elliptic-curve", + "rfc6979", + "signature", ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "either" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] -name = "fastrand" -version = "1.8.0" +name = "elliptic-curve" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "instant", + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.5", + "ff", + "generic-array 0.14.6", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", ] [[package]] -name = "fil_actor_account" -version = "10.0.0-alpha.1" +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ - "fil_actors_runtime", - "fvm_ipld_blockstore", - "fvm_ipld_encoding", - "fvm_shared", - "num-derive", - "num-traits", - "serde", + "cfg-if", ] [[package]] -name = "fil_actor_bundler" -version = "4.0.0" +name = "eth-keystore" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1e563d609dede95fb0a847ea4ebfe71f5d7074703fc3e92736c74be4b237c2" +checksum = "6f65b750ac950f2f825b36d08bef4cda4112e19a7b1a68f6e2bb499413e12440" +dependencies = [ + "aes", + "ctr", + "digest 0.10.5", + "hex", + "hmac", + "pbkdf2 0.11.0", + "rand", + "scrypt", + "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", + "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", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "ethers" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16142eeb3155cfa5aec6be3f828a28513a28bd995534f945fa70e7d608f16c10" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", +] + +[[package]] +name = "ethers-addressbook" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23f8992ecf45ea9dd2983696aabc566c108723585f07f5dc8c9efb24e52d3db" +dependencies = [ + "ethers-core", + "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", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "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", + "eyre", + "getrandom", + "hex", + "proc-macro2", + "quote", + "reqwest", + "serde", + "serde_json", + "syn", + "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", + "ethers-core", + "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", + "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-etherscan" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b279a3d00bd219caa2f9a34451b4accbfa9a1eaafc26dcda9d572591528435f0" +dependencies = [ + "ethers-core", + "getrandom", + "reqwest", + "semver", + "serde", + "serde-aux", + "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", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "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", + "base64 0.13.0", + "ethers-core", + "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", + "ethers-core", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +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", + "bytes", + "ethereum-types", + "fastrlp-derive", +] + +[[package]] +name = "fastrlp-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fa41ebc231af281098b11ad4a4f6182ec9096902afffe948034a20d4e1385a" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ff" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fil_actor_account" +version = "10.0.0-alpha.1" +dependencies = [ + "fil_actors_runtime", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "num-derive", + "num-traits", + "serde", +] + +[[package]] +name = "fil_actor_bundler" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1e563d609dede95fb0a847ea4ebfe71f5d7074703fc3e92736c74be4b237c2" dependencies = [ "anyhow", "async-std", @@ -693,6 +1392,7 @@ dependencies = [ "bytes", "cid", "derive_more", + "ethers", "etk-asm", "fil_actors_runtime", "fixed-hash", @@ -711,6 +1411,7 @@ dependencies = [ "num-traits", "rlp", "serde", + "serde_json", "serde_tuple", "sha3", "strum", @@ -985,6 +1686,9 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ + "byteorder", + "rand", + "rustc-hex", "static_assertions", ] @@ -1004,6 +1708,21 @@ 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 = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.24" @@ -1067,6 +1786,17 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-locks" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb42d4fb72227be5778429f9ef5240a38a358925a49f05b5cf702ce7c7e558a" +dependencies = [ + "futures-channel", + "futures-task", + "tokio", +] + [[package]] name = "futures-macro" version = "0.3.24" @@ -1090,6 +1820,12 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +[[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.24" @@ -1247,6 +1983,24 @@ dependencies = [ "unsigned-varint", ] +[[package]] +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" @@ -1264,8 +2018,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1280,12 +2036,51 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "group" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7391856def869c1c81063a03457c676fbcd419709c3dfb33d8d319de484b154d" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[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" @@ -1313,6 +2108,86 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", +] + +[[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 = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1320,13 +2195,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "impl-serde" -version = "0.3.2" +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" @@ -1346,6 +2266,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1358,6 +2281,12 @@ dependencies = [ "futures-util", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itertools" version = "0.10.4" @@ -1382,6 +2311,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3636d281d46c3b64182eb3a0a42b7b483191a2ecc3f05301fa67403f7c9bc949" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", + "sha3", +] + [[package]] name = "keccak" version = "0.1.2" @@ -1434,7 +2376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64", + "base64 0.13.0", "digest 0.9.0", "libsecp256k1-core", "libsecp256k1-gen-ecmult", @@ -1473,6 +2415,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" @@ -1489,6 +2441,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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "multibase" version = "0.9.1" @@ -1541,7 +2511,7 @@ source = "git+https://github.com/filecoin-project/near-blake2.git#47a58e5061ba6d dependencies = [ "crypto-mac", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -1631,12 +2601,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +[[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" @@ -1649,18 +2635,118 @@ version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +[[package]] +name = "parity-scale-codec" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +dependencies = [ + "proc-macro-crate", + "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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +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.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest 0.10.5", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.5", + "hmac", + "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.3.1" @@ -1705,6 +2791,36 @@ dependencies = [ "sha1", ] +[[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" @@ -1717,6 +2833,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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" version = "2.3.0" @@ -1737,6 +2863,19 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "primitive-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] + [[package]] name = "proc-macro-crate" version = "1.2.1" @@ -1790,6 +2929,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" @@ -1820,6 +2971,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.6.0" @@ -1837,6 +2997,71 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64 0.13.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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +dependencies = [ + "crypto-bigint", + "hmac", + "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.2" @@ -1856,6 +3081,28 @@ 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.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +dependencies = [ + "arrayvec", + "num-traits", + "serde", +] + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1871,29 +3118,114 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64 0.13.0", +] + [[package]] name = "rustversion" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "salsa20" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +dependencies = [ + "cipher", +] + +[[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 = "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", + "password-hash 0.3.2", + "pbkdf2 0.10.1", + "salsa20", + "sha2 0.10.6", +] [[package]] -name = "ryu" -version = "1.0.11" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "sec1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.6", + "pkcs8", + "subtle", + "zeroize", +] [[package]] name = "semver" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +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" @@ -1904,6 +3236,16 @@ 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-big-array" version = "0.3.3" @@ -1988,6 +3330,18 @@ dependencies = [ "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 = "sha1" version = "0.10.5" @@ -1999,6 +3353,18 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -2009,7 +3375,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -2033,6 +3399,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb766570a2825fa972bceff0d195727876a9cdf2460ab2e52d455dc2de47fd9" +dependencies = [ + "digest 0.10.5", + "rand_core", +] + [[package]] name = "slab" version = "0.4.7" @@ -2042,6 +3418,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + [[package]] name = "snafu" version = "0.7.1" @@ -2080,6 +3462,16 @@ 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" @@ -2097,6 +3489,9 @@ 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" @@ -2153,6 +3548,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.1.3" @@ -2229,6 +3630,73 @@ 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.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "socket2", + "winapi", +] + +[[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.9" @@ -2238,6 +3706,60 @@ 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.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "typenum" version = "1.15.0" @@ -2262,12 +3784,27 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[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-xid" version = "0.2.4" @@ -2280,6 +3817,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" @@ -2302,6 +3866,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" @@ -2374,6 +3959,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" @@ -2384,6 +3984,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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +dependencies = [ + "webpki", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -2423,3 +4042,88 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 338c45fb2..3cc4fe6a2 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -46,6 +46,8 @@ lazy_static = "1.4.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } etk-asm = "^0.2.1" +ethers = { version = "0.17.0", features = ["abigen"] } +serde_json = "1.0" [features] fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index 92eb1e6a0..22ff74d18 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -65,6 +65,12 @@ impl EthAddress { } } +impl AsRef<[u8]> for EthAddress { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + #[cfg(test)] mod tests { use crate::interpreter::address::EthAddress; diff --git a/actors/evm/tests/contracts/StorageFootprint.bin b/actors/evm/tests/contracts/StorageFootprint.bin index 42f788daf..2eabe041f 100644 --- a/actors/evm/tests/contracts/StorageFootprint.bin +++ b/actors/evm/tests/contracts/StorageFootprint.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b506107d2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105ee565b610171565b005b6100b16101ed565b005b6100cd60048036038101906100c8919061061b565b6102ee565b005b6100d761036a565b005b6100f360048036038101906100ee919061066e565b6103ab565b60405161010091906106bd565b60405180910390f35b610123600480360381019061011e919061061b565b610438565b005b61013f600480360381019061013a91906105ee565b6104b4565b005b61015b6004803603810190610156919061066e565b610530565b60405161016891906106bd565b60405180910390f35b60005b8163ffffffff168163ffffffff1610156101e95760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e190610707565b915050610174565b5050565b60016000808282829054906101000a900463ffffffff1661020e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff1661024e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff1661028e9190610733565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102ce9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103009190610733565b63ffffffff168163ffffffff1610156103645781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035c90610707565b9150506102f4565b50505050565b60016000808282829054906101000a900463ffffffff1661038b9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103be9190610733565b63ffffffff168163ffffffff1610156104315760018163ffffffff16815481106103eb576103ea61076d565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041c9190610733565b9150808061042990610707565b9150506103b2565b5092915050565b60008390505b828461044a9190610733565b63ffffffff168163ffffffff1610156104ae5781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a690610707565b91505061043e565b50505050565b60005b8163ffffffff168163ffffffff16101561052c5760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052490610707565b9150506104b7565b5050565b6000808390505b82846105439190610733565b63ffffffff168163ffffffff1610156105a657600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105919190610733565b9150808061059e90610707565b915050610537565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105cb816105b2565b81146105d657600080fd5b50565b6000813590506105e8816105c2565b92915050565b600060208284031215610604576106036105ad565b5b6000610612848285016105d9565b91505092915050565b600080600060608486031215610634576106336105ad565b5b6000610642868287016105d9565b9350506020610653868287016105d9565b9250506040610664868287016105d9565b9150509250925092565b60008060408385031215610685576106846105ad565b5b6000610693858286016105d9565b92505060206106a4858286016105d9565b9150509250929050565b6106b7816105b2565b82525050565b60006020820190506106d260008301846106ae565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610712826105b2565b915063ffffffff8203610728576107276106d8565b5b600182019050919050565b600061073e826105b2565b9150610749836105b2565b92508263ffffffff03821115610762576107616106d8565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea2646970667358221220b56ff0edba0990b947a69ab604b1e9f1c9a8932103796b2ab50b891f9aedaa2564736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506107d8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610773565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b92508263ffffffff03821115610768576107676106de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212204627e855ec638124e86ac435a22bc2e64114895f4cfa3ad1db43962f0273c67a64736f6c634300080f0033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.hex b/actors/evm/tests/contracts/StorageFootprint.hex index 42f788daf..2eabe041f 100644 --- a/actors/evm/tests/contracts/StorageFootprint.hex +++ b/actors/evm/tests/contracts/StorageFootprint.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506107d2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105ee565b610171565b005b6100b16101ed565b005b6100cd60048036038101906100c8919061061b565b6102ee565b005b6100d761036a565b005b6100f360048036038101906100ee919061066e565b6103ab565b60405161010091906106bd565b60405180910390f35b610123600480360381019061011e919061061b565b610438565b005b61013f600480360381019061013a91906105ee565b6104b4565b005b61015b6004803603810190610156919061066e565b610530565b60405161016891906106bd565b60405180910390f35b60005b8163ffffffff168163ffffffff1610156101e95760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e190610707565b915050610174565b5050565b60016000808282829054906101000a900463ffffffff1661020e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff1661024e9190610733565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff1661028e9190610733565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102ce9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103009190610733565b63ffffffff168163ffffffff1610156103645781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035c90610707565b9150506102f4565b50505050565b60016000808282829054906101000a900463ffffffff1661038b9190610733565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103be9190610733565b63ffffffff168163ffffffff1610156104315760018163ffffffff16815481106103eb576103ea61076d565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041c9190610733565b9150808061042990610707565b9150506103b2565b5092915050565b60008390505b828461044a9190610733565b63ffffffff168163ffffffff1610156104ae5781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a690610707565b91505061043e565b50505050565b60005b8163ffffffff168163ffffffff16101561052c5760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052490610707565b9150506104b7565b5050565b6000808390505b82846105439190610733565b63ffffffff168163ffffffff1610156105a657600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105919190610733565b9150808061059e90610707565b915050610537565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105cb816105b2565b81146105d657600080fd5b50565b6000813590506105e8816105c2565b92915050565b600060208284031215610604576106036105ad565b5b6000610612848285016105d9565b91505092915050565b600080600060608486031215610634576106336105ad565b5b6000610642868287016105d9565b9350506020610653868287016105d9565b9250506040610664868287016105d9565b9150509250925092565b60008060408385031215610685576106846105ad565b5b6000610693858286016105d9565b92505060206106a4858286016105d9565b9150509250929050565b6106b7816105b2565b82525050565b60006020820190506106d260008301846106ae565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610712826105b2565b915063ffffffff8203610728576107276106d8565b5b600182019050919050565b600061073e826105b2565b9150610749836105b2565b92508263ffffffff03821115610762576107616106d8565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea2646970667358221220b56ff0edba0990b947a69ab604b1e9f1c9a8932103796b2ab50b891f9aedaa2564736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506107d8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610773565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b92508263ffffffff03821115610768576107676106de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212204627e855ec638124e86ac435a22bc2e64114895f4cfa3ad1db43962f0273c67a64736f6c634300080f0033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.sol b/actors/evm/tests/contracts/StorageFootprint.sol index 929940446..d72bf5aeb 100644 --- a/actors/evm/tests/contracts/StorageFootprint.sol +++ b/actors/evm/tests/contracts/StorageFootprint.sol @@ -34,14 +34,15 @@ contract StorageFootprint { // 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 { - for (uint32 i = 0; i < n; i++) { + // 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 = 0; i < n; i++) { + for (uint32 i = 1; i <= n; i++) { array2.push(i); } } diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs new file mode 100644 index 000000000..385f1f41a --- /dev/null +++ b/actors/evm/tests/env.rs @@ -0,0 +1,75 @@ +use ethers::{ + abi::Detokenize, + prelude::{builders::ContractCall, decode_function_data}, + providers::{MockProvider, Provider}, +}; +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, EVM_ACTOR_CODE_ID}; +use fvm_ipld_blockstore::tracking::{BSStats, TrackingBlockstore}; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::RawBytes; +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 { bytecode: hex::decode(contract_hex).unwrap().into() }; + // invoke constructor + self.runtime.expect_validate_caller_any(); + self.runtime.set_origin(self.evm_address); + + let result = self + .runtime + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + expect_empty(result); + + 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 = RawBytes::from(input.to_vec()); + self.runtime.expect_validate_caller_any(); + + let result = self + .runtime + .call::(evm::Method::InvokeContract as u64, &input) + .unwrap(); + + decode_function_data(&call.function, result.bytes(), false).unwrap() + } +} 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..21a8dcc60 --- /dev/null +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2286,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2283,"get_count":4,"put_bytes":196,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2291,"get_count":4,"put_bytes":204,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2299,"get_count":4,"put_bytes":213,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2308,"get_count":4,"put_bytes":221,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2316,"get_count":4,"put_bytes":229,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2324,"get_count":4,"put_bytes":237,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2332,"get_count":4,"put_bytes":245,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2340,"get_count":4,"put_bytes":333,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2428,"get_count":4,"put_bytes":341,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2436,"get_count":4,"put_bytes":349,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2444,"get_count":4,"put_bytes":358,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2453,"get_count":4,"put_bytes":366,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2461,"get_count":4,"put_bytes":374,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":382,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":391,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":2486,"get_count":4,"put_bytes":465,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":2560,"get_count":4,"put_bytes":473,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2568,"get_count":4,"put_bytes":481,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2576,"get_count":4,"put_bytes":490,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":2585,"get_count":4,"put_bytes":498,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2593,"get_count":4,"put_bytes":506,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2601,"get_count":4,"put_bytes":514,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2609,"get_count":4,"put_bytes":522,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":2617,"get_count":4,"put_bytes":596,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":2691,"get_count":4,"put_bytes":604,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":2699,"get_count":4,"put_bytes":612,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":2707,"get_count":4,"put_bytes":621,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":2716,"get_count":4,"put_bytes":629,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2724,"get_count":4,"put_bytes":637,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2732,"get_count":4,"put_bytes":645,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":2740,"get_count":4,"put_bytes":653,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":2748,"get_count":4,"put_bytes":726,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":2821,"get_count":4,"put_bytes":734,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":2829,"get_count":4,"put_bytes":742,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":2837,"get_count":4,"put_bytes":751,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":2846,"get_count":4,"put_bytes":759,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":2854,"get_count":4,"put_bytes":767,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":2862,"get_count":4,"put_bytes":775,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":2870,"get_count":4,"put_bytes":783,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":2878,"get_count":4,"put_bytes":857,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":2952,"get_count":4,"put_bytes":865,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":2960,"get_count":4,"put_bytes":873,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":2968,"get_count":4,"put_bytes":882,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":2977,"get_count":4,"put_bytes":890,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":2985,"get_count":4,"put_bytes":898,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":2993,"get_count":4,"put_bytes":906,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":3001,"get_count":4,"put_bytes":914,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":3009,"get_count":4,"put_bytes":989,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3084,"get_count":4,"put_bytes":997,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":3092,"get_count":4,"put_bytes":1005,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3100,"get_count":4,"put_bytes":1014,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":3109,"get_count":4,"put_bytes":1022,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3117,"get_count":4,"put_bytes":1030,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":3125,"get_count":4,"put_bytes":1038,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":3133,"get_count":4,"put_bytes":1046,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":3141,"get_count":4,"put_bytes":1120,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3215,"get_count":4,"put_bytes":1128,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3223,"get_count":4,"put_bytes":1136,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":3231,"get_count":4,"put_bytes":1145,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":3240,"get_count":4,"put_bytes":1153,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3248,"get_count":4,"put_bytes":1161,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":3256,"get_count":4,"put_bytes":1169,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3264,"get_count":4,"put_bytes":1177,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":3272,"get_count":4,"put_bytes":1251,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":3346,"get_count":4,"put_bytes":1259,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":3354,"get_count":4,"put_bytes":1267,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":3362,"get_count":4,"put_bytes":1276,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":3371,"get_count":4,"put_bytes":1284,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":3379,"get_count":4,"put_bytes":1292,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":3387,"get_count":4,"put_bytes":1300,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":3395,"get_count":4,"put_bytes":1308,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":3403,"get_count":4,"put_bytes":1382,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":3477,"get_count":4,"put_bytes":1390,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":3485,"get_count":4,"put_bytes":1398,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":3493,"get_count":4,"put_bytes":1407,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":3502,"get_count":4,"put_bytes":1415,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":3510,"get_count":4,"put_bytes":1423,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":3518,"get_count":4,"put_bytes":1431,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":3526,"get_count":4,"put_bytes":1439,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":3534,"get_count":4,"put_bytes":1513,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":3608,"get_count":4,"put_bytes":1521,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":3616,"get_count":4,"put_bytes":1529,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":3624,"get_count":4,"put_bytes":1538,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":3633,"get_count":4,"put_bytes":1546,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":3641,"get_count":4,"put_bytes":1554,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":3649,"get_count":4,"put_bytes":1562,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":3657,"get_count":4,"put_bytes":1570,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":3665,"get_count":4,"put_bytes":1644,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":3739,"get_count":4,"put_bytes":1652,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1660,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":3755,"get_count":4,"put_bytes":1669,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":3764,"get_count":4,"put_bytes":1677,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":3772,"get_count":4,"put_bytes":1685,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1693,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":3788,"get_count":4,"put_bytes":1701,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":3796,"get_count":4,"put_bytes":1775,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1783,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1791,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":3886,"get_count":4,"put_bytes":1800,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":3895,"get_count":4,"put_bytes":1884,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3979,"get_count":4,"put_bytes":1892,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3987,"get_count":4,"put_bytes":1900,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":1909,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":4004,"get_count":4,"put_bytes":1917,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":4012,"get_count":4,"put_bytes":1925,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":4020,"get_count":4,"put_bytes":1933,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":4028,"get_count":4,"put_bytes":1941,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":4036,"get_count":4,"put_bytes":2015,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":4110,"get_count":4,"put_bytes":2023,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":4118,"get_count":4,"put_bytes":2031,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":4126,"get_count":4,"put_bytes":2040,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":4135,"get_count":4,"put_bytes":2048,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":4143,"get_count":4,"put_bytes":2056,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":4151,"get_count":4,"put_bytes":2064,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":4159,"get_count":4,"put_bytes":2073,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":4168,"get_count":4,"put_bytes":2146,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":4241,"get_count":4,"put_bytes":2154,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":4249,"get_count":4,"put_bytes":2162,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2171,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2179,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":4274,"get_count":4,"put_bytes":2187,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":4282,"get_count":4,"put_bytes":2195,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":4290,"get_count":4,"put_bytes":2203,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":4298,"get_count":4,"put_bytes":2277,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":4372,"get_count":4,"put_bytes":2285,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":4380,"get_count":4,"put_bytes":2293,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":4388,"get_count":4,"put_bytes":2302,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":4397,"get_count":4,"put_bytes":2310,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2318,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":4413,"get_count":4,"put_bytes":2326,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":4421,"get_count":4,"put_bytes":2334,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":4429,"get_count":4,"put_bytes":2408,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":4503,"get_count":4,"put_bytes":2416,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":4511,"get_count":4,"put_bytes":2424,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":4519,"get_count":4,"put_bytes":2433,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":4528,"get_count":4,"put_bytes":2441,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":4536,"get_count":4,"put_bytes":2449,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":4544,"get_count":4,"put_bytes":2457,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":4552,"get_count":4,"put_bytes":2465,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":4560,"get_count":4,"put_bytes":2539,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":4634,"get_count":4,"put_bytes":2547,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":4642,"get_count":4,"put_bytes":2555,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":4650,"get_count":4,"put_bytes":2564,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":4659,"get_count":4,"put_bytes":2572,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":4667,"get_count":4,"put_bytes":2580,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":4675,"get_count":4,"put_bytes":2588,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":4683,"get_count":4,"put_bytes":2596,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":4691,"get_count":4,"put_bytes":2672,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":4767,"get_count":4,"put_bytes":2680,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":4775,"get_count":4,"put_bytes":2688,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":4783,"get_count":4,"put_bytes":2697,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":4792,"get_count":4,"put_bytes":2705,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":4800,"get_count":4,"put_bytes":2713,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":4808,"get_count":4,"put_bytes":2721,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":4816,"get_count":4,"put_bytes":2729,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":4824,"get_count":4,"put_bytes":2803,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":4898,"get_count":4,"put_bytes":2811,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":4906,"get_count":4,"put_bytes":2819,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":4914,"get_count":4,"put_bytes":2828,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":4923,"get_count":4,"put_bytes":2836,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":4931,"get_count":4,"put_bytes":2844,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":4939,"get_count":4,"put_bytes":2852,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":4947,"get_count":4,"put_bytes":2860,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":4955,"get_count":4,"put_bytes":2934,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":5029,"get_count":4,"put_bytes":2942,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":5037,"get_count":4,"put_bytes":2950,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":5045,"get_count":4,"put_bytes":2959,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":5054,"get_count":4,"put_bytes":2967,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":5062,"get_count":4,"put_bytes":2975,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":5070,"get_count":4,"put_bytes":2983,"put_count":2}} +{"i":71,"series":2,"stats":{"get_bytes":5078,"get_count":4,"put_bytes":2991,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":5086,"get_count":4,"put_bytes":3065,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":5160,"get_count":4,"put_bytes":3073,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":5168,"get_count":4,"put_bytes":3081,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":5176,"get_count":4,"put_bytes":3090,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":5185,"get_count":4,"put_bytes":3098,"put_count":2}} +{"i":77,"series":2,"stats":{"get_bytes":5193,"get_count":4,"put_bytes":3106,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":5201,"get_count":4,"put_bytes":3114,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":5209,"get_count":4,"put_bytes":3122,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":5217,"get_count":4,"put_bytes":3197,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":5292,"get_count":4,"put_bytes":3205,"put_count":2}} +{"i":82,"series":2,"stats":{"get_bytes":5300,"get_count":4,"put_bytes":3213,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":5308,"get_count":4,"put_bytes":3222,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":5317,"get_count":4,"put_bytes":3230,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":5325,"get_count":4,"put_bytes":3238,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":5333,"get_count":4,"put_bytes":3246,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":5341,"get_count":4,"put_bytes":3254,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":5349,"get_count":4,"put_bytes":3327,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":5422,"get_count":4,"put_bytes":3335,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":5430,"get_count":4,"put_bytes":3343,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":5438,"get_count":4,"put_bytes":3352,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":5447,"get_count":4,"put_bytes":3360,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":5455,"get_count":4,"put_bytes":3368,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":5463,"get_count":4,"put_bytes":3376,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":5471,"get_count":4,"put_bytes":3384,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":5479,"get_count":4,"put_bytes":3458,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":5553,"get_count":4,"put_bytes":3466,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":5561,"get_count":4,"put_bytes":3474,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":5569,"get_count":4,"put_bytes":3483,"put_count":2}} 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..17e6151a0 --- /dev/null +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":3910,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":3907,"get_count":4,"put_bytes":3428,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":5523,"get_count":4,"put_bytes":5113,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":7208,"get_count":4,"put_bytes":6723,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":8818,"get_count":4,"put_bytes":8405,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":10500,"get_count":4,"put_bytes":10019,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":12114,"get_count":4,"put_bytes":11697,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":13792,"get_count":4,"put_bytes":13309,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":15404,"get_count":4,"put_bytes":14987,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":17082,"get_count":4,"put_bytes":16742,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":17727,"get_count":4,"put_bytes":17308,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":19403,"get_count":4,"put_bytes":18917,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":21012,"get_count":4,"put_bytes":20594,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":22689,"get_count":4,"put_bytes":22265,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":23815,"get_count":4,"put_bytes":23396,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":25491,"get_count":4,"put_bytes":25004,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":27099,"get_count":4,"put_bytes":26760,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":28297,"get_count":4,"put_bytes":28039,"put_count":5}} +{"i":18,"series":1,"stats":{"get_bytes":28455,"get_count":4,"put_bytes":28035,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":30130,"get_count":4,"put_bytes":29720,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":31816,"get_count":5,"put_bytes":31396,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":32796,"get_count":4,"put_bytes":32373,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":33918,"get_count":4,"put_bytes":33498,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":36151,"get_count":5,"put_bytes":35660,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":37066,"get_count":4,"put_bytes":36717,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":38259,"get_count":4,"put_bytes":37839,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":39383,"get_count":4,"put_bytes":38960,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":41605,"get_count":5,"put_bytes":41282,"put_count":5}} +{"i":28,"series":1,"stats":{"get_bytes":41566,"get_count":4,"put_bytes":41217,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":43872,"get_count":6,"put_bytes":43605,"put_count":7}} +{"i":30,"series":1,"stats":{"get_bytes":43204,"get_count":5,"put_bytes":43017,"put_count":6}} +{"i":31,"series":1,"stats":{"get_bytes":43304,"get_count":5,"put_bytes":43185,"put_count":8}} +{"i":32,"series":1,"stats":{"get_bytes":42512,"get_count":5,"put_bytes":42161,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":44125,"get_count":6,"put_bytes":43763,"put_count":6}} +{"i":34,"series":1,"stats":{"get_bytes":44509,"get_count":6,"put_bytes":44173,"put_count":5}} +{"i":35,"series":1,"stats":{"get_bytes":45439,"get_count":6,"put_bytes":45108,"put_count":6}} +{"i":36,"series":1,"stats":{"get_bytes":45809,"get_count":6,"put_bytes":45462,"put_count":5}} +{"i":37,"series":1,"stats":{"get_bytes":47559,"get_count":7,"put_bytes":47305,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":47728,"get_count":7,"put_bytes":47442,"put_count":7}} +{"i":39,"series":1,"stats":{"get_bytes":46395,"get_count":5,"put_bytes":46071,"put_count":5}} +{"i":40,"series":1,"stats":{"get_bytes":48984,"get_count":8,"put_bytes":48641,"put_count":7}} +{"i":41,"series":1,"stats":{"get_bytes":48124,"get_count":6,"put_bytes":47867,"put_count":7}} +{"i":42,"series":1,"stats":{"get_bytes":48298,"get_count":6,"put_bytes":48101,"put_count":7}} +{"i":43,"series":1,"stats":{"get_bytes":48809,"get_count":7,"put_bytes":48520,"put_count":8}} +{"i":44,"series":1,"stats":{"get_bytes":49378,"get_count":7,"put_bytes":49330,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":49909,"get_count":10,"put_bytes":49423,"put_count":8}} +{"i":46,"series":1,"stats":{"get_bytes":48241,"get_count":6,"put_bytes":47898,"put_count":5}} +{"i":47,"series":1,"stats":{"get_bytes":49560,"get_count":6,"put_bytes":49304,"put_count":7}} +{"i":48,"series":1,"stats":{"get_bytes":50015,"get_count":7,"put_bytes":49895,"put_count":9}} +{"i":49,"series":1,"stats":{"get_bytes":50185,"get_count":8,"put_bytes":49753,"put_count":7}} +{"i":50,"series":1,"stats":{"get_bytes":51196,"get_count":8,"put_bytes":50849,"put_count":7}} +{"i":51,"series":1,"stats":{"get_bytes":53049,"get_count":10,"put_bytes":52724,"put_count":10}} +{"i":52,"series":1,"stats":{"get_bytes":50435,"get_count":6,"put_bytes":50386,"put_count":9}} +{"i":53,"series":1,"stats":{"get_bytes":51496,"get_count":9,"put_bytes":51335,"put_count":11}} +{"i":54,"series":1,"stats":{"get_bytes":50132,"get_count":8,"put_bytes":49874,"put_count":8}} +{"i":55,"series":1,"stats":{"get_bytes":50032,"get_count":8,"put_bytes":49781,"put_count":9}} +{"i":56,"series":1,"stats":{"get_bytes":50931,"get_count":10,"put_bytes":50589,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":52130,"get_count":10,"put_bytes":51805,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":51523,"get_count":9,"put_bytes":51424,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":49672,"get_count":8,"put_bytes":49271,"put_count":7}} +{"i":60,"series":1,"stats":{"get_bytes":52854,"get_count":10,"put_bytes":52443,"put_count":8}} +{"i":61,"series":1,"stats":{"get_bytes":52099,"get_count":9,"put_bytes":51785,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":54025,"get_count":11,"put_bytes":53844,"put_count":12}} +{"i":63,"series":1,"stats":{"get_bytes":52890,"get_count":10,"put_bytes":52641,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":52240,"get_count":9,"put_bytes":51819,"put_count":7}} +{"i":65,"series":1,"stats":{"get_bytes":52700,"get_count":8,"put_bytes":52655,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":51048,"get_count":10,"put_bytes":50704,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":53909,"get_count":12,"put_bytes":53580,"put_count":12}} +{"i":68,"series":1,"stats":{"get_bytes":52681,"get_count":11,"put_bytes":52418,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":52012,"get_count":10,"put_bytes":51754,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":52329,"get_count":10,"put_bytes":52228,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":51716,"get_count":11,"put_bytes":51307,"put_count":10}} +{"i":72,"series":1,"stats":{"get_bytes":53181,"get_count":13,"put_bytes":52991,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":52220,"get_count":13,"put_bytes":51894,"put_count":13}} +{"i":74,"series":1,"stats":{"get_bytes":49843,"get_count":9,"put_bytes":49858,"put_count":13}} +{"i":75,"series":1,"stats":{"get_bytes":51407,"get_count":14,"put_bytes":51004,"put_count":13}} +{"i":76,"series":1,"stats":{"get_bytes":48188,"get_count":10,"put_bytes":48105,"put_count":12}} +{"i":77,"series":1,"stats":{"get_bytes":50864,"get_count":15,"put_bytes":50482,"put_count":14}} +{"i":78,"series":1,"stats":{"get_bytes":49043,"get_count":13,"put_bytes":48853,"put_count":14}} +{"i":79,"series":1,"stats":{"get_bytes":49468,"get_count":14,"put_bytes":49127,"put_count":14}} +{"i":80,"series":1,"stats":{"get_bytes":47506,"get_count":11,"put_bytes":47232,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":47446,"get_count":12,"put_bytes":47203,"put_count":13}} +{"i":82,"series":1,"stats":{"get_bytes":46969,"get_count":12,"put_bytes":46555,"put_count":10}} +{"i":83,"series":1,"stats":{"get_bytes":49194,"get_count":13,"put_bytes":48860,"put_count":13}} +{"i":84,"series":1,"stats":{"get_bytes":49473,"get_count":15,"put_bytes":49308,"put_count":16}} +{"i":85,"series":1,"stats":{"get_bytes":46204,"get_count":12,"put_bytes":45874,"put_count":12}} +{"i":86,"series":1,"stats":{"get_bytes":47696,"get_count":13,"put_bytes":47431,"put_count":13}} +{"i":87,"series":1,"stats":{"get_bytes":46046,"get_count":14,"put_bytes":45798,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":50421,"get_count":18,"put_bytes":50020,"put_count":16}} +{"i":89,"series":1,"stats":{"get_bytes":45194,"get_count":12,"put_bytes":44946,"put_count":13}} +{"i":90,"series":1,"stats":{"get_bytes":45573,"get_count":14,"put_bytes":45397,"put_count":15}} +{"i":91,"series":1,"stats":{"get_bytes":43669,"get_count":13,"put_bytes":43414,"put_count":14}} +{"i":92,"series":1,"stats":{"get_bytes":42756,"get_count":12,"put_bytes":42653,"put_count":14}} +{"i":93,"series":1,"stats":{"get_bytes":41303,"get_count":12,"put_bytes":41121,"put_count":14}} +{"i":94,"series":1,"stats":{"get_bytes":42378,"get_count":15,"put_bytes":42121,"put_count":15}} +{"i":95,"series":1,"stats":{"get_bytes":42470,"get_count":15,"put_bytes":42067,"put_count":14}} +{"i":96,"series":1,"stats":{"get_bytes":42832,"get_count":16,"put_bytes":42432,"put_count":14}} +{"i":97,"series":1,"stats":{"get_bytes":41669,"get_count":14,"put_bytes":41272,"put_count":13}} +{"i":98,"series":1,"stats":{"get_bytes":40421,"get_count":13,"put_bytes":40159,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":40459,"get_count":13,"put_bytes":40202,"put_count":14}} +{"i":0,"series":2,"stats":{"get_bytes":38736,"get_count":12,"put_bytes":38480,"put_count":12}} +{"i":1,"series":2,"stats":{"get_bytes":42380,"get_count":17,"put_bytes":41975,"put_count":16}} +{"i":2,"series":2,"stats":{"get_bytes":40417,"get_count":15,"put_bytes":40088,"put_count":14}} +{"i":3,"series":2,"stats":{"get_bytes":43359,"get_count":16,"put_bytes":42956,"put_count":15}} +{"i":4,"series":2,"stats":{"get_bytes":40458,"get_count":14,"put_bytes":40130,"put_count":13}} +{"i":5,"series":2,"stats":{"get_bytes":36954,"get_count":11,"put_bytes":36630,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":40074,"get_count":14,"put_bytes":39742,"put_count":13}} +{"i":7,"series":2,"stats":{"get_bytes":40115,"get_count":15,"put_bytes":39701,"put_count":14}} +{"i":8,"series":2,"stats":{"get_bytes":38649,"get_count":13,"put_bytes":38317,"put_count":12}} +{"i":9,"series":2,"stats":{"get_bytes":41516,"get_count":15,"put_bytes":41035,"put_count":13}} +{"i":10,"series":2,"stats":{"get_bytes":41568,"get_count":15,"put_bytes":41299,"put_count":15}} +{"i":11,"series":2,"stats":{"get_bytes":37970,"get_count":13,"put_bytes":37646,"put_count":13}} +{"i":12,"series":2,"stats":{"get_bytes":39766,"get_count":15,"put_bytes":39496,"put_count":15}} +{"i":13,"series":2,"stats":{"get_bytes":36939,"get_count":12,"put_bytes":36617,"put_count":12}} +{"i":14,"series":2,"stats":{"get_bytes":42376,"get_count":17,"put_bytes":42047,"put_count":16}} +{"i":15,"series":2,"stats":{"get_bytes":39652,"get_count":14,"put_bytes":39257,"put_count":13}} +{"i":16,"series":2,"stats":{"get_bytes":40827,"get_count":16,"put_bytes":40424,"put_count":14}} +{"i":17,"series":2,"stats":{"get_bytes":41341,"get_count":17,"put_bytes":40958,"put_count":16}} +{"i":18,"series":2,"stats":{"get_bytes":38987,"get_count":15,"put_bytes":38733,"put_count":15}} +{"i":19,"series":2,"stats":{"get_bytes":37462,"get_count":15,"put_bytes":37044,"put_count":14}} +{"i":20,"series":2,"stats":{"get_bytes":41582,"get_count":17,"put_bytes":41169,"put_count":15}} +{"i":21,"series":2,"stats":{"get_bytes":39943,"get_count":17,"put_bytes":39543,"put_count":16}} +{"i":22,"series":2,"stats":{"get_bytes":38613,"get_count":13,"put_bytes":38499,"put_count":15}} +{"i":23,"series":2,"stats":{"get_bytes":35351,"get_count":14,"put_bytes":34949,"put_count":13}} +{"i":24,"series":2,"stats":{"get_bytes":37760,"get_count":18,"put_bytes":37363,"put_count":16}} +{"i":25,"series":2,"stats":{"get_bytes":38128,"get_count":17,"put_bytes":37733,"put_count":16}} +{"i":26,"series":2,"stats":{"get_bytes":35567,"get_count":13,"put_bytes":35381,"put_count":14}} +{"i":27,"series":2,"stats":{"get_bytes":36406,"get_count":16,"put_bytes":36024,"put_count":15}} +{"i":28,"series":2,"stats":{"get_bytes":37283,"get_count":16,"put_bytes":36878,"put_count":14}} +{"i":29,"series":2,"stats":{"get_bytes":35939,"get_count":16,"put_bytes":35605,"put_count":16}} +{"i":30,"series":2,"stats":{"get_bytes":36981,"get_count":16,"put_bytes":36647,"put_count":15}} +{"i":31,"series":2,"stats":{"get_bytes":34151,"get_count":14,"put_bytes":33902,"put_count":15}} +{"i":32,"series":2,"stats":{"get_bytes":34070,"get_count":15,"put_bytes":33807,"put_count":15}} +{"i":33,"series":2,"stats":{"get_bytes":33364,"get_count":16,"put_bytes":32961,"put_count":15}} +{"i":34,"series":2,"stats":{"get_bytes":34795,"get_count":16,"put_bytes":34463,"put_count":15}} +{"i":35,"series":2,"stats":{"get_bytes":36658,"get_count":17,"put_bytes":36253,"put_count":16}} +{"i":36,"series":2,"stats":{"get_bytes":33705,"get_count":16,"put_bytes":33440,"put_count":16}} +{"i":37,"series":2,"stats":{"get_bytes":34183,"get_count":17,"put_bytes":33724,"put_count":15}} +{"i":38,"series":2,"stats":{"get_bytes":33678,"get_count":17,"put_bytes":33338,"put_count":16}} +{"i":39,"series":2,"stats":{"get_bytes":35653,"get_count":17,"put_bytes":35260,"put_count":16}} +{"i":40,"series":2,"stats":{"get_bytes":30665,"get_count":15,"put_bytes":30490,"put_count":16}} +{"i":41,"series":2,"stats":{"get_bytes":27791,"get_count":12,"put_bytes":27531,"put_count":13}} +{"i":42,"series":2,"stats":{"get_bytes":31036,"get_count":18,"put_bytes":30632,"put_count":16}} +{"i":43,"series":2,"stats":{"get_bytes":30403,"get_count":15,"put_bytes":30071,"put_count":15}} +{"i":44,"series":2,"stats":{"get_bytes":29623,"get_count":16,"put_bytes":29369,"put_count":16}} +{"i":45,"series":2,"stats":{"get_bytes":32498,"get_count":18,"put_bytes":32017,"put_count":16}} +{"i":46,"series":2,"stats":{"get_bytes":28694,"get_count":17,"put_bytes":28360,"put_count":16}} +{"i":47,"series":2,"stats":{"get_bytes":27667,"get_count":18,"put_bytes":27196,"put_count":16}} +{"i":48,"series":2,"stats":{"get_bytes":32457,"get_count":18,"put_bytes":32048,"put_count":16}} +{"i":49,"series":2,"stats":{"get_bytes":31612,"get_count":17,"put_bytes":31131,"put_count":15}} +{"i":50,"series":2,"stats":{"get_bytes":31897,"get_count":18,"put_bytes":31493,"put_count":16}} +{"i":51,"series":2,"stats":{"get_bytes":31603,"get_count":17,"put_bytes":31130,"put_count":15}} +{"i":52,"series":2,"stats":{"get_bytes":31811,"get_count":18,"put_bytes":31401,"put_count":16}} +{"i":53,"series":2,"stats":{"get_bytes":31866,"get_count":18,"put_bytes":31394,"put_count":16}} +{"i":54,"series":2,"stats":{"get_bytes":31738,"get_count":17,"put_bytes":31341,"put_count":15}} +{"i":55,"series":2,"stats":{"get_bytes":32007,"get_count":18,"put_bytes":31526,"put_count":16}} +{"i":56,"series":2,"stats":{"get_bytes":31345,"get_count":16,"put_bytes":31017,"put_count":15}} +{"i":57,"series":2,"stats":{"get_bytes":29591,"get_count":15,"put_bytes":29188,"put_count":14}} +{"i":58,"series":2,"stats":{"get_bytes":31048,"get_count":16,"put_bytes":30771,"put_count":16}} +{"i":59,"series":2,"stats":{"get_bytes":30195,"get_count":17,"put_bytes":29713,"put_count":15}} +{"i":60,"series":2,"stats":{"get_bytes":31551,"get_count":18,"put_bytes":31157,"put_count":16}} +{"i":61,"series":2,"stats":{"get_bytes":32179,"get_count":18,"put_bytes":31702,"put_count":16}} +{"i":62,"series":2,"stats":{"get_bytes":31283,"get_count":17,"put_bytes":30873,"put_count":15}} +{"i":63,"series":2,"stats":{"get_bytes":31429,"get_count":18,"put_bytes":30950,"put_count":16}} +{"i":64,"series":2,"stats":{"get_bytes":29158,"get_count":17,"put_bytes":28829,"put_count":16}} +{"i":65,"series":2,"stats":{"get_bytes":30933,"get_count":18,"put_bytes":30454,"put_count":16}} +{"i":66,"series":2,"stats":{"get_bytes":31198,"get_count":17,"put_bytes":30867,"put_count":16}} +{"i":67,"series":2,"stats":{"get_bytes":27636,"get_count":17,"put_bytes":27158,"put_count":15}} +{"i":68,"series":2,"stats":{"get_bytes":31142,"get_count":18,"put_bytes":30737,"put_count":16}} +{"i":69,"series":2,"stats":{"get_bytes":32136,"get_count":17,"put_bytes":31733,"put_count":16}} +{"i":70,"series":2,"stats":{"get_bytes":30132,"get_count":18,"put_bytes":29724,"put_count":16}} +{"i":71,"series":2,"stats":{"get_bytes":31813,"get_count":18,"put_bytes":31331,"put_count":16}} +{"i":72,"series":2,"stats":{"get_bytes":30217,"get_count":17,"put_bytes":29904,"put_count":16}} +{"i":73,"series":2,"stats":{"get_bytes":31052,"get_count":18,"put_bytes":30581,"put_count":16}} +{"i":74,"series":2,"stats":{"get_bytes":28946,"get_count":17,"put_bytes":28617,"put_count":16}} +{"i":75,"series":2,"stats":{"get_bytes":29500,"get_count":17,"put_bytes":29017,"put_count":15}} +{"i":76,"series":2,"stats":{"get_bytes":30230,"get_count":18,"put_bytes":29820,"put_count":16}} +{"i":77,"series":2,"stats":{"get_bytes":30162,"get_count":17,"put_bytes":29682,"put_count":15}} +{"i":78,"series":2,"stats":{"get_bytes":29536,"get_count":17,"put_bytes":29133,"put_count":15}} +{"i":79,"series":2,"stats":{"get_bytes":31375,"get_count":18,"put_bytes":30907,"put_count":16}} +{"i":80,"series":2,"stats":{"get_bytes":29837,"get_count":18,"put_bytes":29432,"put_count":16}} +{"i":81,"series":2,"stats":{"get_bytes":27233,"get_count":17,"put_bytes":26759,"put_count":15}} +{"i":82,"series":2,"stats":{"get_bytes":30087,"get_count":18,"put_bytes":29681,"put_count":16}} +{"i":83,"series":2,"stats":{"get_bytes":31650,"get_count":18,"put_bytes":31173,"put_count":16}} +{"i":84,"series":2,"stats":{"get_bytes":26820,"get_count":15,"put_bytes":26487,"put_count":14}} +{"i":85,"series":2,"stats":{"get_bytes":32329,"get_count":18,"put_bytes":31855,"put_count":16}} +{"i":86,"series":2,"stats":{"get_bytes":32610,"get_count":17,"put_bytes":32219,"put_count":15}} +{"i":87,"series":2,"stats":{"get_bytes":33000,"get_count":18,"put_bytes":32526,"put_count":16}} +{"i":88,"series":2,"stats":{"get_bytes":31466,"get_count":18,"put_bytes":31058,"put_count":16}} +{"i":89,"series":2,"stats":{"get_bytes":34195,"get_count":18,"put_bytes":33721,"put_count":16}} +{"i":90,"series":2,"stats":{"get_bytes":33537,"get_count":17,"put_bytes":33125,"put_count":15}} +{"i":91,"series":2,"stats":{"get_bytes":33658,"get_count":18,"put_bytes":33176,"put_count":16}} +{"i":92,"series":2,"stats":{"get_bytes":32177,"get_count":16,"put_bytes":31846,"put_count":15}} +{"i":93,"series":2,"stats":{"get_bytes":31822,"get_count":17,"put_bytes":31352,"put_count":15}} +{"i":94,"series":2,"stats":{"get_bytes":31446,"get_count":16,"put_bytes":31102,"put_count":15}} +{"i":95,"series":2,"stats":{"get_bytes":32028,"get_count":17,"put_bytes":31546,"put_count":15}} +{"i":96,"series":2,"stats":{"get_bytes":27384,"get_count":16,"put_bytes":26983,"put_count":14}} +{"i":97,"series":2,"stats":{"get_bytes":31918,"get_count":18,"put_bytes":31442,"put_count":16}} +{"i":98,"series":2,"stats":{"get_bytes":31776,"get_count":17,"put_bytes":31367,"put_count":15}} +{"i":99,"series":2,"stats":{"get_bytes":33881,"get_count":18,"put_bytes":33408,"put_count":16}} 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..43ad56113 --- /dev/null +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} 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..a63852f7b --- /dev/null +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":42071,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":42389,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":43499,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":42714,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":42309,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":39006,"get_count":13,"put_bytes":31004,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":41130,"get_count":13,"put_bytes":31004,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":41853,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":44292,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":46580,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":41306,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":43658,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":44263,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":43508,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":42418,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":45239,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":44415,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":42952,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":46498,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":44904,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":43648,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":45119,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":43298,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":43814,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":40601,"get_count":13,"put_bytes":31004,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":43921,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":44288,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":45164,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":43774,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":43065,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":44063,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":44174,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":43940,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":43621,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":41546,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":45997,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":45285,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":43236,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":45813,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":41085,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":42985,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":41989,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":45401,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":43754,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":39809,"get_count":13,"put_bytes":31004,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":42601,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":44114,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":43108,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":44068,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":44180,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":43966,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":43556,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":41907,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":40240,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":41887,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":42994,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":43109,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":41296,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":42255,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":42860,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":42656,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":43085,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":44355,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":43239,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":42948,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":43936,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":41964,"get_count":14,"put_bytes":31004,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":44570,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":40879,"get_count":13,"put_bytes":31004,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":44374,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":44481,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":43900,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":44752,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":42693,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":46248,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":44650,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":43511,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":42993,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":44146,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":45293,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":41858,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":43920,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":42883,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":46903,"get_count":18,"put_bytes":31004,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":43542,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":43959,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":43281,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":42727,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":42435,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":43920,"get_count":17,"put_bytes":31004,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":43933,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":43791,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":42548,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":41899,"get_count":15,"put_bytes":31004,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":42711,"get_count":16,"put_bytes":31004,"put_count":2}} 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..501ab6b36 --- /dev/null +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2436,"get_count":4,"put_bytes":351,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":500,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2742,"get_count":4,"put_bytes":647,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2888,"get_count":4,"put_bytes":793,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":3102,"get_count":4,"put_bytes":1007,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":3250,"get_count":4,"put_bytes":1155,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":3397,"get_count":4,"put_bytes":1302,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":3544,"get_count":4,"put_bytes":1449,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":3757,"get_count":4,"put_bytes":1662,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":3905,"get_count":4,"put_bytes":1810,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":4052,"get_count":4,"put_bytes":1957,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":4201,"get_count":4,"put_bytes":2106,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2319,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":4562,"get_count":4,"put_bytes":2467,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":4709,"get_count":4,"put_bytes":2614,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":4856,"get_count":4,"put_bytes":2762,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":5069,"get_count":4,"put_bytes":2974,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":5217,"get_count":4,"put_bytes":3122,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":5363,"get_count":4,"put_bytes":3268,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":5511,"get_count":4,"put_bytes":3416,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":5724,"get_count":4,"put_bytes":3629,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":5872,"get_count":4,"put_bytes":3777,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":6018,"get_count":4,"put_bytes":3923,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":6165,"get_count":4,"put_bytes":4070,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":6378,"get_count":4,"put_bytes":4283,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":6527,"get_count":4,"put_bytes":4432,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":6676,"get_count":4,"put_bytes":4581,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":6823,"get_count":4,"put_bytes":4728,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":7036,"get_count":4,"put_bytes":4941,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":7184,"get_count":4,"put_bytes":5089,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":7331,"get_count":4,"put_bytes":5236,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":7478,"get_count":4,"put_bytes":5383,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":7691,"get_count":4,"put_bytes":5596,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":7838,"get_count":4,"put_bytes":5743,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":7985,"get_count":4,"put_bytes":5890,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":8132,"get_count":4,"put_bytes":6037,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":8344,"get_count":4,"put_bytes":6249,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":8492,"get_count":4,"put_bytes":6397,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":8638,"get_count":4,"put_bytes":6543,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":8784,"get_count":4,"put_bytes":6689,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":8997,"get_count":4,"put_bytes":6902,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":9144,"get_count":4,"put_bytes":7049,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":9291,"get_count":4,"put_bytes":7196,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":9438,"get_count":4,"put_bytes":7343,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":9651,"get_count":4,"put_bytes":7556,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":9799,"get_count":4,"put_bytes":7704,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":9946,"get_count":4,"put_bytes":7851,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":10093,"get_count":4,"put_bytes":7998,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":10306,"get_count":4,"put_bytes":8211,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":10454,"get_count":4,"put_bytes":8359,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":10601,"get_count":4,"put_bytes":8506,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":10748,"get_count":4,"put_bytes":8653,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":10961,"get_count":4,"put_bytes":8866,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":11109,"get_count":4,"put_bytes":9014,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":11256,"get_count":4,"put_bytes":9161,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":11403,"get_count":4,"put_bytes":9308,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":11616,"get_count":4,"put_bytes":9521,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":11764,"get_count":4,"put_bytes":9669,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":11911,"get_count":4,"put_bytes":9816,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":12058,"get_count":4,"put_bytes":9963,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":12270,"get_count":4,"put_bytes":10175,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":12418,"get_count":4,"put_bytes":10323,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":12564,"get_count":4,"put_bytes":10469,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":12711,"get_count":4,"put_bytes":10616,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":12924,"get_count":4,"put_bytes":10829,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":13071,"get_count":4,"put_bytes":10976,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":13218,"get_count":4,"put_bytes":11123,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":13365,"get_count":4,"put_bytes":11270,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":13577,"get_count":4,"put_bytes":11482,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":13724,"get_count":4,"put_bytes":11629,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":13871,"get_count":4,"put_bytes":11776,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":14017,"get_count":4,"put_bytes":11922,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":14230,"get_count":4,"put_bytes":12135,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":14378,"get_count":4,"put_bytes":12283,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":14525,"get_count":4,"put_bytes":12430,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":14672,"get_count":4,"put_bytes":12577,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":14885,"get_count":4,"put_bytes":12790,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":15033,"get_count":4,"put_bytes":12938,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":15179,"get_count":4,"put_bytes":13084,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":15326,"get_count":4,"put_bytes":13231,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":15538,"get_count":4,"put_bytes":13443,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":15685,"get_count":4,"put_bytes":13590,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":15831,"get_count":4,"put_bytes":13736,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":15977,"get_count":4,"put_bytes":13882,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":16190,"get_count":4,"put_bytes":14095,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":16337,"get_count":4,"put_bytes":14242,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":16484,"get_count":4,"put_bytes":14389,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":16631,"get_count":4,"put_bytes":14536,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":16844,"get_count":4,"put_bytes":14749,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":16992,"get_count":4,"put_bytes":14897,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":17139,"get_count":4,"put_bytes":15044,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":17285,"get_count":4,"put_bytes":15190,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":17019,"get_count":4,"put_bytes":14924,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":17166,"get_count":4,"put_bytes":15071,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":17313,"get_count":4,"put_bytes":15218,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":17460,"get_count":4,"put_bytes":15365,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":17673,"get_count":4,"put_bytes":15578,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":17374,"get_count":4,"put_bytes":15279,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":17488,"get_count":4,"put_bytes":15393,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":17635,"get_count":4,"put_bytes":15540,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":2958,"get_count":4,"put_bytes":873,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1613,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2352,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":5185,"get_count":4,"put_bytes":3090,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":5924,"get_count":4,"put_bytes":3829,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":6664,"get_count":4,"put_bytes":4569,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":7401,"get_count":4,"put_bytes":5306,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":8137,"get_count":4,"put_bytes":6042,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":8872,"get_count":4,"put_bytes":6777,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7511,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":10345,"get_count":4,"put_bytes":8250,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":11076,"get_count":4,"put_bytes":8981,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":11809,"get_count":4,"put_bytes":9714,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":12543,"get_count":4,"put_bytes":10448,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":13027,"get_count":4,"put_bytes":10932,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":13760,"get_count":4,"put_bytes":11666,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":14244,"get_count":4,"put_bytes":12149,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":14477,"get_count":4,"put_bytes":12382,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":15213,"get_count":4,"put_bytes":13118,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":15449,"get_count":4,"put_bytes":13354,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":16181,"get_count":4,"put_bytes":14086,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":16840,"get_count":4,"put_bytes":14745,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":17499,"get_count":4,"put_bytes":15404,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":18161,"get_count":4,"put_bytes":16066,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":18823,"get_count":4,"put_bytes":16728,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":19309,"get_count":4,"put_bytes":17214,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":19039,"get_count":4,"put_bytes":16944,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":19448,"get_count":4,"put_bytes":17353,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":20182,"get_count":4,"put_bytes":18087,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":20342,"get_count":4,"put_bytes":18247,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":20750,"get_count":4,"put_bytes":18655,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":20733,"get_count":4,"put_bytes":18638,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":21468,"get_count":4,"put_bytes":19373,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":21378,"get_count":4,"put_bytes":19283,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":22037,"get_count":4,"put_bytes":19942,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":22448,"get_count":4,"put_bytes":20353,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":23183,"get_count":4,"put_bytes":21088,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":23843,"get_count":4,"put_bytes":21748,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":24001,"get_count":4,"put_bytes":21906,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":24409,"get_count":4,"put_bytes":22314,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":24818,"get_count":4,"put_bytes":22723,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":25331,"get_count":4,"put_bytes":23236,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":25738,"get_count":4,"put_bytes":23643,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":25750,"get_count":4,"put_bytes":23655,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":26483,"get_count":4,"put_bytes":24388,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":26894,"get_count":4,"put_bytes":24799,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":27375,"get_count":4,"put_bytes":25280,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":27858,"get_count":4,"put_bytes":25763,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":28268,"get_count":4,"put_bytes":26173,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":28501,"get_count":4,"put_bytes":26406,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":28737,"get_count":4,"put_bytes":26642,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":28821,"get_count":4,"put_bytes":26726,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":29227,"get_count":4,"put_bytes":27132,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":29384,"get_count":4,"put_bytes":27289,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":29794,"get_count":4,"put_bytes":27699,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":29701,"get_count":4,"put_bytes":27606,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":29713,"get_count":4,"put_bytes":27618,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":29726,"get_count":4,"put_bytes":27631,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":30061,"get_count":4,"put_bytes":27966,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":30073,"get_count":4,"put_bytes":27978,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":29908,"get_count":4,"put_bytes":27813,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":30171,"get_count":4,"put_bytes":28076,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":30538,"get_count":4,"put_bytes":28443,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":30623,"get_count":4,"put_bytes":28528,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":30458,"get_count":4,"put_bytes":28363,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":30219,"get_count":4,"put_bytes":28124,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":29981,"get_count":4,"put_bytes":27886,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":29638,"get_count":4,"put_bytes":27543,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":29575,"get_count":4,"put_bytes":27480,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":29586,"get_count":4,"put_bytes":27491,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":29666,"get_count":5,"put_bytes":27571,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":30001,"get_count":5,"put_bytes":27906,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":29616,"get_count":5,"put_bytes":27521,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":29481,"get_count":5,"put_bytes":27386,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":29242,"get_count":5,"put_bytes":27147,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":28826,"get_count":5,"put_bytes":26731,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":28515,"get_count":5,"put_bytes":26420,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":28557,"get_count":5,"put_bytes":26462,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":28923,"get_count":5,"put_bytes":26828,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":28434,"get_count":5,"put_bytes":26339,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":27726,"get_count":5,"put_bytes":25631,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":27622,"get_count":5,"put_bytes":25527,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":27632,"get_count":5,"put_bytes":25537,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":27497,"get_count":5,"put_bytes":25402,"put_count":3}} +{"i":84,"series":2,"stats":{"get_bytes":27936,"get_count":5,"put_bytes":25841,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":27948,"get_count":5,"put_bytes":25853,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":27668,"get_count":5,"put_bytes":25573,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":27782,"get_count":5,"put_bytes":25687,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":27575,"get_count":5,"put_bytes":25480,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":27190,"get_count":5,"put_bytes":25095,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":26982,"get_count":5,"put_bytes":24887,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":26597,"get_count":5,"put_bytes":24502,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":26036,"get_count":5,"put_bytes":23941,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":25900,"get_count":5,"put_bytes":23805,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":26162,"get_count":5,"put_bytes":24067,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":26028,"get_count":5,"put_bytes":23933,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":26466,"get_count":5,"put_bytes":24371,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":26477,"get_count":5,"put_bytes":24382,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":26269,"get_count":5,"put_bytes":24174,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":26311,"get_count":5,"put_bytes":24216,"put_count":3}} 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..731cb84e2 --- /dev/null +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -0,0 +1,20 @@ +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2211,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2208,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} 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..47f8b6edf --- /dev/null +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2188,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":180,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2275,"get_count":4,"put_bytes":261,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2356,"get_count":4,"put_bytes":345,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2440,"get_count":4,"put_bytes":419,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2514,"get_count":4,"put_bytes":493,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2588,"get_count":4,"put_bytes":567,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2662,"get_count":4,"put_bytes":641,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2736,"get_count":4,"put_bytes":715,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2810,"get_count":4,"put_bytes":789,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2884,"get_count":4,"put_bytes":863,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2958,"get_count":4,"put_bytes":937,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":3032,"get_count":4,"put_bytes":1011,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":3106,"get_count":4,"put_bytes":1085,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":3180,"get_count":4,"put_bytes":1159,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":3254,"get_count":4,"put_bytes":1233,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":3328,"get_count":4,"put_bytes":1308,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":3403,"get_count":4,"put_bytes":1383,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":3478,"get_count":4,"put_bytes":1458,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":3553,"get_count":4,"put_bytes":1533,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":3628,"get_count":4,"put_bytes":1607,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":3702,"get_count":4,"put_bytes":1682,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":3777,"get_count":4,"put_bytes":1757,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":3852,"get_count":4,"put_bytes":1831,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1907,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":4002,"get_count":4,"put_bytes":1982,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":4077,"get_count":4,"put_bytes":2057,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":4152,"get_count":4,"put_bytes":2132,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":4227,"get_count":4,"put_bytes":2207,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":4302,"get_count":4,"put_bytes":2282,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2357,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":4452,"get_count":4,"put_bytes":2432,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":4527,"get_count":4,"put_bytes":2506,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":4601,"get_count":4,"put_bytes":2581,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":4676,"get_count":4,"put_bytes":2656,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":4751,"get_count":4,"put_bytes":2731,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":4826,"get_count":4,"put_bytes":2805,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":4900,"get_count":4,"put_bytes":2880,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":4975,"get_count":4,"put_bytes":2955,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":5050,"get_count":4,"put_bytes":3030,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":5125,"get_count":4,"put_bytes":3105,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":5200,"get_count":4,"put_bytes":3180,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":5275,"get_count":4,"put_bytes":3255,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":5350,"get_count":4,"put_bytes":3330,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":5425,"get_count":4,"put_bytes":3404,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":5499,"get_count":4,"put_bytes":3479,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":5574,"get_count":4,"put_bytes":3554,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":5649,"get_count":4,"put_bytes":3629,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":5724,"get_count":4,"put_bytes":3704,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":5799,"get_count":4,"put_bytes":3779,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":5874,"get_count":4,"put_bytes":3854,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":5949,"get_count":4,"put_bytes":3929,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":6024,"get_count":4,"put_bytes":4004,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":6099,"get_count":4,"put_bytes":4079,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":6174,"get_count":4,"put_bytes":4154,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":6249,"get_count":4,"put_bytes":4229,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":6324,"get_count":4,"put_bytes":4304,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":6399,"get_count":4,"put_bytes":4379,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":6474,"get_count":4,"put_bytes":4454,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":6549,"get_count":4,"put_bytes":4529,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":6624,"get_count":4,"put_bytes":4603,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":6698,"get_count":4,"put_bytes":4677,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":6772,"get_count":4,"put_bytes":4752,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":6847,"get_count":4,"put_bytes":4827,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":6922,"get_count":4,"put_bytes":4902,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":6997,"get_count":4,"put_bytes":4977,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":7072,"get_count":4,"put_bytes":5052,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":7147,"get_count":4,"put_bytes":5126,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":7221,"get_count":4,"put_bytes":5201,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":7296,"get_count":4,"put_bytes":5276,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":7371,"get_count":4,"put_bytes":5351,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":7446,"get_count":4,"put_bytes":5426,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":7521,"get_count":4,"put_bytes":5501,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":7596,"get_count":4,"put_bytes":5576,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":7671,"get_count":4,"put_bytes":5650,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":7745,"get_count":4,"put_bytes":5724,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":7819,"get_count":4,"put_bytes":5799,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":7894,"get_count":4,"put_bytes":5873,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":7968,"get_count":4,"put_bytes":5947,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":8042,"get_count":4,"put_bytes":6022,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":8117,"get_count":4,"put_bytes":6097,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":8192,"get_count":4,"put_bytes":6171,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":8266,"get_count":4,"put_bytes":6246,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":8341,"get_count":4,"put_bytes":6321,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":8416,"get_count":4,"put_bytes":6396,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":8491,"get_count":4,"put_bytes":6470,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":8565,"get_count":4,"put_bytes":6544,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":8639,"get_count":4,"put_bytes":6618,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":8713,"get_count":4,"put_bytes":6692,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":8787,"get_count":4,"put_bytes":6767,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":8862,"get_count":4,"put_bytes":6842,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":8937,"get_count":4,"put_bytes":6916,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":9011,"get_count":4,"put_bytes":6990,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":9085,"get_count":4,"put_bytes":7064,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":9159,"get_count":4,"put_bytes":7139,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":9234,"get_count":4,"put_bytes":7214,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":9309,"get_count":4,"put_bytes":7288,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":9383,"get_count":4,"put_bytes":7362,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":9457,"get_count":4,"put_bytes":7437,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":9532,"get_count":4,"put_bytes":7511,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7511,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7585,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":9680,"get_count":4,"put_bytes":7658,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":9753,"get_count":4,"put_bytes":7732,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":9827,"get_count":4,"put_bytes":7806,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":9901,"get_count":4,"put_bytes":7879,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":9974,"get_count":4,"put_bytes":7952,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":10047,"get_count":4,"put_bytes":8026,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":10121,"get_count":4,"put_bytes":8100,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":10195,"get_count":4,"put_bytes":8174,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":10269,"get_count":4,"put_bytes":8247,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":10342,"get_count":4,"put_bytes":8320,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":10415,"get_count":4,"put_bytes":8394,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":10489,"get_count":4,"put_bytes":8468,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":10563,"get_count":4,"put_bytes":8541,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":10636,"get_count":4,"put_bytes":8614,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":10709,"get_count":4,"put_bytes":8689,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":10784,"get_count":4,"put_bytes":8764,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":10859,"get_count":4,"put_bytes":8839,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":10934,"get_count":4,"put_bytes":8914,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":11009,"get_count":4,"put_bytes":8989,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":11084,"get_count":4,"put_bytes":9064,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":11159,"get_count":4,"put_bytes":9138,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":11233,"get_count":4,"put_bytes":9213,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":11308,"get_count":4,"put_bytes":9287,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":11382,"get_count":4,"put_bytes":9362,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":11457,"get_count":4,"put_bytes":9437,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":11532,"get_count":4,"put_bytes":9511,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":11606,"get_count":4,"put_bytes":9586,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":11681,"get_count":4,"put_bytes":9660,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":11755,"get_count":4,"put_bytes":9735,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":11830,"get_count":4,"put_bytes":9809,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":11904,"get_count":4,"put_bytes":9883,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":11978,"get_count":4,"put_bytes":9957,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":12052,"get_count":4,"put_bytes":10031,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":12126,"get_count":4,"put_bytes":10105,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":12200,"get_count":4,"put_bytes":10179,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":12274,"get_count":4,"put_bytes":10254,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":12349,"get_count":4,"put_bytes":10329,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":12424,"get_count":4,"put_bytes":10403,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":12498,"get_count":4,"put_bytes":10477,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":12572,"get_count":4,"put_bytes":10552,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":12647,"get_count":4,"put_bytes":10627,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":12722,"get_count":4,"put_bytes":10702,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":12797,"get_count":4,"put_bytes":10777,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":12872,"get_count":4,"put_bytes":10852,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":12947,"get_count":4,"put_bytes":10926,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":13021,"get_count":4,"put_bytes":11000,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":13095,"get_count":4,"put_bytes":11074,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":13169,"get_count":4,"put_bytes":11149,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":13244,"get_count":4,"put_bytes":11223,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":13318,"get_count":4,"put_bytes":11297,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":13392,"get_count":4,"put_bytes":11371,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":13466,"get_count":4,"put_bytes":11446,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":13541,"get_count":4,"put_bytes":11521,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":13616,"get_count":4,"put_bytes":11596,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":13691,"get_count":4,"put_bytes":11671,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":13766,"get_count":4,"put_bytes":11745,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":13840,"get_count":4,"put_bytes":11820,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":13915,"get_count":4,"put_bytes":11895,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":13990,"get_count":4,"put_bytes":11969,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":14064,"get_count":4,"put_bytes":12043,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":14138,"get_count":4,"put_bytes":12118,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":14213,"get_count":4,"put_bytes":12192,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":14287,"get_count":4,"put_bytes":12266,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":14361,"get_count":4,"put_bytes":12341,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":14436,"get_count":4,"put_bytes":12416,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":14511,"get_count":4,"put_bytes":12490,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":14585,"get_count":4,"put_bytes":12564,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":14659,"get_count":4,"put_bytes":12639,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":14734,"get_count":4,"put_bytes":12789,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":14554,"get_count":4,"put_bytes":12533,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":14628,"get_count":4,"put_bytes":12608,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":14703,"get_count":4,"put_bytes":12682,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":14777,"get_count":4,"put_bytes":12756,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":14851,"get_count":4,"put_bytes":12830,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":14925,"get_count":4,"put_bytes":12985,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":14746,"get_count":4,"put_bytes":12726,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":14821,"get_count":4,"put_bytes":12800,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":14895,"get_count":4,"put_bytes":12874,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":14969,"get_count":4,"put_bytes":12949,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":15374,"get_count":5,"put_bytes":13354,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":15044,"get_count":4,"put_bytes":13022,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":15117,"get_count":4,"put_bytes":13097,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":15192,"get_count":4,"put_bytes":13171,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":15266,"get_count":4,"put_bytes":13245,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":15340,"get_count":4,"put_bytes":13319,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":15414,"get_count":4,"put_bytes":13393,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":15488,"get_count":4,"put_bytes":13468,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":15563,"get_count":4,"put_bytes":13543,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":15638,"get_count":4,"put_bytes":13618,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":15713,"get_count":4,"put_bytes":13692,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":15787,"get_count":4,"put_bytes":13767,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":15862,"get_count":4,"put_bytes":13841,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":15936,"get_count":4,"put_bytes":13916,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":16011,"get_count":4,"put_bytes":13991,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":16086,"get_count":4,"put_bytes":14065,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":16160,"get_count":4,"put_bytes":14140,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":16235,"get_count":4,"put_bytes":14214,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":16643,"get_count":5,"put_bytes":14623,"put_count":3}} 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..d131c2554 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2188,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":7494,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":9589,"get_count":4,"put_bytes":15001,"put_count":4}} +{"i":3,"series":1,"stats":{"get_bytes":16765,"get_count":5,"put_bytes":22582,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":22347,"get_count":6,"put_bytes":28124,"put_count":11}} +{"i":5,"series":1,"stats":{"get_bytes":29235,"get_count":10,"put_bytes":35299,"put_count":19}} +{"i":6,"series":1,"stats":{"get_bytes":34719,"get_count":16,"put_bytes":41819,"put_count":38}} +{"i":7,"series":1,"stats":{"get_bytes":36899,"get_count":20,"put_bytes":43687,"put_count":38}} +{"i":8,"series":1,"stats":{"get_bytes":42385,"get_count":35,"put_bytes":49554,"put_count":58}} +{"i":9,"series":1,"stats":{"get_bytes":42057,"get_count":36,"put_bytes":48656,"put_count":52}} +{"i":10,"series":1,"stats":{"get_bytes":45608,"get_count":43,"put_bytes":52209,"put_count":58}} +{"i":11,"series":1,"stats":{"get_bytes":46118,"get_count":45,"put_bytes":52740,"put_count":61}} +{"i":12,"series":1,"stats":{"get_bytes":48132,"get_count":53,"put_bytes":54913,"put_count":71}} +{"i":13,"series":1,"stats":{"get_bytes":45955,"get_count":52,"put_bytes":52796,"put_count":70}} +{"i":14,"series":1,"stats":{"get_bytes":52092,"get_count":71,"put_bytes":58491,"put_count":83}} +{"i":15,"series":1,"stats":{"get_bytes":55710,"get_count":76,"put_bytes":61485,"put_count":80}} +{"i":16,"series":1,"stats":{"get_bytes":54014,"get_count":72,"put_bytes":60324,"put_count":81}} +{"i":17,"series":1,"stats":{"get_bytes":54575,"get_count":75,"put_bytes":60610,"put_count":81}} +{"i":18,"series":1,"stats":{"get_bytes":57974,"get_count":78,"put_bytes":63687,"put_count":80}} +{"i":19,"series":1,"stats":{"get_bytes":64451,"get_count":84,"put_bytes":70094,"put_count":85}} +{"i":20,"series":1,"stats":{"get_bytes":59422,"get_count":76,"put_bytes":65192,"put_count":78}} +{"i":21,"series":1,"stats":{"get_bytes":70175,"get_count":87,"put_bytes":75865,"put_count":88}} +{"i":22,"series":1,"stats":{"get_bytes":65733,"get_count":81,"put_bytes":71319,"put_count":81}} +{"i":23,"series":1,"stats":{"get_bytes":71977,"get_count":85,"put_bytes":77487,"put_count":84}} +{"i":24,"series":1,"stats":{"get_bytes":70109,"get_count":83,"put_bytes":75767,"put_count":84}} +{"i":25,"series":1,"stats":{"get_bytes":69675,"get_count":76,"put_bytes":75356,"put_count":77}} +{"i":26,"series":1,"stats":{"get_bytes":77947,"get_count":86,"put_bytes":83538,"put_count":86}} +{"i":27,"series":1,"stats":{"get_bytes":78937,"get_count":88,"put_bytes":84387,"put_count":86}} +{"i":28,"series":1,"stats":{"get_bytes":86209,"get_count":92,"put_bytes":91635,"put_count":90}} +{"i":29,"series":1,"stats":{"get_bytes":83993,"get_count":85,"put_bytes":89425,"put_count":83}} +{"i":30,"series":1,"stats":{"get_bytes":90627,"get_count":91,"put_bytes":96122,"put_count":90}} +{"i":31,"series":1,"stats":{"get_bytes":91040,"get_count":88,"put_bytes":96441,"put_count":86}} +{"i":32,"series":1,"stats":{"get_bytes":89691,"get_count":89,"put_bytes":95166,"put_count":88}} +{"i":33,"series":1,"stats":{"get_bytes":91840,"get_count":85,"put_bytes":97241,"put_count":83}} +{"i":34,"series":1,"stats":{"get_bytes":91962,"get_count":81,"put_bytes":97364,"put_count":79}} +{"i":35,"series":1,"stats":{"get_bytes":99485,"get_count":90,"put_bytes":104889,"put_count":88}} +{"i":36,"series":1,"stats":{"get_bytes":95514,"get_count":83,"put_bytes":100916,"put_count":81}} +{"i":37,"series":1,"stats":{"get_bytes":107805,"get_count":88,"put_bytes":113208,"put_count":86}} +{"i":38,"series":1,"stats":{"get_bytes":106202,"get_count":88,"put_bytes":111610,"put_count":86}} +{"i":39,"series":1,"stats":{"get_bytes":104285,"get_count":84,"put_bytes":109681,"put_count":82}} +{"i":40,"series":1,"stats":{"get_bytes":109033,"get_count":86,"put_bytes":114421,"put_count":84}} +{"i":41,"series":1,"stats":{"get_bytes":105864,"get_count":82,"put_bytes":111278,"put_count":80}} +{"i":42,"series":1,"stats":{"get_bytes":108991,"get_count":85,"put_bytes":114411,"put_count":83}} +{"i":43,"series":1,"stats":{"get_bytes":114426,"get_count":86,"put_bytes":119836,"put_count":84}} +{"i":44,"series":1,"stats":{"get_bytes":121966,"get_count":87,"put_bytes":127367,"put_count":85}} +{"i":45,"series":1,"stats":{"get_bytes":125866,"get_count":86,"put_bytes":131291,"put_count":84}} +{"i":46,"series":1,"stats":{"get_bytes":117722,"get_count":80,"put_bytes":123126,"put_count":78}} +{"i":47,"series":1,"stats":{"get_bytes":125329,"get_count":85,"put_bytes":130743,"put_count":83}} +{"i":48,"series":1,"stats":{"get_bytes":126394,"get_count":84,"put_bytes":131795,"put_count":82}} +{"i":49,"series":1,"stats":{"get_bytes":134371,"get_count":88,"put_bytes":139778,"put_count":86}} +{"i":50,"series":1,"stats":{"get_bytes":141144,"get_count":91,"put_bytes":146542,"put_count":89}} +{"i":51,"series":1,"stats":{"get_bytes":137743,"get_count":86,"put_bytes":143144,"put_count":84}} +{"i":52,"series":1,"stats":{"get_bytes":136344,"get_count":85,"put_bytes":141754,"put_count":83}} +{"i":53,"series":1,"stats":{"get_bytes":144905,"get_count":90,"put_bytes":150319,"put_count":88}} +{"i":54,"series":1,"stats":{"get_bytes":140411,"get_count":86,"put_bytes":145801,"put_count":84}} +{"i":55,"series":1,"stats":{"get_bytes":142972,"get_count":88,"put_bytes":148366,"put_count":86}} +{"i":56,"series":1,"stats":{"get_bytes":146418,"get_count":87,"put_bytes":151821,"put_count":85}} +{"i":57,"series":1,"stats":{"get_bytes":164396,"get_count":91,"put_bytes":169803,"put_count":89}} +{"i":58,"series":1,"stats":{"get_bytes":149656,"get_count":85,"put_bytes":155057,"put_count":83}} +{"i":59,"series":1,"stats":{"get_bytes":162591,"get_count":91,"put_bytes":167988,"put_count":89}} +{"i":60,"series":1,"stats":{"get_bytes":156642,"get_count":86,"put_bytes":162050,"put_count":84}} +{"i":61,"series":1,"stats":{"get_bytes":158418,"get_count":84,"put_bytes":163827,"put_count":82}} +{"i":62,"series":1,"stats":{"get_bytes":173638,"get_count":94,"put_bytes":179035,"put_count":92}} +{"i":63,"series":1,"stats":{"get_bytes":168876,"get_count":88,"put_bytes":174275,"put_count":86}} +{"i":64,"series":1,"stats":{"get_bytes":169959,"get_count":87,"put_bytes":175366,"put_count":85}} +{"i":65,"series":1,"stats":{"get_bytes":175244,"get_count":86,"put_bytes":180634,"put_count":84}} +{"i":66,"series":1,"stats":{"get_bytes":182458,"get_count":90,"put_bytes":187869,"put_count":88}} +{"i":67,"series":1,"stats":{"get_bytes":184118,"get_count":89,"put_bytes":189519,"put_count":87}} +{"i":68,"series":1,"stats":{"get_bytes":175682,"get_count":89,"put_bytes":181088,"put_count":87}} +{"i":69,"series":1,"stats":{"get_bytes":150359,"get_count":74,"put_bytes":155765,"put_count":72}} +{"i":70,"series":1,"stats":{"get_bytes":169348,"get_count":81,"put_bytes":174835,"put_count":80}} +{"i":71,"series":1,"stats":{"get_bytes":176815,"get_count":85,"put_bytes":182220,"put_count":83}} +{"i":72,"series":1,"stats":{"get_bytes":179781,"get_count":83,"put_bytes":185179,"put_count":81}} +{"i":73,"series":1,"stats":{"get_bytes":204850,"get_count":93,"put_bytes":210251,"put_count":91}} +{"i":74,"series":1,"stats":{"get_bytes":180951,"get_count":86,"put_bytes":186338,"put_count":84}} +{"i":75,"series":1,"stats":{"get_bytes":182832,"get_count":82,"put_bytes":188225,"put_count":80}} +{"i":76,"series":1,"stats":{"get_bytes":190178,"get_count":86,"put_bytes":195585,"put_count":84}} +{"i":77,"series":1,"stats":{"get_bytes":197388,"get_count":84,"put_bytes":202781,"put_count":82}} +{"i":78,"series":1,"stats":{"get_bytes":201793,"get_count":87,"put_bytes":207183,"put_count":85}} +{"i":79,"series":1,"stats":{"get_bytes":207205,"get_count":88,"put_bytes":212672,"put_count":87}} +{"i":80,"series":1,"stats":{"get_bytes":207794,"get_count":89,"put_bytes":213191,"put_count":87}} +{"i":81,"series":1,"stats":{"get_bytes":217720,"get_count":91,"put_bytes":223116,"put_count":89}} +{"i":82,"series":1,"stats":{"get_bytes":206077,"get_count":84,"put_bytes":211466,"put_count":82}} +{"i":83,"series":1,"stats":{"get_bytes":231542,"get_count":94,"put_bytes":236934,"put_count":92}} +{"i":84,"series":1,"stats":{"get_bytes":212690,"get_count":83,"put_bytes":218083,"put_count":81}} +{"i":85,"series":1,"stats":{"get_bytes":224796,"get_count":88,"put_bytes":230179,"put_count":86}} +{"i":86,"series":1,"stats":{"get_bytes":224059,"get_count":88,"put_bytes":229445,"put_count":86}} +{"i":87,"series":1,"stats":{"get_bytes":210313,"get_count":83,"put_bytes":215703,"put_count":81}} +{"i":88,"series":1,"stats":{"get_bytes":225382,"get_count":83,"put_bytes":230772,"put_count":81}} +{"i":89,"series":1,"stats":{"get_bytes":215945,"get_count":84,"put_bytes":221328,"put_count":82}} +{"i":90,"series":1,"stats":{"get_bytes":223357,"get_count":86,"put_bytes":228748,"put_count":84}} +{"i":91,"series":1,"stats":{"get_bytes":233105,"get_count":87,"put_bytes":238496,"put_count":85}} +{"i":92,"series":1,"stats":{"get_bytes":231913,"get_count":85,"put_bytes":237300,"put_count":83}} +{"i":93,"series":1,"stats":{"get_bytes":250888,"get_count":91,"put_bytes":256277,"put_count":89}} +{"i":94,"series":1,"stats":{"get_bytes":244010,"get_count":89,"put_bytes":249392,"put_count":87}} +{"i":95,"series":1,"stats":{"get_bytes":241102,"get_count":88,"put_bytes":246485,"put_count":86}} +{"i":96,"series":1,"stats":{"get_bytes":259402,"get_count":92,"put_bytes":264789,"put_count":90}} +{"i":97,"series":1,"stats":{"get_bytes":254250,"get_count":88,"put_bytes":259638,"put_count":86}} +{"i":98,"series":1,"stats":{"get_bytes":245365,"get_count":83,"put_bytes":250747,"put_count":81}} +{"i":99,"series":1,"stats":{"get_bytes":265670,"get_count":91,"put_bytes":271053,"put_count":89}} +{"i":0,"series":2,"stats":{"get_bytes":262559,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":256304,"get_count":86,"put_bytes":261594,"put_count":84}} +{"i":2,"series":2,"stats":{"get_bytes":259549,"get_count":88,"put_bytes":264832,"put_count":86}} +{"i":3,"series":2,"stats":{"get_bytes":258453,"get_count":87,"put_bytes":263745,"put_count":85}} +{"i":4,"series":2,"stats":{"get_bytes":262085,"get_count":86,"put_bytes":267370,"put_count":84}} +{"i":5,"series":2,"stats":{"get_bytes":264600,"get_count":88,"put_bytes":269892,"put_count":86}} +{"i":6,"series":2,"stats":{"get_bytes":289344,"get_count":94,"put_bytes":294620,"put_count":92}} +{"i":7,"series":2,"stats":{"get_bytes":257470,"get_count":85,"put_bytes":262762,"put_count":83}} +{"i":8,"series":2,"stats":{"get_bytes":283634,"get_count":91,"put_bytes":288925,"put_count":89}} +{"i":9,"series":2,"stats":{"get_bytes":272719,"get_count":87,"put_bytes":278004,"put_count":85}} +{"i":10,"series":2,"stats":{"get_bytes":277843,"get_count":87,"put_bytes":283132,"put_count":85}} +{"i":11,"series":2,"stats":{"get_bytes":286400,"get_count":89,"put_bytes":291691,"put_count":87}} +{"i":12,"series":2,"stats":{"get_bytes":289965,"get_count":88,"put_bytes":295245,"put_count":86}} +{"i":13,"series":2,"stats":{"get_bytes":291491,"get_count":89,"put_bytes":296779,"put_count":87}} +{"i":14,"series":2,"stats":{"get_bytes":287482,"get_count":87,"put_bytes":292767,"put_count":85}} +{"i":15,"series":2,"stats":{"get_bytes":290065,"get_count":86,"put_bytes":295349,"put_count":84}} +{"i":16,"series":2,"stats":{"get_bytes":286312,"get_count":85,"put_bytes":291691,"put_count":83}} +{"i":17,"series":2,"stats":{"get_bytes":302281,"get_count":90,"put_bytes":307663,"put_count":88}} +{"i":18,"series":2,"stats":{"get_bytes":294303,"get_count":86,"put_bytes":299696,"put_count":84}} +{"i":19,"series":2,"stats":{"get_bytes":296316,"get_count":86,"put_bytes":301712,"put_count":84}} +{"i":20,"series":2,"stats":{"get_bytes":325914,"get_count":94,"put_bytes":331291,"put_count":92}} +{"i":21,"series":2,"stats":{"get_bytes":312436,"get_count":90,"put_bytes":317823,"put_count":88}} +{"i":22,"series":2,"stats":{"get_bytes":315108,"get_count":88,"put_bytes":320570,"put_count":87}} +{"i":23,"series":2,"stats":{"get_bytes":287991,"get_count":82,"put_bytes":293377,"put_count":80}} +{"i":24,"series":2,"stats":{"get_bytes":287424,"get_count":82,"put_bytes":292805,"put_count":80}} +{"i":25,"series":2,"stats":{"get_bytes":326188,"get_count":91,"put_bytes":331570,"put_count":89}} +{"i":26,"series":2,"stats":{"get_bytes":323939,"get_count":88,"put_bytes":329314,"put_count":86}} +{"i":27,"series":2,"stats":{"get_bytes":295325,"get_count":81,"put_bytes":300705,"put_count":79}} +{"i":28,"series":2,"stats":{"get_bytes":330729,"get_count":90,"put_bytes":336114,"put_count":88}} +{"i":29,"series":2,"stats":{"get_bytes":286893,"get_count":79,"put_bytes":292279,"put_count":77}} +{"i":30,"series":2,"stats":{"get_bytes":338412,"get_count":91,"put_bytes":343781,"put_count":89}} +{"i":31,"series":2,"stats":{"get_bytes":332854,"get_count":87,"put_bytes":338230,"put_count":85}} +{"i":32,"series":2,"stats":{"get_bytes":324105,"get_count":85,"put_bytes":329486,"put_count":83}} +{"i":33,"series":2,"stats":{"get_bytes":333118,"get_count":86,"put_bytes":338491,"put_count":84}} +{"i":34,"series":2,"stats":{"get_bytes":345411,"get_count":89,"put_bytes":350784,"put_count":87}} +{"i":35,"series":2,"stats":{"get_bytes":335646,"get_count":87,"put_bytes":341033,"put_count":85}} +{"i":36,"series":2,"stats":{"get_bytes":342220,"get_count":87,"put_bytes":347600,"put_count":85}} +{"i":37,"series":2,"stats":{"get_bytes":335888,"get_count":85,"put_bytes":341271,"put_count":83}} +{"i":38,"series":2,"stats":{"get_bytes":354870,"get_count":90,"put_bytes":360253,"put_count":88}} +{"i":39,"series":2,"stats":{"get_bytes":362562,"get_count":89,"put_bytes":367943,"put_count":87}} +{"i":40,"series":2,"stats":{"get_bytes":361173,"get_count":89,"put_bytes":366620,"put_count":88}} +{"i":41,"series":2,"stats":{"get_bytes":378647,"get_count":93,"put_bytes":384028,"put_count":91}} +{"i":42,"series":2,"stats":{"get_bytes":331756,"get_count":83,"put_bytes":337127,"put_count":81}} +{"i":43,"series":2,"stats":{"get_bytes":349359,"get_count":87,"put_bytes":354736,"put_count":85}} +{"i":44,"series":2,"stats":{"get_bytes":372794,"get_count":90,"put_bytes":378178,"put_count":88}} +{"i":45,"series":2,"stats":{"get_bytes":339895,"get_count":80,"put_bytes":345269,"put_count":78}} +{"i":46,"series":2,"stats":{"get_bytes":349793,"get_count":84,"put_bytes":355172,"put_count":82}} +{"i":47,"series":2,"stats":{"get_bytes":391126,"get_count":93,"put_bytes":396503,"put_count":91}} +{"i":48,"series":2,"stats":{"get_bytes":393747,"get_count":93,"put_bytes":399125,"put_count":91}} +{"i":49,"series":2,"stats":{"get_bytes":359497,"get_count":85,"put_bytes":364880,"put_count":83}} +{"i":50,"series":2,"stats":{"get_bytes":335354,"get_count":78,"put_bytes":340737,"put_count":76}} +{"i":51,"series":2,"stats":{"get_bytes":377428,"get_count":88,"put_bytes":382807,"put_count":86}} +{"i":52,"series":2,"stats":{"get_bytes":397377,"get_count":91,"put_bytes":402757,"put_count":89}} +{"i":53,"series":2,"stats":{"get_bytes":377648,"get_count":86,"put_bytes":383023,"put_count":84}} +{"i":54,"series":2,"stats":{"get_bytes":378144,"get_count":85,"put_bytes":383521,"put_count":83}} +{"i":55,"series":2,"stats":{"get_bytes":402297,"get_count":92,"put_bytes":407666,"put_count":90}} +{"i":56,"series":2,"stats":{"get_bytes":409357,"get_count":91,"put_bytes":414731,"put_count":89}} +{"i":57,"series":2,"stats":{"get_bytes":383876,"get_count":87,"put_bytes":389247,"put_count":85}} +{"i":58,"series":2,"stats":{"get_bytes":382651,"get_count":84,"put_bytes":388027,"put_count":82}} +{"i":59,"series":2,"stats":{"get_bytes":390398,"get_count":85,"put_bytes":395780,"put_count":83}} +{"i":60,"series":2,"stats":{"get_bytes":391327,"get_count":86,"put_bytes":396703,"put_count":84}} +{"i":61,"series":2,"stats":{"get_bytes":403559,"get_count":87,"put_bytes":408938,"put_count":85}} +{"i":62,"series":2,"stats":{"get_bytes":398461,"get_count":86,"put_bytes":403841,"put_count":84}} +{"i":63,"series":2,"stats":{"get_bytes":413241,"get_count":89,"put_bytes":418611,"put_count":87}} +{"i":64,"series":2,"stats":{"get_bytes":414009,"get_count":89,"put_bytes":419390,"put_count":87}} +{"i":65,"series":2,"stats":{"get_bytes":411165,"get_count":86,"put_bytes":416534,"put_count":84}} +{"i":66,"series":2,"stats":{"get_bytes":434970,"get_count":91,"put_bytes":440353,"put_count":89}} +{"i":67,"series":2,"stats":{"get_bytes":445201,"get_count":92,"put_bytes":450574,"put_count":90}} +{"i":68,"series":2,"stats":{"get_bytes":430937,"get_count":90,"put_bytes":436314,"put_count":88}} +{"i":69,"series":2,"stats":{"get_bytes":429730,"get_count":89,"put_bytes":435104,"put_count":87}} +{"i":70,"series":2,"stats":{"get_bytes":433862,"get_count":89,"put_bytes":439244,"put_count":87}} +{"i":71,"series":2,"stats":{"get_bytes":410758,"get_count":86,"put_bytes":416140,"put_count":84}} +{"i":72,"series":2,"stats":{"get_bytes":448697,"get_count":92,"put_bytes":454070,"put_count":90}} +{"i":73,"series":2,"stats":{"get_bytes":411359,"get_count":84,"put_bytes":416880,"put_count":84}} +{"i":74,"series":2,"stats":{"get_bytes":428625,"get_count":87,"put_bytes":434006,"put_count":85}} +{"i":75,"series":2,"stats":{"get_bytes":410158,"get_count":83,"put_bytes":415529,"put_count":81}} +{"i":76,"series":2,"stats":{"get_bytes":459400,"get_count":92,"put_bytes":464775,"put_count":90}} +{"i":77,"series":2,"stats":{"get_bytes":468934,"get_count":94,"put_bytes":474370,"put_count":93}} +{"i":78,"series":2,"stats":{"get_bytes":454825,"get_count":90,"put_bytes":460202,"put_count":88}} +{"i":79,"series":2,"stats":{"get_bytes":459668,"get_count":90,"put_bytes":465047,"put_count":88}} +{"i":80,"series":2,"stats":{"get_bytes":442113,"get_count":87,"put_bytes":447557,"put_count":86}} +{"i":81,"series":2,"stats":{"get_bytes":443786,"get_count":86,"put_bytes":449157,"put_count":84}} +{"i":82,"series":2,"stats":{"get_bytes":465035,"get_count":88,"put_bytes":470400,"put_count":86}} +{"i":83,"series":2,"stats":{"get_bytes":445039,"get_count":85,"put_bytes":450418,"put_count":83}} +{"i":84,"series":2,"stats":{"get_bytes":465888,"get_count":89,"put_bytes":471412,"put_count":89}} +{"i":85,"series":2,"stats":{"get_bytes":483185,"get_count":91,"put_bytes":488566,"put_count":89}} +{"i":86,"series":2,"stats":{"get_bytes":508765,"get_count":95,"put_bytes":514221,"put_count":94}} +{"i":87,"series":2,"stats":{"get_bytes":481159,"get_count":90,"put_bytes":486596,"put_count":89}} +{"i":88,"series":2,"stats":{"get_bytes":465605,"get_count":86,"put_bytes":470971,"put_count":84}} +{"i":89,"series":2,"stats":{"get_bytes":456384,"get_count":84,"put_bytes":461844,"put_count":83}} +{"i":90,"series":2,"stats":{"get_bytes":474727,"get_count":87,"put_bytes":480100,"put_count":85}} +{"i":91,"series":2,"stats":{"get_bytes":488491,"get_count":91,"put_bytes":493871,"put_count":89}} +{"i":92,"series":2,"stats":{"get_bytes":453904,"get_count":84,"put_bytes":459278,"put_count":82}} +{"i":93,"series":2,"stats":{"get_bytes":484358,"get_count":90,"put_bytes":489888,"put_count":90}} +{"i":94,"series":2,"stats":{"get_bytes":512672,"get_count":91,"put_bytes":518107,"put_count":90}} +{"i":95,"series":2,"stats":{"get_bytes":457983,"get_count":84,"put_bytes":463361,"put_count":82}} +{"i":96,"series":2,"stats":{"get_bytes":484698,"get_count":88,"put_bytes":490068,"put_count":86}} +{"i":97,"series":2,"stats":{"get_bytes":511434,"get_count":91,"put_bytes":516878,"put_count":90}} +{"i":98,"series":2,"stats":{"get_bytes":464298,"get_count":83,"put_bytes":469728,"put_count":82}} +{"i":99,"series":2,"stats":{"get_bytes":473466,"get_count":84,"put_bytes":478926,"put_count":83}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline new file mode 100644 index 000000000..67f978723 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":27514,"put_count":10}} +{"i":1,"series":1,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":27865,"put_count":11}} +{"i":2,"series":1,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":26841,"put_count":8}} +{"i":3,"series":1,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":27915,"put_count":9}} +{"i":4,"series":1,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":29060,"put_count":12}} +{"i":5,"series":1,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":26553,"put_count":8}} +{"i":6,"series":1,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":29124,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":27335,"put_count":9}} +{"i":8,"series":1,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":28412,"put_count":11}} +{"i":9,"series":1,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":28564,"put_count":11}} +{"i":10,"series":1,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":27544,"put_count":10}} +{"i":11,"series":1,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":28292,"put_count":10}} +{"i":12,"series":1,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":27301,"put_count":9}} +{"i":13,"series":1,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":28397,"put_count":11}} +{"i":14,"series":1,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":28852,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":28469,"put_count":10}} +{"i":16,"series":1,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":27613,"put_count":9}} +{"i":17,"series":1,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":27573,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":26700,"put_count":9}} +{"i":19,"series":1,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":27910,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":28543,"put_count":12}} +{"i":21,"series":1,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":27484,"put_count":10}} +{"i":22,"series":1,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":28044,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":27154,"put_count":9}} +{"i":24,"series":1,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":27729,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":28262,"put_count":11}} +{"i":26,"series":1,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":28864,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":28281,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":27039,"put_count":8}} +{"i":29,"series":1,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":28217,"put_count":9}} +{"i":30,"series":1,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":27386,"put_count":9}} +{"i":31,"series":1,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":26667,"put_count":7}} +{"i":32,"series":1,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":26689,"put_count":8}} +{"i":33,"series":1,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":29135,"put_count":11}} +{"i":34,"series":1,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":27730,"put_count":10}} +{"i":35,"series":1,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":27593,"put_count":9}} +{"i":36,"series":1,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":26305,"put_count":8}} +{"i":37,"series":1,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":26381,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":26904,"put_count":8}} +{"i":39,"series":1,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":27586,"put_count":8}} +{"i":40,"series":1,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":28116,"put_count":11}} +{"i":41,"series":1,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":28315,"put_count":10}} +{"i":42,"series":1,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":27942,"put_count":10}} +{"i":43,"series":1,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":28307,"put_count":10}} +{"i":44,"series":1,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":26451,"put_count":8}} +{"i":45,"series":1,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":25968,"put_count":6}} +{"i":46,"series":1,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":26768,"put_count":8}} +{"i":47,"series":1,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":27113,"put_count":8}} +{"i":48,"series":1,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":26766,"put_count":8}} +{"i":49,"series":1,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":27768,"put_count":9}} +{"i":50,"series":1,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":25589,"put_count":6}} +{"i":51,"series":1,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":27258,"put_count":9}} +{"i":52,"series":1,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":26919,"put_count":8}} +{"i":53,"series":1,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":27953,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":25734,"put_count":6}} +{"i":55,"series":1,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":27941,"put_count":10}} +{"i":56,"series":1,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":27976,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":27712,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":28023,"put_count":10}} +{"i":59,"series":1,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":28255,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":28496,"put_count":11}} +{"i":61,"series":1,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":27367,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":27394,"put_count":9}} +{"i":63,"series":1,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":27501,"put_count":10}} +{"i":64,"series":1,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":27789,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":28339,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":27257,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":27560,"put_count":10}} +{"i":68,"series":1,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":28259,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":27113,"put_count":9}} +{"i":70,"series":1,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":27770,"put_count":9}} +{"i":71,"series":1,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":26373,"put_count":7}} +{"i":72,"series":1,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":29067,"put_count":11}} +{"i":73,"series":1,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":27529,"put_count":9}} +{"i":74,"series":1,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":27947,"put_count":10}} +{"i":75,"series":1,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":27792,"put_count":10}} +{"i":76,"series":1,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":27897,"put_count":9}} +{"i":77,"series":1,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":27703,"put_count":9}} +{"i":78,"series":1,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":27045,"put_count":7}} +{"i":79,"series":1,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":27620,"put_count":9}} +{"i":80,"series":1,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":28670,"put_count":12}} +{"i":81,"series":1,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":28145,"put_count":10}} +{"i":82,"series":1,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":26903,"put_count":8}} +{"i":83,"series":1,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":27754,"put_count":9}} +{"i":84,"series":1,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":25882,"put_count":6}} +{"i":85,"series":1,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":26450,"put_count":8}} +{"i":86,"series":1,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":28702,"put_count":11}} +{"i":87,"series":1,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":28210,"put_count":11}} +{"i":88,"series":1,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":27851,"put_count":10}} +{"i":89,"series":1,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":27493,"put_count":10}} +{"i":90,"series":1,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":28326,"put_count":11}} +{"i":91,"series":1,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":27496,"put_count":10}} +{"i":92,"series":1,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":27010,"put_count":9}} +{"i":93,"series":1,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":27242,"put_count":9}} +{"i":94,"series":1,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":26799,"put_count":7}} +{"i":95,"series":1,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":27441,"put_count":9}} +{"i":96,"series":1,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":26195,"put_count":6}} +{"i":97,"series":1,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":26333,"put_count":7}} +{"i":98,"series":1,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":27600,"put_count":9}} +{"i":99,"series":1,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":27547,"put_count":9}} +{"i":0,"series":2,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":24138,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":24138,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":24138,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":24138,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":24138,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":24138,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":24138,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":24138,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":71,"series":2,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":24138,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":77,"series":2,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":24138,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":24138,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":82,"series":2,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":24138,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":24138,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":24138,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":24138,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":24138,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":24138,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":24138,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":24138,"put_count":2}} 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..b83e223ee --- /dev/null +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":16957,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":16889,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":15923,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":15695,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":15629,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":15551,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":16513,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":15921,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":16072,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":16439,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":16294,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":16363,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":15924,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":15999,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":15991,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":17322,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":16296,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":17175,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":17037,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":16069,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":16740,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":16298,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":15483,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":16070,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":16665,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":15626,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":15925,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":16142,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":16000,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":16808,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":16436,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":17619,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":15849,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":17178,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":15702,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":16290,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":16071,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":15997,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":16657,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":16440,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":16293,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":16884,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":16588,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":11133,"put_count":2}} 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..d84eb86fc --- /dev/null +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":266491,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":257836,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":246528,"get_count":81,"put_bytes":11133,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":258731,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":269892,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":277895,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":253688,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":244118,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":261238,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":267250,"get_count":90,"put_bytes":11133,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":247536,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":260116,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":251442,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":249516,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":266163,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":262195,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":272231,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":265168,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":255856,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":269006,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":250187,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":282751,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":252070,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":260660,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":260160,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":239583,"get_count":79,"put_bytes":11133,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":265024,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":266671,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":274442,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":256022,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":277883,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":266982,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":262070,"get_count":90,"put_bytes":11133,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":256448,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":249022,"get_count":81,"put_bytes":11133,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":266876,"get_count":90,"put_bytes":11133,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":245415,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":269056,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":264272,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":253375,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":258091,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":243447,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":249141,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":251888,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":264174,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":258121,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":241572,"get_count":80,"put_bytes":11133,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":254362,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":246996,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":261938,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":269981,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":258752,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":259423,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":263537,"get_count":90,"put_bytes":11133,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":257615,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":260749,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":255598,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":278597,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":253717,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":269693,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":258100,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":251759,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":278232,"get_count":94,"put_bytes":11133,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":260531,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":261868,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":262696,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":272423,"get_count":90,"put_bytes":11133,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":270334,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":261406,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":220833,"get_count":75,"put_bytes":11133,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":237960,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":251797,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":247447,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":278770,"get_count":93,"put_bytes":11133,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":246382,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":246300,"get_count":82,"put_bytes":11133,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":253088,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":252908,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":259910,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":267248,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":259625,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":272858,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":255094,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":281548,"get_count":94,"put_bytes":11133,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":255832,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":268789,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":264134,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":245157,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":257535,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":246060,"get_count":84,"put_bytes":11133,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":251084,"get_count":86,"put_bytes":11133,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":259741,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":256740,"get_count":85,"put_bytes":11133,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":272969,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":263014,"get_count":89,"put_bytes":11133,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":257747,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":273134,"get_count":92,"put_bytes":11133,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":266752,"get_count":88,"put_bytes":11133,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":254375,"get_count":83,"put_bytes":11133,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":271999,"get_count":91,"put_bytes":11133,"put_count":2}} diff --git a/actors/evm/tests/storage_footprint.rs b/actors/evm/tests/storage_footprint.rs new file mode 100644 index 000000000..4802d9cc0 --- /dev/null +++ b/actors/evm/tests/storage_footprint.rs @@ -0,0 +1,243 @@ +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_actor_evm::interpreter::address::EthAddress; +use fvm_ipld_blockstore::tracking::BSStats as BlockstoreStats; +use fvm_shared::address::Address; + +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: Address = Address::new_id(100); + +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_address(&OWNER).unwrap(); + 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/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index d3b4df38f..c77c2444a 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use anyhow::anyhow; use cid::multihash::{Code, Multihash as OtherMultihash}; use cid::Cid; -use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore}; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_shared::address::Payload; @@ -112,7 +112,7 @@ pub fn make_builtin(bz: &[u8]) -> Cid { Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long")) } -pub struct MockRuntime { +pub struct MockRuntime { pub epoch: ChainEpoch, pub miner: Address, pub base_fee: TokenAmount, @@ -141,7 +141,7 @@ pub struct MockRuntime { // VM Impl pub in_call: bool, - pub store: Rc, + pub store: Rc, pub in_transaction: bool, // Expectations @@ -266,6 +266,12 @@ impl Expectations { impl Default for MockRuntime { fn default() -> Self { + Self::new(Default::default()) + } +} + +impl MockRuntime { + pub fn new(store: BS) -> Self { Self { epoch: Default::default(), miner: Address::new_id(0), @@ -284,7 +290,7 @@ impl Default for MockRuntime { state: Default::default(), balance: Default::default(), in_call: Default::default(), - store: Default::default(), + store: Rc::new(store), in_transaction: Default::default(), expectations: Default::default(), policy: Default::default(), @@ -410,7 +416,7 @@ pub fn expect_abort(exit_code: ExitCode, res: Result MockRuntime { ///// Runtime access for tests ///// pub fn get_state(&self) -> T { @@ -678,7 +684,7 @@ impl MockRuntime { } } -impl MessageInfo for MockRuntime { +impl MessageInfo for MockRuntime { fn caller(&self) -> Address { self.caller } @@ -693,7 +699,7 @@ impl MessageInfo for MockRuntime { } } -impl Runtime> for MockRuntime { +impl Runtime> for MockRuntime { fn network_version(&self) -> NetworkVersion { self.network_version } @@ -906,7 +912,7 @@ impl Runtime> for MockRuntime { ret } - fn store(&self) -> &Rc { + fn store(&self) -> &Rc { &self.store } @@ -1036,7 +1042,7 @@ impl Runtime> for MockRuntime { } } -impl Primitives for MockRuntime { +impl Primitives for MockRuntime { fn verify_signature( &self, signature: &Signature, @@ -1131,7 +1137,7 @@ impl Primitives for MockRuntime { } } -impl Verifier for MockRuntime { +impl Verifier for MockRuntime { fn verify_seal(&self, seal: &SealVerifyInfo) -> anyhow::Result<()> { let exp = self .expectations @@ -1259,7 +1265,7 @@ impl Verifier for MockRuntime { } } -impl RuntimePolicy for MockRuntime { +impl RuntimePolicy for MockRuntime { fn policy(&self) -> &Policy { &self.policy } From 6d800c449c0a59bbc4728a68924b4c64fbff130d Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Sat, 1 Oct 2022 13:37:13 +0100 Subject: [PATCH 071/339] EVM-889: Visualize measurements (#708) * EVM-889: Visualize measurements * EVM-889: Check in rendered charts. * EVM-889: Convenience target to run footprint measurements. * EVM-889: Separate script for plotting --- actors/evm/Makefile | 47 +++++++++++++++--- .../evm/tests/measurements/array_push_n1.png | Bin 0 -> 16330 bytes .../tests/measurements/array_push_n100.png | Bin 0 -> 21008 bytes .../evm/tests/measurements/array_read_n1.png | Bin 0 -> 16527 bytes .../tests/measurements/array_read_n100.png | Bin 0 -> 19889 bytes .../evm/tests/measurements/inc_after_fill.png | Bin 0 -> 16258 bytes .../evm/tests/measurements/inc_one_vs_all.png | Bin 0 -> 15108 bytes .../evm/tests/measurements/mapping_add_n1.png | Bin 0 -> 16110 bytes .../tests/measurements/mapping_add_n100.png | Bin 0 -> 21305 bytes .../tests/measurements/mapping_overwrite.png | Bin 0 -> 23406 bytes .../tests/measurements/mapping_read_n1.png | Bin 0 -> 19046 bytes .../tests/measurements/mapping_read_n100.png | Bin 0 -> 20467 bytes .../tests/measurements/storage-footprint.plt | 20 ++++++++ .../tests/measurements/storage-footprint.sh | 32 ++++++++++++ 14 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 actors/evm/tests/measurements/array_push_n1.png create mode 100644 actors/evm/tests/measurements/array_push_n100.png create mode 100644 actors/evm/tests/measurements/array_read_n1.png create mode 100644 actors/evm/tests/measurements/array_read_n100.png create mode 100644 actors/evm/tests/measurements/inc_after_fill.png create mode 100644 actors/evm/tests/measurements/inc_one_vs_all.png create mode 100644 actors/evm/tests/measurements/mapping_add_n1.png create mode 100644 actors/evm/tests/measurements/mapping_add_n100.png create mode 100644 actors/evm/tests/measurements/mapping_overwrite.png create mode 100644 actors/evm/tests/measurements/mapping_read_n1.png create mode 100644 actors/evm/tests/measurements/mapping_read_n100.png create mode 100644 actors/evm/tests/measurements/storage-footprint.plt create mode 100755 actors/evm/tests/measurements/storage-footprint.sh diff --git a/actors/evm/Makefile b/actors/evm/Makefile index d0813d5bb..b1d4d69ca 100644 --- a/actors/evm/Makefile +++ b/actors/evm/Makefile @@ -2,15 +2,17 @@ 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 -.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 +# Run storage footprint tests. +measure-storage-footprint: + cargo test --test storage_footprint + # Compile all Solidity test contracts. # This could also be achieved with https://docs.rs/ethers/latest/ethers/solc/ @@ -23,3 +25,36 @@ $(TEST_CONTRACTS_DIR)/%.hex: $(TEST_CONTRACTS_DIR)/%.sol | solc @# We could use just the .bin files in the test, but this is a way to stick to the existing pattern. solc --bin $< | tail -n +4 | tr -d '\n' > $@ solc --bin --abi --storage-layout --hashes --overwrite $< -o $(TEST_CONTRACTS_DIR) + + +# 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 | 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 gnuplot)" ]; then \ + echo "Please install jq. See https://stedolan.github.io/jq/"; \ + exit 1; \ + fi 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 0000000000000000000000000000000000000000..58b583e024c79b0b59eb1193f3acf0cfd49dfc55 GIT binary patch literal 16330 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..49c7f8198ed9b628823bbceb9fd3361d521cfd1f GIT binary patch literal 21008 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 VNojWIPi}+fTAH806drLQ{|nBG<{1D0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..faaed96b60401340dde6efffe77720db8cd843b5 GIT binary patch literal 16527 zcmdVB1yodD*e^Vkl$6pf-5@Zeqyhqhi5Lf-CPN0s>*l>)bO?L8H;YlXvglVJ?jzPzbsaga&PIH!6bK+d)bW^XNua zP>5n<2wDUd5(5G$hJer^=wS444j&&dRB^5!7)MV}FD53YrKM$OXBQkCoRX6A`Sa(- z#>VdM?xm$AfW@w;=*#Vw#pBDnhP!iU^!79y{RLIBy^S6N0o*_~!2iK)F>FFHMwj<7 zbabwOC?HTzWbXzDBO)5qJAkH)M+fWdhJ>J1K&K(@Ye{~)%dvG8PL&bFhlf+|LRir` zpl$ee0%&OZ-SkTxMMEdy5K2%8C1r>r@D~ySYYc&*Loo6yqTd03F!VcDbYmkL&cCS+e#Y2Iy0aIo1pRKq*jN!X9V1WX9>R@yp zRTKhZM(+b&GB?O87Jq&CT>Wj#%-sZ4B&cz%08fH9Dz#94f7Uks*b0AZMQ(^$^gPL@ zT1q&fmMBSdh*=(z{2HcS`s=%}Q3mnoC!>erlTId06$Rfp9|~Y!jXmz|P^U{6Q~J^b zP@quqabMBAqOt z+o06?O7_P%AkXG(T6K3w(}#G-gaG-EihP+WdGOZa`*+39iNVJlSokCZiE_v*!Vd&t{pt?~XKnQn=DcEoUtQ)UWsPFZW_Y8< zIG{y!%tVmWbp&_^mw!{jaQHoJsVvOp@tPt|$c>Z)R1Q6a8d1v|P|g;8kJj@gZdY}q zSDsn3$<`qhNge0TLI8OtqG6!jWj5<>qSJv*n>enrBmqX$I$UFJpw}WlhRVF()Ak93 z-pA|eowXcACVlkH#N9|u>W`$#(a>oD2B@>99nO?ELA!Fv%|`6)ctwX^_?7EF(ahh$ zb1HNtq?^HBwj$BL%ivUYcLBM12%fit-j?e0du0LnsJ$?a4x1Oe(b!MR`72w{aU`VtF-;BTy#c_sdHD032K2tGZoTuc?M%ogu@&3lXm`boQimqADTA+eN8 z;O<d+r=x2d)q2yX^yAQto9JcqY(P-uH%qcv^(=z`-=9@c3R2v?(zOQS zZ8ImbX(A@Kpf%J-^C~9qhZy+7)1375i`V6mGOL!I1wNT9+ec%~K6*S)Q$ArB%wu!R^hsVb8yR!;dAXAB)&X%MYTfD9JUu#la*0ar6ysOdIwZ@PZ z3Zr~u$RjzA8U!2Ir1{iTyEg8et9qRN4l?@nx>nXCy-N}GzTfhXx_R@t&IVqOirYO;H7m>|a#CxwBu6%=PstL^b_!t>o$X#M@wt$fkxeX!mz|EIQC`zAIG@rO z(HFGg(t?}}!D4tSWGtRKKVZ!r+8<`z$n0zP6$j_JT3QbcfYU3p-aE;-`3$7XgViD( z+c-=lhVLM>JdCCej84Skmi82p-TSX-vgBl$-rgBImQ(&9JmkPD@!pB^OY4U-F}sg< z@Em?Po_9G)jiE$~x}H3fsq%i=?$N9v;>&)j_1;^IM3eBP(Q{kPc#%L;;-uF?v9D%h15^yi|1?;Ao zEK(|5evQHG`wj#{g9oP1Q}K#+B&@d9g%zE|Z$P;^hWELw2(D}q_Hr~es3SLT4G)H& zj(JHn4o-uKlQt$>DB*a>9PgB8P2UkELWQ6$l? zV9OCHX=ttwGz{(fTA^c!L9e1?sxZ*gvftlL=+{yEf<{jKM6g z`2raS$j^|=)$uW}%K|=-5zj>ySa4O%PRi5IdOk-t|v!HS=x$$xpN#iq1MciUdE{}AqC>3{LbP&3&>xCbuCf? zA(`(hM1OeC^tW0%&I&fwrR=XrUKOl;2zsJo313Qb%j%nWXfR8Kn1*|;TPfg~%<`EN zCQ)1j#yN~U8hKU`QKE!&L=`(Fe#CVWCr6mLZkXVq9BFZjsHdtSN=SM;%jE!5KOQdv zNDV8LxJcEfwOk+hIhh_2q$StUn!18M;_@HP%el1D&CM{c7WOUX3(AN1@|E9PtZ z#gPk76D-(}k1#_27;+LvP2tB�sbWzG)MAzbR_ZK{|T%#z~3LYqB8CRqGz!x&}|S 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a4d64fed884e3a62ef26df507407a05765e6066c GIT binary patch literal 19889 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 RkVWzHYgdhROLT5V{6Akzwjlrj literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..12c56697053c2841a9dd108f04b2163f4fbe2e90 GIT binary patch literal 15108 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e46f9d98662c49b72e96b256b4a1e179888f5b32 GIT binary patch literal 16110 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 lifRU*`_Hxef22JF2NDCbVr3Z-g4hHF6nt(I` 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 IbHC(&15^GUf&c&j literal 0 HcmV?d00001 diff --git a/actors/evm/tests/measurements/mapping_overwrite.png b/actors/evm/tests/measurements/mapping_overwrite.png new file mode 100644 index 0000000000000000000000000000000000000000..0859ef06bec258abd6b72a46b612e03dbc527d16 GIT binary patch literal 23406 zcmb@uc|29``~SNmwT*3a#*CRZLWsyr*`YEmbK6ja6f%~f_BJQPmbntb#!BYdGPjcu zGG|VP%=2)T_xt<#p6}zF?;pR%;~Wp;TK9deYu)P}uKRU8m+)KqImR0sru_QrM1 zI|#&S5P=})L6XBqPG57rgg~6qymiw^>-hK>KJxbM+mjy+2t?3v1L7F5yxgFQXm3ZT zIZYfl@F0Rz8-k86-3^LFAXI}8$3e$|$3L<}Md7NdzrMru*xA`-WMuU8^zPle7Z@0r zl$7-O^XGn zm(n6jKYS$95~N$ty`s7?+V?sTVXzW;K+m+fDBJ5`7A9OQ$*0~6TlxR+ArV@ZsM)uP zyVcs3^;ivJmy?WrNY&)8>)7=<>nvhLu8P;$h)d?E89DVdzTYu3-M!;G%~XBn+vfK{ zudrt%nuI?A?@uFwUSG+ zP87u0?ulc)um!>PXiRdBa9c8K<0v1(4d3JCcw?M4iYC(~WR*Ij-<>zy_1!3;t-AC! zqi6W@mo`^>qBr?hU5NsvA2{qC!ncTw0rQ%_uu%Xp#ua!^S9-<*CmUn6(C?19BILnn zB1EBxrw=#lLDyZLa+@lDT3&<*N=7BHo1L1ppF3mLN6$UTGUVjD(Z%C7VuIC6W@~ES z9Cf6wR3E7fzoc$=^U8-5QzB&kLe}}_=$3<%=bZC<7DdH!l0me20E^8THqJV-Us>%s zUg}Iue8lul=UvC7!{?UVe@|Bu8;RLTH-$6>%Q*361#drV2>-o0*s3{ap+WCSU3Q5w zKjpGRtsqmiOPfU}R)`qDLik8A8G;HLc#p(*o=0&}vKTyez|xzPBO^%-y{7cqM%k?& zcP!t#01-Li2d=4l|Y<$`N_q}I?sZf z|D*k>75yzJ?qVFR`K%n#3@DRBPtXjO-*t_Mv+y(`3cRPp(3@zKB%?AcMy+ItSq|0* z5~bxI%*wDgjOXde5!S(Xn)sF){JSL^58@xO5SB#VJ!KcJ7K!o>e(Flak!^j}dA&~n zz3SpdS?1+@J`rfUs{L~-{+6@n{SRCNTYQ~%=wpjs^!w=#O@``-vB;i~zS}*oHqnZO zR@F93dsuOZ;Sj7UxcZ~#9Hv>VpV_67+{KSKX8xKhGfPfx_x9{ZCF$)dJ56NRe|p#`K$i0Lm-TQtRg!~5cS`y7d2kYM#-0R1*d-7E>)Lf?QzWAwFtXGUEsZ^_%9B=F85q z*(zQ|Qu_pfkXgE;EUMqP%IVs88rV9l+9l3=3i0#%m!C?{wh`DgjpPVth@E-X$6 zQRsm7>ri$^eMrK7Y?IhzDb}yf-(qeXp(W%2^yVwJYH( zeS#_^7K1_k)rcJ);>wQw1p8@mF6qBZn2qA8-;N)!HJi=~tcRKvE}q+-ocRAL*RVr? z1Vwx?KjF#kqSueE1cfq_h1vVgt-JoN?IrrP8^eli02h38}oDy!n2sZRR`@@_KqOBw3&m`F=L^bXTEmUwVr$uUmA{+Vm(t5qrGx zxKc2+ZV9h7thP~;x+neeiS(d+l;;a##!FF-p)AqTn$3#Mp1@0`_oZ~(Q>OBje7z~1 z2fo)_t}I2aL7}gOf&U@Op%bX z?E&-Zu`v3YcPXdlq4NSAc-gxXitroEQshA+Tt*BPv+Jx=z4fu-s?aTU^Y^2=RL;Ip z3MJLtr~Y7CAJ9IWX2rZ~q9cdM8d{aJ>|%eQjGEZ{TC@~%*a?fhimsi6WTpoI;^}jL z-Ln~;lZDvuoEK}OgtA0sc6*L~OKW9~-d`{#wfW(JQ=WWDQ(O?>uRa1}`z`C|5t<3`3v##m#+7QL$&= z`DXRKd|hql>$9ieVtQ{qjm{RIMsNso^#!9(HJs7adD48Z!zR{fv5Z_Q}Am^C-Kud9jTj@QYKDozE+n;$Rm++~$0vdM68riMFk*deeXEc+?!Ia5{>*(O^%b98U;hDA zN9PCd{y3vL5LchbZR%tlFf=Bs>3%h7Wt4nJ4QOa284Tx#T({QdQKpMMV8!H4(earB ze`_@1Yq$9BI{EjaI+oT-quKjM#EJBLX=i~>X=7y^1!v7cNv&hpboB3Yv~IZWheWk9 z+mXfq#gS;w7x8&@VB2d!bF~1cj(~YA^W03%l9q;mVVS03lOd$mpV~Y{Doklhwz(QH zPwmfxZX6%RSc{x^CZoC5Ut@e*g_wJ8aO9r6%B4$aVNI%qEW!P(P~&v(qeFAwR#I0N zCts3>woq2w{BGlqndb}CU+$p@zu*$w$OW%-MNM~ucuMP>-yFWx{o?|qz{c~hJ zp81ic)Q0YU7ax@eY;2%aaOrkhxoa z|EDulC^>cM%%>>wOE)u?tO1fmcjTM?^p$)U@;j?;lhdD=5KwZNzi)?Fa~rO7#KhH( zmWjs&H?8p9US8qOnkcc(ER@H%JQ>gIX|UtA?Pjb=y)Yt3g{_TvMVW6#UHj{&hA^WV zzkuXneYsj}xU_KON38<9WA`$xzHp&UNEc^@_9+f=g=rf-Fqss z?1xuYxbkS7&z-=osJ|eUK$`^7l^DE;i;AiF5m&Xj$y^Qgvk!|BgVO0yEVw6ZC=-cA z=+A3KO$vHxb?FCy8$Ds?9P}Jq`&A`4{Wy&VqGaRs1!g{%U~h=u&IHE>LN-~jhq5RW za%@`LHGzgg+I+FTcOv#UdpU!os%wHw)Hj(;RP=PDXr*iF$ks!0aPVbj!h|-rzgh|t zDN?|#leV~vn=U=>TT|GW%6Zf+idML{mi%pr`3>tv({qpqt)rA%e?OZWD`H3cbn{wY)|BSwa?_G5F86l6B@t@~?a4^oI}KH_Bl8gsA=k4>q;9w0 zS02EVxW4|D%?t1*69=Dfo%~ZTW-)7E*7@Quby0ERMraJmgf9orT)sJY@v|fBNF{IF zoUxju%D^5=s0?O9+r_zq&WRQejtWcmSFz=HM1Ul#$@SsqTNP4BWWFtYGgrl7ms7NuFKezW8m!%)N#vs7J*ACL4~qbXOi{ zd@pSJVV@DR>q%*@`7`r9>3Uq>%48Ry^b{Pk->4ce=aT_SbY}Z2MzgT-B^3*%v!^pW zl(~8*>z?qf0Z^msfCi!xTMqnIv{k_Hkeio?hl+|*nXYflUZ6J#=Z#ayeH1obm+{x& zYB8m=)#t;Kxf<|@UHpx*^swo^iAaIfr-943g!6}cU9u^GT0l~+N7%H}J)Jsb*}Jty zf2;28J9Rl>54o<5#tzmEl|Ki2L`J1JA-~g4=wg*R7q1w<)pUs1>Lo=lBroMCpY-yq zw((RJldoGS6ZQnUSoGf11>%o5Mo4;OVcztnBc{A^g4AZyyh(}W5;?lH#DcX-GLWP< zDe$zvGP1oo%1#o0_vA}^G2B{xaBMpG{{l$lXbiwa<;UdUG3P@Z!3(1*D$f)*-*7Pw!pgynfH&m;;A+(Fkm<5%3tlF9>}}djXf#q$E|%f{rgqm953b zcVZ)}++~mp74wJ7FFQlJ>^e0Unqnmv8y>iJ!@YX59pz_hcSnvH;`~QV==ku z8wCD1eV&loDBd^%kGWs)h+BvDddu+Ob1(Li*I~3g#O>Dxz@N1N+>Xs8XesSJ8kixGciSuTaB zZzP^Kmatt889<>3;TrS5tL_!YKi0Q>L}7wWBA&)pZmm0F*0RTvv(CS=Naa`RNc(FF!9joB|YIPS6M-m!M++Ww0!hqlkE{zK7s9qie?`)v^32I zMWDe6RbVf6M3Pt*Co*2&+zGkc6w^b}@uEn~jrUU51Ua}b5=V_6zXHwOQ@Kwu%@M)q z0zn5{AMp3nL)QL`VlEu>;{%IkW-8LP+7&*uS84#2WkV=#M%Z^X@Xxsvb%1vsyeO6k z9?>nx++`-nS#ei9&&NHf`D1{@gn!{BPW91Z=U0`@fxSMjcc9|V9~K_8pa3rGD8MC3 zWlaG~oBEpIAr-F3I{lL(j6drF!5H`gWlWqMmKaevN;0jUx`85GRU1nqz7)Q3d#2If z!{uDE1c=E-$cKoC!{ z*3F%h=PkY%Mo%bPVZt5`l5weDvjzg53J)OicwEqAJw*fE3$$5DGru`Zadt${FN>GEMPp^Y7)-n{80K zJ4E%dQWconp+2v80a84LGmfuwJ7f0ZPnfeBFZ>(?m*@%oqIXPyS)ha*Tt;Vt`Xkob ze{T$le)w5!I0@>{K*gG|>&+U#%&*kjV~=*HpEE84x8$z!qlcN}HGsfbNn=Wq*`Syh zQN?ccs~FntWee!Eay!(Bb~Hs5=*kwFoXO55lQqfagoDN|27*Zr*yjEUyptI*nqVfw zj1=1ddC?6bFTf~g8#FQKhmA0CMXe9+eotdnfmiPst%hvfD8-9>XPHdGL{nANfLHV; zzaI~u6ZT<5E;P1ZE6B9$Y>-tKMrV4b|p2PjFODsKa=Vr?11$OS`q>q-bK zPi5b&_+ARmnPBlpw&JM5D-qOUw@xcOLXrZL$!pog(M{Q*2aiX1)q(NsL@pgOzqZ>0 z6ijGzAI&b_f$1D>X3)-2Yo+S|g9GUj@e609B0(k7d0!sl9h$}Id<6;|^CL8xAX6YgKQG0u4ldmnyl*y*Rw@j}AhtwO7 zIsI$Ec;@BK(oz%)(w3f(rJwm!!IH5|)K3b_Mm;Npci3TZ6l^n?hkD#+4OI5i1=IkZ zFcHjAl*6wb3W#dxvFAJiFy`3vZ+8TJ*=P)HYw~8m?z`EeC42O~%9C!yx zj`L|ApK;@M>if8?z6N#vbMyq`-)4B`7}{?M;ek(-RDr%&Q{X{&l_{`y!3Ig%#Ykay zH>NQW&RO&CgFx$>H?X;%fGf|>)Lim}nYbAf|6h7_K;K7F4Fm$)1 zyvJ$~XiL`0#L-pZEre$_ebrZYno3MGg%RfvegWEJIR^(WBgqv9)l_dKu!4d!6;=E7dq^nrHv@@QaFHPHu8;RB^*a|y)Sw8a{)h7XvUsc0IhL0wBa^=warRPeBu5~ z)>9-n&eD3Q0hm%_NG%SH7tsb0x>T6@^RMwwMlKaJ(-L#ye%LFe)y>$}Y!!YL$KJy_ zjgvi0PbGiwP0VFmINGu4d1z78jxxGk!i5$pPRqsMvX&1duw2pntAvY5 zlZ7i+f;H*dj~-k`FW0d$nq*q7nBhtf%@eKUXzE`d9KLEbO3V1p1 zxH#{BxkQ8CduPmt9%nv_&1!X}&fpsoAun`&2J?pS}_QXaK|ptYo)5`!PH6(G-}amP?=Va)SOi9xutE zrS@}w52;c&*OKD=a1K?Lyu=S5T{%>NZvtNh#uy|%WKHfy6+ig0*<@a64AhZ1)#|Kj zQ)1cVF14X}&=zk#GEK7(W}d&i_!_*{W!d!SBmd3no5qqXn(I#uPESfmJLc_W$Ajuc zX;rwH@Vz@&JeRhm`N-MBIUr4#`0UX*TM+<6m3$Gp)E!4)9J}47VEbXJ;7a&XMMJYk zU;1k7r-%Kq4;@>QpI{BluoR*G??67?&XY;~7fU}cR}0P|m(aFY6a!va%KAt!pkB|% zDeGZcs2v?#J7zpZWcpv_0O+vUU1PTe12`DZL6Vr{(C2Xu1=54hE7y)m(4VT@r{Wnb zghuu{OG*wg%#o_NE4;ou=(tF9($guUpg@YL0q@$lzqCNbZiRn!Ee?@bL3UCgrXoW0 z7a``E3Gj~pd^B@@i>`(SAZbzB?A$wPQzs!j{06tVJ>EQ#D1P--{qPEY`1q)Nf4O>O z_R%}Y{JreOHdOfbiZt~?MSYWZK4|;7(R;`xCvZ@IG-KNxO!KlRJ|3ypp%(Kx4q3^; zZH}HgAk(?zGSjQ+J1#HL9q^zpInOAwy`RExgbXQmY498?LEbU}ipZDg<3S&F-;aWw zDqFMCxMrWp8vhg zL5+J2?ZzmB#N}$a0Xu>vP5Bluc6glI%`X1}jGh?dL7SEhZv`Uj0bsGb>E4w97zR)fuXhUK{NtnDt9Zt=Q5%{h)Pi7A)( z&YH4VX?VeE4iWb|Lj)~u@ofG|p=qF`AQ~f`AxI1kEc5GYudW%BVPQ!a)zmSQ`l66I zshs}!<)SY8$m0@rtZz~0_d`|&le>c*0z{P=yL!$P+S`K@qt}c96SsazVz5U~be79* z$;4mzT`%pkFUi~wuE}fCLcoW_m&UbQ+R<};sd_-$@8gZqcLN4HPqyv@?jf|)vzRhL z7SriU{LD4>YH)4+ezbo^gNlQL<;e0&MD*m_Pz7!VEdRk`@WunVh$V`}%)K3P<(XP9{HH$XY0vT77x|j$BqIbg> zpom84AFD^6DmFk_q>u%z^Mjg+%T5cdm&T;KXo1}0}K2u7P$vI5H_oP4(G2*>nqoG%AX^x0;*eM++gUGL4J6>2{$%C_0`9AuN_Vffkf$_h-_^h;%=MejBcD^)o z)E>6P`G#a0y||3=Asv!bf5eBK7n-uh&ie-vK_aUM3zn9|L%W!{o*QB9SfKPcl_jiB z9gAN?LRIlkA$QjVQ(#}fMi{M~nDq+QV&tEvMr84kKUX#ajw-w7KJpMb&*Kte!r*1x zv>eU48;oNpuRYl6PG3e56jtEfoXR;Y?@PfA0~HRG3A9YWFW^c`F9&)ycmG9-dJ1?8y!EQ zv4+!XOOnaQ%2hhw{|cjOlLh6ZYOe!rvc^;k2I3Dx5#C6Qg+3#}BmXOO=iB1yA~U0j z&a5_(U>_(>p=1iYliI^Mj1P7~J^g_eKsa-iw!uRzRyJzGb)b}~XqM>|wD#em&$9>D zkereEt_lPLZg73!I>J_Wr$$ZZV&7@L<597LqQn)x7xb zbQeSa_z|h>mJ#rl6J^qzy-dsZe9tes-Uz50^GL#HS(c&tuYSA%+>dxJqurJ_jCVl4 zD`F;W&y7Gmlb?@`abUXYGw4gzh2h345loKHN{59eL3LL)$WIOn6&H3<&)jL_q z#of2y%?B$K)RY%Eujm2M#a>b^#kn6rx{eU=p;mukvk!}A^wBpj=M6k8i`)WZRvLp} z;Iq6+O2)og>Q%b*=zRM0R4>9Z{47+JL6CbvR4cW&{(Y00%9s-4G$K;X+FQ&IT;KWL zkmtigwEH?_n+^|CM=hmR(H4_D9`yRabUQE6 z&k-LxEzZl|Wb~T-FiTRHLk&=CBzt}~G#W*y9AZugA+zKoHqp5+>Q`3r5UZ24uh_qL zNV^6Atc!Ej*j%kMP>-z~`aUC}^d$|f!qKnuEB}WDkV^@phqUD>#u+4T+PD_=Y5-%u z-o5LSo}wo>X&}Xvy^#4V@kdn73;v#&)k~010(kHcJUWe)6~t#PwXBINw6<$Y!wot* znXmsef{waVeb4Jl zSE#TGfe|-T4SdV|nP6olRH^y}_#n88_V$ZQEb}vAJvWMGN6^H`Eyj!JCO1@n90Ex_ z0=401MmTT|vz1V>{2vYA9PcFz4?1uq23bO2Ly}5a*AtZYU#l74d16#jY*4S6vL5r3Az(wvv=Zk*7!)7U4Y8yCZU zIZvHbR{i+)yNDDV2BiUYOkpY&v=}PVNEW?iuzSyO%6GEC{yCw1aIxcP{h;un+kVt& zy%4t^Ko}nvMGwyldv2zo2)kMIk0?qh#qO)Y8szE|Y?2L$!t_=WSj=)7nGZ`F01jM+ z)e{4F54yxlthvJ8L8n2s7}zL2QL74|OfDC(_LH%X=1-+S$zJ4G!8O=I8$H4lfmPN9 z*7x?GJ$VTdb9h--A@(am;^i;-5rsS@<`!yF9UV67+jD_@Y&{YjjB0nwB`rQCQj03n71n_z zc+h82aPv}+P~c_|Q1PPQ5h^OQfPPq8>$|Et|M8?$4Ya0p3Y&XXO2WE|JU-gCB)+{ev>!j; z=`Quq;D^!X-E%mHGZ5L_T{xyZZy9y_+M`e3Wv)ptwk}K2O};=sK7~z!1#kseU7QIE zi~8mqpAq$0g%4wSz(!!;M~0C@cf4&aYdp^ib7?u{ssRf-y|cj|V+G+>p-vQ-+!#C; zeF3V%r&BtuhP-~LG(b#zX|_OfVK(<#%s*vNELZfzUN$3ROVL9H1?CH!4`w@l{P!u0 z*dJrV%>AK&J_h6hjTwICB_xF2#W2_Tg@R>{uB(}z@h@&L()O4;YxMbW{H_)((PyJ1 zEBw83o`<-R7C*#+dvd`e5Z|HmI)xoucR3Tz#X%w#f#4_e>)08 zKVI|Rxf#tP057e@uxAn;C{uoP-fIULxEV9CoS_n>%)kPf8_#i}0<%Y0nRrCtB#BJC)ag=-;c}?qu*^!fN zq6SczB1v2daORW;6EWqQd`Cka5SzF!@#JiJ#lL1bkz&k9YECr*(^*KjWuHlsM92A$ zOCPSFOhjAoGvxnVZ-!FB8%5B40x@X3dt`V05k1NTR!1}+Fq&W#zeV$Z3C0A2Uta40 zV{I#vnR3{U`H#)^e!t{+h-<1qxDmWvFu}2C&Fa_rY#pamJPfU)>#KfbfHIlfjT}e+ z6$f39qdk*wed01TLGW}a!J`d)3qti%>Qg&c3EHFj(^LUg1UAdnBn5o)rWLOac#~nR z5V(Z0L070)Px31g+m@uUUezGam~rsSd;!IBJ@a2mJyK=T}ojF9{dLMSNgZ|JE4qNx6S zdUBhRc==S%spCnfGeVX&x60HAKThMa$gs6ZChXz#(z4iaW4i)fn_F_~|2gr75GE8< z+?@soCH%pl5iEpD(~c@$cYgVhs!p(^W|k73H||vKJKh$JklHqV^Yn5R<;!J*W+4IO zg83~)-R@52oYIiKTT&aI<0&j#JeUV${}xv~S(c*I<6Hq6E}aYErq{g_f4vIsxs5#8 zDfr0Fb*lnT;6J(ADj(8;g83cD2 zQ6^UZ_OpvT#HxR$tdlnQe;G7>yzL{Zdg>qJ#;35x{~FJOjZ^(&+=$|-G4sF1sgU`i z{~BjTnHc|TT$G2H_pfndRlxXPZ}Z?t>tApGH-m_~|Mqyq&wuJsBIN$DNft!=&-MO2 z?SB8p%&KvEO;L-f?-~!${j1E6a|@}PwFmZ{Erxxj+%R*pZ~inv$RN^9!r|BDlgMhS za+*v#CHuau(|K8~s&|4<%fUIP#G6*#ymfi+#kekHKK*sN^-IvkwS^qX7hum`;@!g= zl%H2XOm^{al#OwBLbO4Z?vFF7Ear;ObVbCWxGi>P)8Xe(lSI;&_Zm-)wa#LdBhx!x zb=<^lt_7sLUM+ickXS_hMeba+y=bHay6*U*ekk?J`)#j3XKqZKX$}l4%6f`cZM%0j z=DOI+l9A2-Y@D0>DI7At6gk2f%yce;Z_#_WzDw%gaS4%Lw~v<%R53inxNbFoA~W9r zDDLKZMY-Ofyz=Jot?pSwt*%sF!Qg3>R}G0Z?X6%*2TR7ax0?!QzJx^2ULAWfy<|Bk^-4BX#COT36ujx?@!M3| znm(6e>TQRU=jdf_yTU7@M&psT!`<_o1 zzNaO{2~9ChncfRCsE?CL*LW22bxOY73n{)Qg*zpu3dAA}cp_Y~ zcUZ6v5f3A>JLUMo{RHjayf(k|DkdT7>n!ncbfC=V7NKQFpSs`L7L?eT2m_AqdzPN$ z^XKW^=I?!_GkYl0QXVCny7WjWizaE(uxR>g%V_~S?iZKYk~$TpnC!0Ik1P_JELgGZ z4nL0r-CJ^K9iPehmT*)(U8S5>zFoL;p4X7kB>i_bI|rL|OOAC%QY7W|YYuY^pC)gE>v_FUON(}6bH0*sT=gys)lbUZC&VnZdVhW0`gEIy ziz}H}j$=p{f7W|mV|UV-u$CfJ_e7w^rGvxS1#R=RLXh}6k7IOhuDvqZp+)7X{M`+O zpCgjFoqkl+F7pGdGdk=i+uu?-w4*Z5+^K8BipTGA*EHWLi;FjYic(CW?H+>%9&li z!2-K$bKylNIfH%;0;B6(c-JMk3+$Z7jzt%*tCm$3+i^QzF_pIy)d~(*SxZdW}^m&&-qTuSa$fxp8elC?f?JB zxX-ythUE$Im(1CIN_0NcBPHFd7wls&2Xp3kKX4Rl_eu3iD$npMPxxbLr}A#!N;MGq zg{U^txwOt@ z>U?jid>ewj046Ty{D1MrMgKQ%Ech>PjQy84zFOY>AKq9lZR4Bc8?TyI3pgjoE=Arq z`lq>D!&;~YL;617qxNDVd=BZm5vbp&Wlj)?lUedevJL3jlK6s`_A3MGG@CV+K_u;7K`FBzm1$sKgOt1xwCLciUDNj1=%T?4D zd-HvVf)e4B)GJ3Rs~ZT!1NCdgj3(z-3tBP`d;3J=sosMPAWQGr}dV z6lsF$zT+L*7Zu0ZBi>qo%)9ZAs`N^zok#4hjycg?#cqBEH3d=fI7AJr7Vy3e!J7hB z6obfo3tWrCulal=zo?;|(bMISOAFCn7W@!y|AJ6S>&!wZpv7F*@R9(keDwdD zHST9|aS$R78ewD(M2Svrz2|!TFmVnXgc0pmtcVr5cy<=2vtyAdQtO5g3@hj0)|}oh zW?vekSSuBAc~iwj>@F8xOY&d-(cyvbYTw*zfH%t9q8?z@kMWLDR!`wlG@{H z`iF$iyVBjo`iUM2V;t&0138ZQn<$ax_5F+&ofmC!VClODumC>7z<>y%EqEgi+4_tG z&}G#_L!3Wj8+(5*ySy;i;doQ3cbpL=d7oBnn?KaFnKc#$j#-ncdE-@{VN}pNe>fLX z(?+F-D{DmT?bD%}zuqS@w_y$6HKdqQ-3{Ok-!|IFi;=5~FEdp}F={gx-h>U$E?@b> zVshY-S_6GKieF)V-oa(l>;(IEU_eQoQ3tEp5qHX1%B9=OjU2lM_F7aGyZdFNO4GES zlq8m08cH6PMU2@!y+%+wAGuNdDQ{q`U8**DJ%Yj8!ZWNW@3(H-Kv$NF=}5$P!*@*q zM9W2->`>e*hSzRIm(O6IY+19s-}PI{I$jw&|4%SQw`$C6wYf^sW4NofUkgqr>&Yd9 zZ>%(JKbIz63-t1i8v1-*aJus(Kup2+RpVvHFrTn=jefWHr`EDu=sk()u>qA1R!p)g zmwddK^S992l)fHtv{>|qoyGO+wSd1LUbc?oL(O|=(~wv5Tmb*FVv(v%N}<|csEFJC zESb0(g}N`V;mpXHZrMIT;yphn;6UKdEp?#RUxFmoLMj+9?n4t{Om))A5ypoF(Ssi( zmzOvph7;8IJMsiIuB*GD)L&nA{|&}t08RXuXqER5J?tt=oWcGQC3Z-)X?tUZdBx}D z$`;U|729#PaZyb$mH~H}Y zq_s3DtvWPRpD2UdGo68K<<2rIVbQE~>*Ek*_72Z|m!yqa`&&EeTWM63LEF8>>?q=`t zRV5%EG5!y}mKi zZnB|vn^7HS{(}~K*Bzw#hfP=N8s5mPzFGIXTeNySQYdkO{TcQgPSAHA8|*Q%yXG;= z8#f@Tr!~qxh|Sb*vC% z58qK8h6&jOf5@RiLL133%JdW?yd!_)mj1%D!1v&_*wIowDe#%$+0c-yAqJKg;00_I zN0Lle`uo=Js!x)T@T}?_HET6*VFSZC(!)L*)Rnx#>qKGm&^|7v4`wP68Ebq zZx|_r0Z8k-%Wb@I$F?1h!${}z^xEcL#I{|GHnsW5D1t7v*8pzsS4N286Wqu2FV0az z<^*ZF*cub3ypP!17yTYSdW?f7i{>Zr+XyG9X!JNQksDYIXIOWDc*F3doW7sqx&XuAD`eHHf@!Vog4t% z@F|}5{_W&(aKr#}&Mm(CzWy*Y4cne_f^=_={@t|VA>G=ac|;IGQfmnO{Z6$uFIKWb zL5H=X!acbPmpg}}usoqgC0I-*#FS+HkYcn*YD*fNtn2n#oGB9i{bmXH&FVeA6M?lN z{R2UB4Nxr%r~+=2OLI%ySbS6rXQ+$keRp(dmu!`(1hM4zxb=$e>ejl~YmiL)I^A6k z8M7_UtXii2xhh#0Ol-s`4Tic&`ega+`&MIswI+oC8^rLN4%_O&6m62>07P%`XMvH12_HT(zHW>O7S<@-qN)vlTKgl8{flej&nt}YCw+M0jvm7 zsy_gt*3@VkEfNw&p3B2w<&JG2YZ9MfElC%YCP&xU7_9ysY1 zl7pvM?>J$z%I7rzr&MLPPs}BxYSO3#dI8ztY_Ykf5H=}cSwe-0PC~~B))%?K;OckZ z)o!Mv7air+BX_%t@Akfe3RuK``K5@8NC#tP0CAe&;29&{J)9c1BVV`!}~J5nR}s+qpN6gcP*~?Y{63KX(3Q>E9#!v>JRh z?CyN-r#|pQ@p;PRyfO8+AQZ9RwcA%`tZqdEnG27&&3^#j%hw^pvK z=B^Ly-}fl&XlBNC6z#=tXA2x~){8C3AW6wI@@-AuM(3B$XDn5h*Cp{y0diYzH&vk0P5|_Rje>n{tPtJYwJGkPUY;J;Fk&#$noIURf`TjVpy+nB>45*$miP+ z;FU+E|3yBqoP*I- zoK%igEniWfZbhA{W!aQt?tjtb$MTQ&fa0l-{X;khCvkUO^<$}Kk$%PJZs(k!2-m$l zc8%p-4WT9e&61W3ufI|=-x@XMe_HcM>^;ywF;V{V#WLkMFM1)l>AkE(m#AIL2R_y( zGxt#ak9+q=Sp|vBblJW*X70vOs4DD^hJb+^N2v1XK`IR2=|9HqZqJR&9l8%t9V|f+ zSF`U^lIj{UCltATb`9;+L@*?6X@DZghBS4X@)N-qx}4|#+G2g>+ppD)k4|A{;7;o5 z;qOel5P)ro54FH=_C_B)#5(!TQa_}e9b<`82~`JzSSRMje4VvE zq^tP|G$KO$eeX*bQfvyZmS%d12&SzJeGG5{EGnjwd68QK)|I;S72mACF7h1xuCdEFyigSD`QZ zX&M4Kk6(gO1F?T6BSg-bzjhs)l+8ouwB(U2^7QB2tch7$fJKvdVml^gr0NEI+Y&e`AnaQd>%Y;5dHH<#=a zG&mt>97V{b8ATJP(hZOcZ(b7K@S;&9#i5sq#)m`e#%4baZvv-SKkQFNbYEwNlIssP zE);4Gf07s6I&F$Z_g+NxH=*>LBf*WqTE3iJDtf}^EPpD+NwBS{O4xepJ1V z@+b0m7c0TRqbMaxOoboqX4Sh_?TSHCGhx}6?xpQ2>=3@osa#Kkq_=Xa^neg)k33X{ zkQGo?Wfla+(QTE0C9XU~q5sm)=Upm<*{hG(`6g_Cx3Umgjl1Uqbx zCG2&f2z2l(P)|xyPUgX0E}g$D1gGPdW&jNEB8gYU_=sOyd%kT<=1rx!HIxx-J|o3; zK5E=t7;B!bNfqLT->iCAk^*a?I3&r7VupoKJQabPp|Jvh)(dM?Bnvw!%72L57@QP_ zPNv!^5jvmY!_QRt*)l_mqCm|?&GYV1+`MkI`r*t%0 zDSL0fE8vKrSHAh>Li|Txa@xTxu2BD%OQN*t<=;`8?-PAhZj#iP%e}oRtuQ}7M0FrSxV5*ajqmkvB5%I z<%q!{UHdEgyt1GKcIodhKmJJ#C&4(sEjeSPA{E@&4-hn)g#*b~_c|D69$aT8_%+V5 z_A@BsnEBDp9m*~nhJgdc-IBzNVt;4FJ_LfT;^hAV9E}%M-U3uyDIq_87Od;$o9r9R z{pMsatX}gukqT2Vpg3879f&Mvk%H;l3m$}zf|CK*+>{EO!^;C1NYTh;an(kUSSdmh ziBf{39jPEc`Fq$TZq}uuTf=rGnc&4msQ6mFf=lsn@1bEiHGO|74W_s>1hp_>CLek1 z`|4PV;LGq{Fx9VrX_X@yqu{lV^i68l20G21<^Sw<^Z9oti=s^4n~pV z1ywB4w0GSrF1i?RWUO^^e&3FSf$3yTRibt0XnhC#~KAML25PZh_n%UUSb@BDPBQp4atpg`X_VqbqocuXu_z2VPbB7zq<&Dr6t;dNZqjQHfeGHZ16#1ww_}tVojYuWl&fivyBcfPoLaE=wsFZc~z;sttN`^P@F3B~m`|5N*FsZP6}V52!t0%H-QW zv*Hhe&yR>O#W?-E3ym)FU8g3A|G9$orQI;*tIPoL$|j4gz*6wQ^eIc$Qj-aq=sL*z57@ilN#R*OfyKZ8in|f~2Hqv! zxwfR%8)L^GaAxfi=%tjuf4Xz-RM-GVn*IUjsEeunE5VV45|yhYbr!6ze&z0Dl_n5M z9t=kG^voOTI?)r{{;yWfJf5np|Ko<}gvuOcDno{29x`Q0rehvALK{&bp-|==$(URY zp^gaAp=}N!N9JTcT?s{|kfCJOZ&A;4?|t3p`MrMk_4@hG9@biC?X}k4>-&9wK55Uk z2QEVIgT(pJ>|ds;#?Xbe>Z`|BXghAoRi?g4Zhg7zU?Fkm z*SZfZ8$7m&Kwem7SVQU|B+eF=Dygwmgr_Fcine2cE@)+s*ab+Hg|X3rr-L&`h{C%6 z&PLJT3r6gjz@jysrNfMy@!n<+zRO~{SZe~ko@#VgpJl||@LZwk2yAHfd4Gr~tb>_6 z8TxprFd!NUv|q1AUPLmauo|t%gZ>m+9OwkANY(l59=UL&z2T3H)kh5>IKo~@1-@UG z#DqKf83VX)Hm^%w+~2y2-<>E=WmbbS2RB6K@RYvJi?Y?8F+dBzA*Kt}Jy~F})XcB4 zfhq*NE0IGpcl2L%J-KYm?2V-2q_u*bvOlCvp0QxPmzpW13cI|pZv zu?5y3@W3n_NuQgW> zh^+liS)~ElTUD7RT0kT~gsMR=@0>sny~=yYE^mi*a9xj>z*M*q`yH~o2s5b({cI5) z)Lr9w^B0rmfzT=euK-HVITxhr5%g{Zt$H+sWW|LYFdez-?M_W?l|_RIX2Y%w1=|s+ zo56tFF~03AV#_|IKT4(i#_mhn@-TC_M?8>^T0oogsyu`l2Ro=HWZchnN7Mj1$h*$~ zT1^xt7`KB@oDy@eJawGz<|LL3qqpg@q77g5_-l(X;pWGdXfSfsQ?WTzU?!M^ z)Ep;BB^|*9l}2OB17MGqey8cRBGhx{iMb>6mivqta09a~Y!3y|v!7`}>vm3yqg&!l z-9Rwzc_0@v)qA2-1PlE%p3XNNDeGfDPQX`eyJB;i?BfvQ#sV@62sb6E+jS|cO&o1j z67q1=?<3l1r7SPx(Pm|npZ?orgJG3|IB{uf?1iA`5>5evY5psN2l4N34|K?(S<@L4 z82B-S5Xz9rfg4dm2O1iOE;huAxG%~7P>W<>>lQ>0kF3&QMg!82bGGvllbw~BwW2Zg z~8~bDStexCLOvBe;3bt`=;v@roYp`aXDGq>P^RIfE?5J*7Ved;OeQ4ibEO z1=Mb9sEf^;FsW+SC~aeM$Ue}1tGKuI z&1+Mca!q!D3HPJ=g)VsgGJqY;Fc+F|!+12TCOqh?Zp;4g!u>LA6xhuXz?0nh}Y2Dn6n?b zRF|fRO}?&L5$;8ObtlmlYG$lv5k`qz>{wiz8(7mPerB6FsS<$sz0y0rsA4rD-Tr8T zOYf9Xx&GsVz)kBSI+V!y@Wsq{+vM%#{Sg;Vx4s%4^sHS$l@2p*w9m}48}PbI`x`na z)oY0@Yt0C5aE24l1EdrIlEKCMX;It^Yo)~H<_s&SeUlz6U*ALXu)47) z82tjc;PQ^&3v2w^kE4g|V;wj?QV=8eA7aMYe2hR$PxRx&=}9kd3hTbxp!!zGF^TwC z$__ea>FPIBVm}*$cknMXN8CXGm>6nMKE~=y`sNX{pf~>eiwP)(P2+k(pirS z)Us>0BCv-KaiGyFES>?g9ZgA_HCgrK&g*H+Rg6aJ@C|%?EgoijzujFu&(}Hb&*js`|RlTN%_M`ZCu%oUPX8KcVl|Ws!jZ2YM`Gl zi&Z$#wE4w${CJdE|uW-33)1-#9flx4~;^WH`DL9Sz_*w#~_6lgm5Jb-UUlTaqNrOo@u z1yI}QK@t$8>d&H*d`e8r?iN@Xc zb^W~lK8q}NkH`C&#&piZ4dZRB8na=69DoqabXiD4A+x?I)aOB=0OKk8y;}W zbgc-M;@6fS7(iBYlUgvRl^SHJhk9CN3T-6&3SeBWn6$^O%oNR=5 zAnj1xRL#0J%55;lt4edrI$|KPVw`ictQcFW6IE2o&0@*6i)(hYDc?4`=NH1=xGOmL zUQ9QY|5>|urB6frX^tbRFm(wbm>cUC?cW7DrBhpI+#c2z{1h8tBdCK$CX=rhY=o7l z*9i=FNa{qkGH4g}8|#aK%gNxXOf&Xz@I;SDSmGwB(ND|XUYgG7m3N5ry_;SzVtBCW zleeMjljzIkkx0Cfqqj7YZJ49q5Maw=iaOrsY#wn2Emn+xlI_qgm*Nu zi(eW!i*Ktd^Ul={AGJ}4m72e8DC4qA_(Z3#vkPX3Nqe%8Sfcz9r0%s^6x-FL@BPxM z*%<}F2`oK0!YojwpA#lbxViy_vN%8QN7@3%x{7+AZC*jjfW*>sW8s*4AOIOg>_U>*)YUlXan(%YV>wDXf}FN* z=k9*Lc9@BzXmR>vY>d__yp>U%I5{J~0ZRNAbE=w+R8V~HVbBi8mZuj4?{bY%`0aQc%p!Ew?RnF_oYHB&UtgxkuYaj-)ZOas2Ucxuc{zun0qtxk^^)Mu-GqsPU3 zJu8rVoEn7w9zRFicU!`F-_y~?^fFzI5hbi(%|QX@gZaZYGgRazWZx-7ft}E_Z*zSZ zRcj8VEb7BixsIk+V?W#l*RDSZ#&Y-2QN#ILhwx3l30Bb9g=)|5?6nlA4&82@_AhL7 zm@gobs~<$P;c5sU3Pmz9#6=iZWk;^i(vzB$$|lo*$YF`U>O?_FS&Tdze? zQQ-MO)KlPtA#9I#9t@w&{b8(VTvg2tj@5r)*9`i|cv2!rxZ@*^(emYtIi^MK=MD1p z-3sr10Q-hd3zG^1qpjYG(FZVFl3w^X*$A#jvcW?QY6?SoQ_U?bAyq$MmzEP74{$=) zdimcRDKsfzq1QxV5|-Z$n(Xu7A}vdRQUSpX@&reZpq?(72j0!L6V7(J1(;6Y1jBEF zM@T;looFy4@=Z310>(TKJ3F2FL0R;H!%=9ItGi{skt}$1(e{VJvBku7ve}Q{gI)b) z^O$4xOX;t89-ijqb-MGZW{nD`>4~P@3F;cW*K0ME%$I)O{>~T9Z9Yd5C zuRV+2gF?BPNP|n?jKX_OqOau37kVCU^$K`(3SIWO zHX4Z=OpScZvOqQsXik$)&I;NvoF-1js6Hd8*VSzHdQw*qM>VQh;K#=tsvX*El4!{m z_1(vbR1%v1nOtXIPBxF|OhuCiIgx^gAt@dx0;6(k>Mu3;VV+;L(X9Z-`ur zL7J&#swoptfWWP8vg$RDonw76t^97aCgOA}BboayybHEofds8M+N{DLJGB;*y*BK_ zAIrLZd-dz)saXp9PI8_`x~9eIoGYAx&E zDPi*SXGA&ODKABfA5RhG*t98bsaT9%D^M?B!f$zUJ8onAY+> zB2%U~GxJguma~ER-WkPN?@b)8P71BNat49Jc7X!HfV}sCS zeY2Ww4b}1^djke^^p&OLKy1UE)EB|$rUQTtl=B{ItEYY;#6)aFV9M;U=`{|j4}5~0 z5g&Zc^mi|g>}Va@eIC?*0&)LAhyMqR_aEIIT@0kVnPpyG*7cOudpNKu0m!Kw{r%p_ zPZ37Le_E6Y`Sx`m#`ZKTxNiUYLD5p3OTSkx_o=wnQv9?tJIesR=-qVT!6||j;Jf#b zVm|MJno|n7VrQs4iid=^;jw5EJVo;v}c;9^-vi6qY(*WQrefTw(fD5vg&r?#$HsUG*m@5jN8 zt$)?=rXN9r%X8DaKJ5~#G0_zbb*DrL?}K!mcL;p!Yv#|DKxxr1R zrKd(b>NP%@j}lMgcak?7NiwB2d^x4mFhkzlZ^kz_z(JFuJ*)Y2i_!;ck9&%A2!Y09%}n|=u$XjcEN??V~B zM8Pa$_F~zMF{iR|ySnLpk@uc-^>nE8pQ%{;$w=D|HrmEIr;8}9y_9y*R2OsT8YcDyQ7pEB{=XGo8 zp+GtUfhK#qf6!lQ)ACE}%F~yLF+ zmL@z+a_(O2p0*d-$ZT^><))sB^5Kk=!vqH2h)oi@aU1lWmf#LN*n6t!Pjqzo?d18( z41*%3sVD0ilQSw)L$67FzjLfP&8Oq*N{Ov@b|;gAxKexxA(9ZU85A8B8<}2=pSySw zSCu`dTx(G@NACfxmxP-v(aRgv+ivS=we(zWU45gDUmZU9aNRB;Y$8N!t|^1jd3uq`VMPAO)=EE)4=*(70n@rcERgfs}%Rg7aGk2oyo=01-hO8yz=5 zA3uULT<3@#yr7619T7xnWJDYYbRz;pj39;+$BRWpfmd&Q{{+0p&dx3;CueAA=;Y)S z9v+^Rl~r3?+tJZ6FfcGbKMz!~B`ce>kyPSkOOcH z=a1u;j5GiH9Czu`1E47&&|u8aD(Jk4M9|O(kvWAJerGEpf~XBTitt%Z58hgcZ*6dY z_mp~f_iI4}FR>W3f!as~jm;FyB;C0IbC-%>21PJ4N8AAZA|j9-5lCXhdHZh=3xGc) zvETu*qk{ciYi@n_a635lAc1L~}4*)qj%eDJxyhu`cFZmklBXZI)oD zM*Q~Qh;Upfb#%4mxb13E!!4K{nr*HBI>)afN9(zd^y}_LQ@@yN905|ohz+Fs%!$NJ z;!Ey%XBhUYQOQ{8(O8{#c6vrZ17s_#ucnmxb-Cs9qay#Amb>e4$6t20n6;Xg>lK1! zAn9IHs?nux(`G)312)mq{ahzm)NobT2fNmtd%Vl_=5WPn>jA=<{GQ>C#m+*rkr?Pr zr0chF&1{Xr%2>Pk-a6;_LYmk@LnVpasem~hl%omU|LI=ptIY|XljVK?@l$M5F^`{l zC_ETFn$X`|F}S#3=((>`1+qzOJ$Alav_Yod?^W(LCH^zYP)Sw8aay)(DfML-*7lHK?LzEoq#L)AS3ZWcJXdz=?SN!x08Cw_3 zT9IY~KQ|$9-tNU{F1&0=*3)c7tK+dRO8)8*A-$k}^=KA~d zjCoM_jL|h0GX06CiM4Zf|Hk7%Wgtd-&EvSbyXXq|PtzbmlQ#3UZbzEe_fBIsfVl)p`_&x{+jteO#AQ*aK; zz|Pnx^swiycpB{cP$*rLyqnn^;bp0nlAMNqb5}1gi~zK=msOn zuMP*Aa?xn7KW|P_yZ)4qTo-?!A_I;>t}}_NdTUKf+gPV5fS#N)i{o z&X)0F?>O1$%U=35xt?a)j)*85MJCc9`GQrNT2z-!EGwh02^?hR!&>oxq*d2d* z%!6lMLD1d1g)4*|vl*Y@l=nrA;9DeoxTSg5(rZ5}wm4=Anwbl^I%U1H^Pf8YsOBWO ztW_okd#CL&Xlv^K!D>!PCvs!Z8-ya;5}R2`szsYhV;D)q5bKOfgJfz`g3KV~)_7U{ z&|@c{{X?dGP54TDy<{L_9#A*03nqT%Q4iazeRsl7^ zb*(zRM^|0zBDj~;(HG0ZV1cJF*Ub`(sTi2iOu2ZUF;U!rq37nnM##ilp26Ea(RTm5 z>~6SRS^tG^uWjXBMFxL@q+;QO;teyE#x~cKQj_MOJ!(Aq7g|+rX(2CqWc9fChNdL* zOBswdQrykG{f>Wvh(rQ1LA}~~Oj4Ou{EE8!7jAQ)q8|-3c9MQ8ebX52SGZ^3a?af; zMX-LlCSj(aZoDM1*n^1nq@;R%Jd$ry*i?z(j=y|sAlk?}CkiHtJ_vK2<-=ex*FF?TVnvi0}5j$}@6vM&H+_=HHxv;q1PQDAK42 z`_u}<#3DndwiRj{T(w-4!;{}_9_{M-qtXKMrL*lw9;LdtX{syq_KgQ;e7c;lm&wbx zke@yyZZNjsHf7u87ZLWZoC#CM6A)XH3E~#a%lvEWeR?Yro&;`mraB=r|Pk8S|N37Rwi{=kd5wj z1&u~t1}l`~2JMuxU39%1dmZPL2`=r;8J9f*R3%WDeFtJurkDL?`_x)RGWK;}Z!<%#S&{WFr^ZWh(GNJ=RsxdCZ{ zBiVN-TW9UqFIs6cR?N9GN(e=C++j+Nup^QFFK;)^m}qa_Qqxvh;D$Z>RTWzbjF$tP z$xoPB;^)$j`3br1L?AmNF3ZDNfu8@;cf$cPeY6JGht&q=A9na_%zTF+Nv9gsc=}$D zw;h~GXslvd#nr{XxszmC8?3nuq0oNxYienm3o*U$kXF{n+*~F9P&5h+5y=t9QKK6E>Y@Hld2bG4WaWGb)0$ zxxxK<2xUf0TX%qD#B6)G%nA#N47oiKRXB4MlAiv#C*>L=U!%7mta80U5>eS*&&WU7 z-=1#U)}z3SNK!gI5wbO0^27U6me;h|U|-z4RT52>q|}!ENfG%}V?S(f!3KoRzY35D z_%W7v-@-z)6*a-r%?9mo&zt~Tl{q$y5P~Yy17c$eTORR`0iCF^lAV|ll&4f?5ucIN z9Wm^=%F3VB90j^7S3!x(^7NvZG2C&y{_xM!%q0r`%gu-8r|zC!ZrK=Re2q+sdT8A9 z{}^#!(?leA`gGX7Ss8$wA3Zc=YoLrJfVRj> zDR1A9xxn`K_9UbIt=t2p@u)@g8j`m-dq0Rb^&+q=Ege`8Au3oa17bHvWq6 z6Q5rmg<}7dPuIX=M0Qd0c8Bo-%ZV?VB*K#z!y(XnaZDI^))DWKq~#BhH*pnihHE1f^JA`^7Wvc10BWIPZ6 ztN-e^4Jg_ekH}djbAQZ(WmJgV*dhb!FpxsbtZT5|Y^N(wzbvFXldgV?5=>p`fM6=( z=wwGN$vtEqN@+!2G=9$ZRg3>Ln{JDvE1T`KsZ#QpTH-=S5IyGXHbdlbMTTQB+C4)s z%8*`s>t!p_aO&=<7C-J3pHU`}vm*o~K0zjc-2>X))M#mxP{=2VYh>l&Chw*57#uXI zOAo6ba6q63A8oKl!rC-Hl|NfPJG?6W6@nsDt6>$_AMtvncDb-b>`GS!FXFoZ4}L0` zP-G<+w3l=%ZO?AgH4^zGru~G(?$AwB*s!bM_*VOO2B69aWFtTgXosgjdBNy)iqG8F z9i$LlDf~KlU*F&L6n@d?>;vHeQ@8w+TCX~xe%VBydoTzJroPULpce+F?FN?DNJI!A zrSYoeZKh8-G6VI$@b&k8?*^*vtx!w$KC`LQ?y4od__LJ%@oShni!IR4hrjdeG_jrA zi)X*OLfksbgN$YP5OYB7CN`!9hfyC}E`X`|=M}o6kYr#y>Q;~;kbObppKJ*H20%CU zGcyy4B`FjiJlh>+X!uB}>PiU=A8b3(1T1m!%qiaHz|d#Ej5VJ9a@N2iDN(y4CJKJ5 zL5XJHnDFd?243@)di2+cw% z%RmUW!^S#r(UFXu+HdB3!w+s?ZG4~HOkAw3m%6&l*w{3kj^y2Nf&l5dz zCx$jBAuSsugKntNnc-%xN1I`*6Q8&v@=MXfTOASWK2pMq!OqJcE+f=oBw#U4VDSv| zB6L!}UY(PbQC%TiKhnVRlj2dU4^fV3y)&`aLM6I0uk`7{^Ty3}+88nV!rp?G<>9Ot zaQh>$0-w|?;I2(TcY^ioME@HPAa^JF>@lf3hVCSX6SG3Q3LFhv#!Iw*P~lEXzr8!?1?(DOWA)b7Fl87{iGJYart*yRbhnFqCd`6VTx z;jJ#V&2e6>yU+P2T|ni^*zB=~+^|x`YZoPc#5V6jbapFpolcpU`Su+kUF*~DyB?$b zCzjJEocJEeFm|cYSrnlk)T@QP{(3JAifIiykT?+q#BDnz9Q2k( z6cN`|1;w=IT^!qF!fu-5H}1FzEI`yyJ%R=p2t@p^FVb#?Vb2t1Ifdco-j884!?0YYd7 zuRc)hq=Y+{Nk&>2Ts%hJ@b7Ik1G^ucy&CXXPe7g~Rd_$UiG9O7YT_h@Sh!oF2YIN? zV`eewqEzGukIgwdm#N%qXR!@4&Yp=pae7RFOm+c@1r z?fO%@>ti*|XCfC~?77-rK`d5RzYq}$ZaNqqPIR}5cMlCi>NziGvt++o+MX3In$B1Dn(!Br-4PathcbY)c zkx(Wr%DdS%_x+!Eg55ta;6sh52>H%`mUwl^R+0H@%J`$2*h|ewYo9=IiIt`{7jAYG z&VA>*+9G(>+wqv;#l((QbgcwJC1X7hfAkQ9td^n0i`LRD+({EEjBfdHt;GQAq-!gV z2ru`pS6@|nJl$~v3s?NWhdA6LQ=#HB#K0w_unEZZ>Fe+MKQuR1th2X=6YMM6i)N9k zKVMQ2EaMay@o2r=eYSK;E~gU3dk;U)O`vck_B}(m{FmNjtB=xblvh>2?)z~E>bqgp z7U?g-DRny}?;A_U#oj03Z1d+P6rpwv{Virm@!6g)ZenSnYCBle2o8%j>`u3S@OVN3 zF-`89me1F+n#A@5$t4~9ATa?QR{6Fmgdh)gmm_t{C>FBvXZlW+=1UizG5mLh+eQ>fw`M=Yt%GGpg$3=?(mJm{bRtV zQ~S#!WVXDm;Cnvc7L`n z49}i<XDP%@!bW?IRfBy*Mk+g;7> zo<#r6k$&$cpSsz;*^{5`_ZtrVFSQa?ekTx=hyV{MH%Yx3oH-*@cAan>Nlo)J)%GhA zOSpfk5tYNO$;HBjL%*~Jzg1wibv06PuM?3=i1P=UFe`ZSXQo53dQ$M@GX#D-ymhEF zSNpqZXS)I(rZPFpWbxut-^sN#gvIpfka$(-NuWo=q*aJ%zvMu1`)P^Ki1@3&KZCR3>`!O-oq4MZ> zGp9Lfrs5WH{ZqGBOxw#v_&VH%5MbNLZEIz*ozouuIcS4@eMcHeCUFhZ&hVx3{IWvtScGKtp2Mf$Fp7PDFZ+K<4h}u#W_5`=EW$C=a0!>GcVI~szB(} zhP{IRpuk^q*j=LK@D*+434M)_V!$MbDNM~LAth=#Z(zd;#v0e1!)J}GU0(NZRnz!F zF}XWyuo;Pz)n=Yj{g|3)C;s1t@06;taD-|kvJEeljMF#G?MFZW~07pKaF?}w-+pR{&S5=Pgm z``(Fu627^4qhx~~!*%<)7B<}?>>k#qL^7K4s^!Y>~{JSg0e0(0@yF88JFBYOu+e{Q@Zl%K(GpU|7O+|M1x~G`$Y?#Y%TJJ_&^jdhiS7 z|I6y0Bw}p+iQDI`tHvdAGbya%iL-o&Sq^0(L>qK2y+R>0&KR3+!EjDcX221XMcE>32`h$%9@+-8M+p@0O;Wcx`KLFm~|I|8d_vQ&tE zo~-LDId=(fd8$48v4;cfzQZ}Y);>M2XN`r^=!hePj7q*FGSJLEY(xgVS+>Mmy0^8O zU;~ctS?#gTZVIAszHRsW6;O`cCH=lbOI(ko5Ha#S4b!tDk*wG1rt>ment>q<+l+jF z8R35MWp0-u-Md@Z$L+ivkFOxQ^a>2=suFzW8MkP@&8BJLL?QrY4l%<9gieLmm_rD8 z{G7~~?W&J?yZaD=!*PuebD>|E-fg-2c){;{fYBI<{&}j<4#U1sGxFDDfc< z>6!ao20wLt<&t-p5!P*vyS94uwu`b}ht^j?gfCsQ3AWxk^c3sg44Ah;hTCDx`AARV z+E-pglgKr^+h<M!_Kk&ntA}47(5YlVvIu3h!$Zw8PE@{1Aj&@s$LX!0 z6prOvkg<8av}u&>i@-)+j89C)s6&nyp=-2RCSS#bDe`PJ#9OY0czT7vvB4z@8Udn; zRGo{z%0J4{XmPffVOv-}Z64_kwO^M_vWce0ta}IF!NP}v;*g%<<<8)8Mdr}dpMCMT z97v($1-gehNQph${X9L);vZmoth=kg?hWfIlLzrg3lGyi1G}VR z6Gfe7ePDPc5%$I^5{Q^%iEmWTke;0B#enbp3nNCSG^$?qe3DtI2{qooN$}T_cf0QK zA#$>7e&hSRs)1EM+uthZMGbx5^BLv8CcL4HZ^G(lW+Df-dFllaap^V0(rL0)gfcIp zYEBtH#b&ver6z$0InJdQH^#LfurHJzJ?3bT+lNhG_QYEb3Zv54&+Lv~?^e4lQR6MS zUm&?A4@B{n%6zCaVZeG@7Ykij<|J&K#8cSQ>yyw$! zu>NJ51#r#0gJaX%o+2w=*|#lAp!`coA0e<%D+bKKBfzR;O+=#nf4tHiuKwbqnLP{` z^KZx8S2k;XWK38wBNPxq>&Fdc!(#1Yj4AAS&2M3y>sJu7uP*E2r&`CubG$#(LV&T` zgx7PK{;&t8hZ9~!kJ_a{k5?ckiZNoxnI*Ab?MW_6GTD1UF)1uFzR9-lf8N{iYUW{o z9*RGjZXJIMn93GkFC(l%_z+TW&{N1~Jg$UVb9Bg?uPs(WA9xWId*No-GsX56@15#V zmA&E(Mohxcyk1$Z8&&|ZJwgTNIVwz4Tfc3P_3%3X}m8pVEn34usFTvCW;B-93NnmIKboady48ZLSISQYBvL>AcfEN ze@u|Ak>cnV7@|sVX9}>v!iSsPsPMspxyUq_wL)*b;6xodJ$Cu7jn#43gVL4un^?(- zazlK!nA27KwQF)eb?}z;J^LvTc#|JWP+c)gUaZ8J5L%0<-#?0ks=&Bstf! zM?n`z*~)hSsMj%AxIXsi&K1Bda#+99S|+z>J>%|ksgfBJDXO5yEt!pb%#TPMp>3LO zcSHQ%Zr0cj)qP}xwT$7Scy!InE32X6bdyDVb5fEXW8Y$peR>(?FIRzdmGi_$1t7V;Mpftfz5_4CM-ZC=p=_9@$jyaYFnp4yNRZ# z+|t?f`;jHG)&7{nWEM>B-h05uVH>1}JvmdD4X#F}d8t+vG8eBfiD&PqG;uvcld5Jj z>0r}gfH4qiElh;Qk~a59A$r2p+kQx4k)De~wrbxhK2yW_zUoC4nv9+h`b{SE3J}8& z-y%atB!5E)N=*%}-hulAPSy5o|H1;0xr?$MLf{gBUld+gx5k(pZI=Jj23WfSn+z#q zr*{NWfTkS*5+rB`JIm2}3;V&dJ+zHp-1?9H^@jcOfnZw*VeE|v;%$Za3spA~e4ja% z9hVI#rWL2SBM})M^C!oMW~G`%sx$VQpE#VJ%wUpP_zdRWEaT9spQwN^lC1?5eE z1x(IG=^%YIGl~x*+z7+Af$;2}>X#8ue2 zd9884W4zTuc)8cM!V|%tZyMVRglDhy_BHoXa9rv&#Tq@H@W=mFU%i{QdkbsOr8m|4 zxkTw|{^N^EjBspcprK~N!=c-E3;^9EUxAnx6NH6GchG?vY)!os0Y!hYz+ zTX<&h>L=YQR&nFIuR?y@S772mk!@GQ z3}RM?T%!0c;IADwb0#3UWPLonGG<1b!R{75Pr&6AcsC8oMrx=r2^+wPwHs(l;Z?KF9B< z_uqyP;D&S`t5CT7)?Hf>F!kPZ*9k#5nb-lc&jYy@81j&s@L9N77kgr_zske?)xNqm zusvvm4{?dn2)m#l1jk=%_0>c9``a*xKT6NFJ6g^1wrvY3LgsQ75oy8hjkV$GU}{?E zSP-5H=C4UjSgsWSv|o$=ZzO7$x;g6xHW`GUQaRogo;}@G_X6Xm>YML#0{z&HGm@HBJhl8<4gB1o?yq?7q1MAv|FkHdzeJT8~FQ$uH{uwM=RRSjp{w ziwgp-rDULDMTj7Rxs2T1n)+jgkpxUl&}c;Ry|y0XZ{i?oqQ)$reUP`kc5 zVJe#+4o#UcIu#`-7*5Mv9`FUmBP9ZWfys5uJLv5j8GyPwX}0zq8OC89-r5y8E?XTI>K=##8|gbSA$8DMpVf=_ z?Df+YjO%$hKHlaeqvwd1nhtvCeUSmcyGFPRS z|0T0Gvv{1RY1Sl7jr^GQrB13CQGa2?=sbekxEHy!!PvH1^G- zcBH~{K|iIk3wSCG7_Px42D&PLKlq!Z3^{%Zm|$E#AO70*gk;l#%9s?JL3Zt5LLAeev+1+N~By$N!p zb2u}T0{F=imw=ox_}n>~CTdV)TxWL*^d3u~HG9x`J1o zbWC-QdgKjB@JF%JZL71|JA&T4bx3PJt^i4CKt~DSO+urpg{nUb{u$cYeS)+OL*JD0 zkVWBKS@BI-FOjfTqyi=RO!$wX%_FnNnfa5KxTX%*KDW|Y%_znJ%>dvar1N&EB91Aa zf>Sd&{ZGKIvwcU&R_z{W^MG)WBo%(@j1AwE@)y;SB@)~B0@r*K8(S1a1?A+OS0Seb zuNwLBaM~_!9CBnpc3o&uWI*`i0V@Q0LIVUffG`LUSoyztd{~mXw@9H0#HRj_B{ok& zNB7VD!1Uq=TwrQ15h-)}4U53@tVE-%vFEyUl%39-$k%er8AVBv%b!`p9?&QSv;p>BZ9CDBgZET?K!%F%O|g4 zwn_FQk@(Fw;)pXx1iFTs5?+MRobqSz@E8c|1-}D=l3~J*_?0XIc(UwE>Av%?{x)4K zHxT&)U2eWP*-i0_|(-E=QL~_;ny+8S$Aiy<0TpQgw2%$!KW?qlwJY z*<}b6{CyrZA(H;79@kx@Oc#mN0DZfHpyye9zDyQIX}(1QxSjzKiUPHC$k1ah13^cS z)B~Eog9+)N<|M`Ik$|a5{(G^L;!RcvoDrxG1g74&fJf24`J3=cyyVX*qaSyFlcApZ71 zG{4MWIFVNceX%}FFB|<6&3d8(!|9iV-ivyilQfmVmXc20ZAatIz}7rvDe=ppELcK! zf;!Ae6oK>9o9uR51Qzf89r`K&#gm>0TA0& zmC5}-0G+7a6D$qM%H2fo*tUKTY=Wj&nvGnZPB-%{&iGG66&svH^<;n`s1EjMg{)n! zDcprUQ+m2?QL5ARIp3%pH%a>2N0Ek<}eG@6sTIp6Mj81iw zUst={gB)p)0NyQl8o*^;K*p|_d5jdP)_PhK%~ z3|k#UHkaFMarlq@6ib!$)iqNQee~dm2nd6mHgkHfm#){V)A|%>(}S*M{1pTleqDJT1 zTqTaOLK&c%JlLxdxae6~3mH4qU}zvib|fOTFnf?d>_%9WiKfbEaa>h;hI72WYFnyr zVeVM!?dDTr@NrN&y1~P#WuJJ=o+-ajci`1u# zb&|!W$AyyHD^7+FIo!nN}sX4Ie+(6cj5M{8-zL+ zv>va0==ZPE1n4#azEA?r$TOA*kMHzzmqJI zxA=+P`Q}zG^%@g;WXaiSIiqh|fbqW&x6J6IQx~~06FM)gRkx7H-W`8N26X3paXHES zAguLHko#s@G`8euHXq{MC%u$M@AOR3?9;D}y$6su7?G59FgUB}$K#JGZXna&4^f)AIX`4Kt8h_D z!d_@5vPNJ0 z#+c0NW#r^UdnISOra%jlM9PGh zHKmAh_42<~EEH?wi?+dzV{2E0_{N7mcvMN?MP~+Y2qWMTPAEq;7k0NP4}WEa&6Thd z8ql{v1eRU7v{_Mqr^*x281*(9)}T2?m5#p1)LD;tWhMf>j>4K_ayLV%Qi=5hW}(?sNah)-{rCkG)T zJ@K-+tQK+hs`q3I6n|5;}LpBD^RHvr#yY|iw0^v%?b^xtEWNZmE22>&BX`-dz} zH7uG1dY;kfa8JK-ljz%SWm5|GMDOCCtFwpW<*&(t)h4A7e?r zS{)BTnKESpW1=?)DGiBC(nnXM-8ufCV1fGdv-vU+sY?WkELdjYJylR!rvSoI(YqMB z{yfhq8myg9k7SZq@5gldS@E&y<7@Os)0);J8q`@7P-Ix`_N1S6o!A!p2T&A@#{CRR zf+x8eQLd0eWr__8c(8yH)SVia_Xgmn7n0<5Eak_uA4b1C8H#ZhLZn=xNh>{#+|cOx zUZeo^{2(g>T%aFxa873TGcWp)nc{s`gQhAQm}ia_f&+XNxy+w;LqFu<{L89)Mc-9+ zVimm}xfS1hof_gj=I-UT30WNoaUaIqcO-qa?5ef5|$h(x_o7l0mduqbVV*b46yp=4~)pwQx zUbvO@pd1@+H4JNF-<)9X(*ngx>lU5gTOFBY?z6=y+TW#4{{|Jept1-Ix#ige?^7%G z4Dif$)-?7GY|`*jWxJj9NI#!yhueO{N!$%T<#j(zyCQo(4OX<^E3xiT-6VUgXaa98 ztE>qr{TThQaZc=>PD`362NW61 zY?;n8h^N%GHkZgawnf)VV5X3Pr87Too&%XRQ_s{P@V92CPpgqx^%=Pma2J&#jsDjX zu<4?s!1((YTB1Zw!|5@J&}ej>yV&i0jqr0M^IDJPXG2{qI!)2A!tHU!CzqTM@KXS@ z+RIo>Oa%82VSMD!{{Wf20%7Uv*TrkAEeE7mlSmJyt|#X?5D zC9!-QE)m%Lh$&(8cT>yP8%IEzYVUEl;GeKKS9uQ>{ko^#x7#{EVIZP04Hb$;ui`QWt09S?alrE#_j6-K)T;ji4LCTPvwz|Hn@sa{x!w7!?xJN-@ z#Ix>F^Yh8Ie!oNnz`gqwZvqB61*|v#xbk1rF;9Ch*}v;}mSjLp&8xd`HaqKAYvv_L zF*$g@CRZWoVIF|puFO67V~E21zzmgbjnXvuhocp+=FoT*a# zuUkD(u)p&feEC%!0)g?beyfuZh4p@8E79uJ_i*1%)DmnKGVW3 z;e&Ft7vg}tq}gJ8d^W3~vEuc(bY@crPesd@eBs-ZVIr_%g-_#x= zwUT4UNktC|wlYF*u6Yg|XAwVbUwV5b2wXlr_agoYkEGhKTRhfob40HvfwmbB>bUZN z?bR!bfi89@STOmF1Pf}H7Xtk?FhDHd7Jm@pTM=@tghjmEEcx0F@4d$#vktF|tH_z* zNMT+-T-29qpo2YEXq(yrET@mt3Q_ORnArNqV4};59KW)<5Cp|v;&>ucBuV6}|Hn6; zBCFVstxaTcU+uKh7Bxiv@?`k4FhTwX{+CwRkE*^KjP@eZA1|E{`Hs?_m54DmrXhu6 zWtiE1(VP)ELto`RJXrR5mUES|;3}t|)gI^vDKVTj5$URYvm`I7l$*9mA_TPkgs9$Q zf*n9Qs)Fr(nraRn>PXRFTa&p8#jVzjt`lh%^a3ZkV9vV0a zXGHb3K9tG%{*Ye$Fs{BtK9P;0;DtiWQ7=h=xwR(Pq-L!-4Y z2%PhrpgyqyAP9lS0}le__tV~lOr=ZSKYx+~PD5zznWrj8V>1m0QakA7g?7IJz@tq#-D?V)V@X`NlP0^R-uPbkmIQr;E&#E1zJ6x=EFcM8lYfp@I!Fzce@|u% zcrb>5wM!4n*@D0ux(tIz>45deFd|31vDfr#mGMP=*YL;6imBEBsjhJbrE&+rv!z^{ z88iQwJGVLivDby5vQEd;iE*{k22pway}CklYsWeL)AW^Z&%Ha*OD- zaz`~^6MO;iMmYnILr&Hbl!)#V!`o?|TsNEHX$k2=p3Z=`=GvLZDNG5}8-ue|v| zxP-rJV@Ckvl4e%v8jH1N1oqyjpt$4x^E44Yy)m&CyGB}oK^Bs2^y`b(Uo=%pWyd@P zc;Ma~(Q!{q%e*+&L$K-{?YV4bIm`F9#TL=mxPkTisXgMC(;!MKh z2A=PJRFX;neb8@;Qf2*OL{fW$bq5RTj3nCL_}qzhxql(b@h^LOH2b_3x%oN@E!mj# zkJv9Rol+_M<94=b zqgMNwH)r!I#i20qtz!6z`WNlYl9vN;Y-Jqspy~=XJilIq*qCwXo-`QvD)J6QGVI533~;khiGgPK0Ze z|0v(=cYbNutDR)jHDCc>ceE; z$AEoLHd=1!INfrT2}gf2f8QzVa7`M5%o^@W);m>;Ug``=i7jxra(NpIB9Yq-pSb98 zO1Lh1^X`A3x+H@>QUaXPc-c~Gnycy8BZ)Ep#zDo90~!%vW!pe4^Uw0qKXlV! ze07bG?BbORos3_Jt7^9*2mDlFa?};rj5NnK5DC*)VsUwK);B?UE<7A9NeMY;LhwFu zbmrr*Ux^Be?7^iuVa2A`pjtWG_jL1N(*$nid#gAZOZf-hV3*U6r!>) zL_>0u{EWz}hIh*`Gu-JDbn5w!% zb({NbBHGO#kolj)W0)B8k)e8ru0>+a@Bd^r%Tmzu0V;Zo^>-eq zk}-3UiS|A3tgBSmp#kE|AMN8T>)RSH7_)R#+oI}mqAMG_?Hdw_3m{DG9{^~+&a21w z6pY%ve!es-k+8ym*u*4DUdu$_h|rJ+1fG+u^Y-W_aJ&}sH_GraO9G$lG~RFy_Yo)a zU(k4WkR-6WL#KOD)02Ch1>gqCeBcPlt4U^Yt-C`^|FZVdkuWFBOnh~76ioDX zZV)|SB?1;8&_204g8Ib{7Id+ex4H|Cfs--k#>b|!T)n_*4Re#HnLlN5sPCmYoxiiA zF7wy=`k%Pn*sBo*fgrW#zYFj`QT+d7vZT}8+~W*OpTndh%xF#bEm zlyJunQ~h?S5k2Q-g43C;m>^a0)NPs?Grja(kqiG}``I>-TKTK2>ZNO1WdWG5|H|YF zk5`k_AzG#tV$?z#?!BmwfBjca#w`7`dw0N=mX+(yjT$|Nm`nWIf_}#)srEEik$>0a z?Af&J)4vvceo_9mlkKta00o}C-hj4ACK6PAZZO}(riPlCD)0lF5uLiH5-E`zLxMMM zekTsgFktEh9W7zSKup4eJ4SK3+rkA}X1RS}Sot^=5nE#!#CpTT1Y^|AIB)Iea%DKENCaU1wwEh6@r02lF zNFOJ2tmxhF=O&@4rD|Ti*B{ZGr)goEo~BVgy%LS}J_6p8Ie!mW>p~@uCL!ruet{C@ zsDJinFL&qHbuN~c+!quZ`E$__`LN-eS+foIb<%U=@CpKm=@EYWT>}C&bpPTnKlCys zt%P`k1NU_@RvsUx1)^8l^?#QA3O?;OH@nNrjm$@Kf|u|NCeEc0PHyVye{0Yc*R}n7SVuACSS^ zLW+++8w;YF==*>;UXqx$9ku=NRMU87x!T`(rV`MHi9NB4euGxqd%jo2a~#Kuladj- zWd0m*@|}C}WXwa?oP~-VGm;!X?sWE8yB=FkmzW7&k6qT#@TD&Y{8#C*z-2*Z0bE`o z9eo2UX~&Py8{ez46e^)08`QvjC>%rx0hcdrNSGn+ir$3TtHUj+laDvCQ$U<=5Qy{r z4}|Ik5!PLOCqw#tY@JlJDE~N#cvbgRHGJ}dB$$+}JR z%6_mP5X8HBZ&&6ozDPVLAPr!d-^&B7R5N=YSI>5r0K5GAgNYuWY*e2d1; z^Ft~6*`63McJc77s+4HF3sZax#M4Rwi%2Hj#`_>}dV9_KE+f9bk-Y0U94klDRGgg% z9;>SZ4Qu?+80mUOxebcA2dY3MZ&v1_?@>9faJxYIL!ETB2I`A!3@bpa*h_asldW7E zi01LylTRyC*>r#0W_)N1NHbDU@6DT8fW{2uv)x#x{dH z<;~^rCk)48p6oV|e61vQS@q0R5B9sef1h?_%Q`#%n5^@SNq1S6D&0w4edx)V(ESdU z+4<914W1=&yO)~-H|VPvGH-2cQacZfV27*!FZ|8(*}2q!Ic1HR$g-KDkD{YArvj~! z%af{4NAkV7t%B@m=@J|F_a`R7xE|(w?q< JF6*2UngG8Q%B}za literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..80d0414f77ffed7bf95f24e4c265e6819bae5385 GIT binary patch literal 20467 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 UA>X4{d8yj=2ORLf=^I=8Gk|}pcK`qY literal 0 HcmV?d00001 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..0fb169fac --- /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 From 9c07d064c0578c8adef24ca20a2f10dab81f339e Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 4 Oct 2022 01:37:46 +0300 Subject: [PATCH 072/339] EVM runtime: support GASPRICE, TIMESTAMP, CHAINID, GASLIMIT, GAS opcodes (#713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raúl Kripalani --- Cargo.lock | 70 +++--- Cargo.toml | 32 +-- actors/evm/src/interpreter/address.rs | 15 +- actors/evm/src/interpreter/execution.rs | 10 +- .../src/interpreter/instructions/context.rs | 65 +++-- .../src/interpreter/instructions/control.rs | 5 - .../evm/src/interpreter/instructions/state.rs | 16 +- actors/evm/tests/basic.rs | 61 +---- actors/evm/tests/calc.rs | 34 +-- actors/evm/tests/call.rs | 54 +---- actors/evm/tests/ext_opcodes.rs | 77 ++---- actors/evm/tests/misc.rs | 225 ++++++++++++++++++ actors/evm/tests/precompile.rs | 25 +- actors/evm/tests/selfdestruct.rs | 29 +-- actors/evm/tests/util.rs | 38 +++ runtime/src/runtime/chainid.rs | 37 +++ runtime/src/runtime/fvm.rs | 24 ++ runtime/src/runtime/mod.rs | 20 ++ runtime/src/test_utils.rs | 56 +++++ test_vm/src/lib.rs | 22 ++ 20 files changed, 603 insertions(+), 312 deletions(-) create mode 100644 actors/evm/tests/misc.rs create mode 100644 actors/evm/tests/util.rs create mode 100644 runtime/src/runtime/chainid.rs diff --git a/Cargo.lock b/Cargo.lock index 09a90e811..9395b4802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -694,12 +694,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -1847,8 +1846,7 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09e5aa7de45452676d18fcb70b750acd65faae7a4fe18fe784b4c85f869fb" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "ahash", "anyhow", @@ -1864,8 +1862,7 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dfb38431e301bf063a412f870a3d9ebb541d581d1884f95e921f5ea823d29d" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1877,8 +1874,7 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "anyhow", "cid", @@ -1903,8 +1899,7 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9cfcb4ae444801009182fded790dd56aff9c9a567e54510e2ccf14fb9daf8e" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "anyhow", "cid", @@ -1921,8 +1916,7 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b5c939897aa1bfd63e7cb9c458ba10689371af3278ff20d66c6f8ca152c6c0" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "anyhow", "byteorder", @@ -1942,8 +1936,7 @@ dependencies = [ [[package]] name = "fvm_sdk" version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f90eee234e07d5879e174167700a14bfcf2b87965fdacf94e98871a37126db" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1957,8 +1950,7 @@ dependencies = [ [[package]] name = "fvm_shared" version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bd2ab8e01c771bb1e8ab1e6a68980b1592d48d8cc7b5fb1449f9f1c5f19ba" +source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" dependencies = [ "anyhow", "blake2b_simd", @@ -2289,9 +2281,9 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -2350,9 +2342,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.133" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libipld-core" @@ -2613,9 +2605,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -2913,9 +2905,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -3064,9 +3056,9 @@ dependencies = [ [[package]] name = "ripemd" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e2ee464e763f6527991a6d532142e3c2016eb9907cc081401c11862c26a840" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ "digest 0.10.5", ] @@ -3229,9 +3221,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] @@ -3266,9 +3258,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -3527,9 +3519,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ "proc-macro2", "quote", @@ -3612,18 +3604,18 @@ checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -3774,9 +3766,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "uint" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" dependencies = [ "byteorder", "crunchy", diff --git a/Cargo.toml b/Cargo.toml index 7ffa36fba..9b476d5d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,14 +55,14 @@ members = [ "test_vm", ] -#[patch.crates-io] -#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } +[patch.crates-io] +fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } +fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } ## 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. @@ -79,14 +79,14 @@ members = [ ## 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_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"} +#[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_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"} [profile.wasm] inherits = "release" diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index 22ff74d18..3a10081e0 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -1,6 +1,7 @@ use crate::StatusCode; use crate::U256; use fvm_shared::address::Address as FilecoinAddress; +use fvm_shared::ActorID; /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). /// @@ -44,8 +45,8 @@ impl EthAddress { EthAddress(bytes) } - /// Interpret the hash as an ID address in EVM-form, and return a Filecoin ID address if that's - /// the case. + /// 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. @@ -59,6 +60,11 @@ impl EthAddress { Some(FilecoinAddress::new_id(u64::from_be_bytes(self.0[12..].try_into().unwrap()))) } + /// Same as as_id_address, but go the extra mile and return the ActorID. + pub fn as_id(&self) -> Option { + self.as_id_address().and_then(|id| id.id().ok()) + } + /// Returns this Address as an EVM word. pub fn as_evm_word(&self) -> U256 { U256::from_big_endian(&self.0) @@ -94,6 +100,11 @@ mod tests { $expectation ); + assert_eq!( + evm_addr.as_id(), + $expectation.map(|addr: FilecoinAddress| addr.id().unwrap()) + ); + // test inverse conversion, if a valid ID address was supplied if let Some(fil_addr) = $expectation { assert_eq!(EthAddress::from_id_address(&fil_addr), Some(evm_addr)); diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 26f099f54..22d2ec474 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -319,7 +319,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } GASPRICE(m) { - context::gas_price(m.runtime, m.system)?; + context::gas_price(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -359,7 +359,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } TIMESTAMP(m) { - context::timestamp(m.runtime, m.system)?; + context::timestamp(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -374,12 +374,12 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } GASLIMIT(m) { - context::gas_limit(m.runtime, m.system)?; + context::gas_limit(m.runtime, m.system); Ok(ControlFlow::Continue) } CHAINID(m) { - context::chain_id(m.runtime, m.system)?; + context::chain_id(m.runtime, m.system); Ok(ControlFlow::Continue) } @@ -448,7 +448,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } GAS(m) { - control::gas(m.runtime); + context::gas(m.runtime, m.system); Ok(ControlFlow::Continue) } diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index a08506bd8..17d820979 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,16 +1,31 @@ -use crate::interpreter::address::EthAddress; use { + crate::interpreter::address::EthAddress, crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, }; #[inline] pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!("requires the client passing down the inclusion tipset hash") + let bn = state.stack.pop(); + if bn.bits() > 8 { + return Err(StatusCode::ArgumentOutOfRange(format!("invalid epoch lookback: {}", bn))); + } + let epoch = bn.as_u64() as i64; + if let Some(cid) = platform.rt.tipset_cid(epoch) { + let mut hash = cid.hash().digest(); + if hash.len() > 32 { + hash = &hash[..32] + } + state.stack.push(U256::from_big_endian(hash)); + Ok(()) + } else { + Err(StatusCode::InvalidArgument(format!("no tipset for epoch lookback at: {}", epoch))) + } } #[inline] @@ -59,18 +74,27 @@ pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn gas_price<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!("should return priority fee (needs syscall) + basefee") + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + let effective_price = platform.rt.base_fee() + platform.rt.message().gas_premium(); + state.stack.push(U256::from(&effective_price)); +} + +#[inline] +pub fn gas<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.gas_available())); } #[inline] pub fn timestamp<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!("should return the timestamp from the block header (requires syscall and FFI change)") + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, +) { + state.stack.push(U256::from(platform.rt.tipset_timestamp())); } #[inline] @@ -78,7 +102,7 @@ pub fn block_number<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.curr_epoch())) + state.stack.push(U256::from(platform.rt.curr_epoch())); } #[inline] @@ -86,23 +110,24 @@ pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::zero()) + state.stack.push(U256::zero()); } #[inline] pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, + state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!("requires a syscall") +) { + const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; + state.stack.push(U256::from(BLOCK_GAS_LIMIT)); } #[inline] pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, + state: &mut ExecutionState, _platform: &'r System<'r, BS, RT>, -) -> Result<(), StatusCode> { - todo!("requires chain ID registration and configuration in the client") +) { + state.stack.push(U256::from(chainid::CHAINID)); } #[inline] diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 176718c8b..f2233ef85 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -50,11 +50,6 @@ pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { Ok(()) } -#[inline] -pub fn gas(_state: &mut ExecutionState) { - todo!() -} - #[inline] pub fn pc(stack: &mut Stack, pc: usize) { stack.push(U256::from(pc)) diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 6fe8c4c4c..b8d2100a5 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -1,5 +1,6 @@ use crate::U256; use { + crate::interpreter::address::EthAddress, crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, @@ -7,10 +8,19 @@ use { #[inline] pub fn balance<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + state: &mut ExecutionState, + platform: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - todo!("requires syscall") + let actor = state.stack.pop(); + + let balance = if let Some(id) = EthAddress::try_from(actor).ok().and_then(|addr| addr.as_id()) { + U256::from(&platform.rt.actor_balance(id)) + } else { + U256::zero() + }; + + state.stack.push(balance); + Ok(()) } #[inline] diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 9a5dd3929..eea26366b 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -9,29 +9,17 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; +mod util; + #[test] fn basic_contract_construction_and_invocation() { - let mut rt = MockRuntime::default(); - + let bytecode = hex::decode(include_str!("contracts/simplecoin.hex")).unwrap(); let contract = Address::new_id(100); - let params = evm::ConstructorParams { - bytecode: hex::decode(include_str!("contracts/simplecoin.hex")).unwrap().into(), - }; - - // invoke constructor - rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); - rt.expect_validate_caller_any(); - rt.set_origin(contract); - - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + 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 @@ -45,10 +33,7 @@ fn basic_contract_construction_and_invocation() { let input_data = RawBytes::from(solidity_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); - + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0)); // invoke contract -- getBalance @@ -63,9 +48,7 @@ fn basic_contract_construction_and_invocation() { let input_data = RawBytes::from(solidity_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(10000)); } @@ -95,20 +78,9 @@ return (asm::new_contract("get_bytecode", init, body).unwrap(), body_bytecode) }; - let mut rt = MockRuntime::default(); + let mut rt = util::construct_and_verify(init_code); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode: init_code.into() }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); rt.reset(); - rt.expect_validate_caller_any(); let returned_bytecode_cid: Cid = rt .call::(evm::Method::GetBytecode as u64, &Default::default()) @@ -135,20 +107,9 @@ sstore"; asm::new_contract("get_storage_at", init, body).unwrap() }; - let mut rt = MockRuntime::default(); + let mut rt = util::construct_and_verify(init_code); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode: init_code.into() }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); 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 diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index f722812b3..8741e9503 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -2,9 +2,10 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; +mod util; + #[allow(dead_code)] pub fn magic_calc_contract() -> Vec { let init = r#" @@ -70,30 +71,13 @@ return fn test_magic_calc() { let contract = magic_calc_contract(); - let mut rt = MockRuntime::default(); - - // invoke constructor - rt.expect_validate_caller_any(); - - let params = evm::ConstructorParams { bytecode: contract.into() }; - - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + let mut rt = util::construct_and_verify(contract); // invoke contract -- get_magic let contract_params = vec![0u8; 32]; let input_data = RawBytes::from(contract_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); - + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); // invoke contract -- add_magic @@ -102,10 +86,7 @@ fn test_magic_calc() { contract_params[35] = 0x01; let input_data = RawBytes::from(contract_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); - + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0x43)); // invoke contract -- mul_magic @@ -114,9 +95,6 @@ fn test_magic_calc() { contract_params[35] = 0x02; let input_data = RawBytes::from(contract_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); - + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0x84)); } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index c07e77959..49c707e1c 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -10,6 +10,8 @@ use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +mod util; + #[allow(dead_code)] pub fn call_proxy_contract() -> Vec { let init = ""; @@ -58,20 +60,11 @@ return #[test] fn test_call() { - let mut rt = MockRuntime::default(); + let contract = call_proxy_contract(); // construct the proxy - let contract = call_proxy_contract(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode: contract.into() }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + let mut rt = util::construct_and_verify(contract); + MockRuntime::default(); // create a mock target and proxy a call through the proxy let target = FILAddress::new_id(0x100); @@ -92,7 +85,6 @@ fn test_call() { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; - rt.expect_validate_caller_any(); rt.expect_send( target, evm::Method::InvokeContract as u64, @@ -101,9 +93,8 @@ fn test_call() { RawBytes::from(return_data), ExitCode::OK, ); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } @@ -126,25 +117,14 @@ return #[test] fn test_methodnum() { - let mut rt = MockRuntime::default(); - let contract = methodnum_contract(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode: contract.into() }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + + let mut rt = util::construct_and_verify(contract); // invoke the contract rt.expect_validate_caller_any(); let result = rt.call::(0x42, &RawBytes::default()).unwrap(); - assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } @@ -197,20 +177,10 @@ return #[test] fn test_callactor() { - let mut rt = MockRuntime::default(); + let contract = callactor_proxy_contract(); // construct the proxy - let contract = callactor_proxy_contract(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode: contract.into() }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + let mut rt = util::construct_and_verify(contract); // create a mock target and proxy a call through the proxy let target = FILAddress::new_id(0x100); @@ -231,7 +201,6 @@ fn test_callactor() { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; - rt.expect_validate_caller_any(); rt.expect_send( target, 0x42, @@ -240,8 +209,7 @@ fn test_callactor() { RawBytes::from(return_data), ExitCode::OK, ); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); + let result = util::invoke_contract(&mut rt, input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index ffa1b14a4..2fed20254 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -11,9 +11,11 @@ use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +mod util; + #[test] fn test_extcodesize() { - let bytecode: RawBytes = { + let bytecode = { let init = ""; let body = r#" # get code size of address f088 @@ -29,20 +31,9 @@ fn test_extcodesize() { "#; asm::new_contract("extcodesize", init, body).unwrap() - } - .into(); - - let mut rt = MockRuntime::default(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + }; + + let mut rt = util::construct_and_verify(bytecode); // 0x88 is an EVM actor let target = FILAddress::new_id(0x88); @@ -53,7 +44,6 @@ fn test_extcodesize() { let other_bytecode = vec![0x01, 0x02, 0x03, 0x04]; rt.store.put_keyed(&bytecode_cid, other_bytecode.as_slice()).unwrap(); - rt.expect_validate_caller_any(); rt.expect_send( target, evm::Method::GetBytecode as u64, @@ -63,16 +53,13 @@ fn test_extcodesize() { ExitCode::OK, ); - let result = rt - .call::(evm::Method::InvokeContract as u64, &Default::default()) - .unwrap(); - + let result = util::invoke_contract(&mut rt, RawBytes::default()); assert_eq!(U256::from_big_endian(&result), U256::from(0x04)); } #[test] fn test_extcodehash() { - let bytecode: RawBytes = { + let bytecode = { let init = ""; let body = r#" # get code hash of address f088 @@ -88,20 +75,9 @@ fn test_extcodehash() { "#; asm::new_contract("extcodehash", init, body).unwrap() - } - .into(); - - let mut rt = MockRuntime::default(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + }; + + let mut rt = util::construct_and_verify(bytecode); // 0x88 is an EVM actor let target = FILAddress::new_id(0x88); @@ -111,7 +87,6 @@ fn test_extcodehash() { let bytecode_cid = Cid::try_from("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); - rt.expect_validate_caller_any(); rt.expect_send( target, evm::Method::GetBytecode as u64, @@ -121,16 +96,13 @@ fn test_extcodehash() { ExitCode::OK, ); - let result = rt - .call::(evm::Method::InvokeContract as u64, &Default::default()) - .unwrap(); - + let result = util::invoke_contract(&mut rt, RawBytes::default()); assert_eq!(U256::from_big_endian(&result), U256::from(&bytecode_cid.hash().digest()[..32])); } #[test] fn test_extcodecopy() { - let bytecode: RawBytes = { + let bytecode = { let init = ""; let body = r#" push1 0xff @@ -145,20 +117,9 @@ fn test_extcodecopy() { "#; asm::new_contract("extcodecopy", init, body).unwrap() - } - .into(); - - let mut rt = MockRuntime::default(); - rt.expect_validate_caller_any(); - let params = evm::ConstructorParams { bytecode }; - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + }; + + let mut rt = util::construct_and_verify(bytecode); // 0x88 is an EVM actor let target = FILAddress::new_id(0x88); @@ -169,7 +130,6 @@ fn test_extcodecopy() { let other_bytecode = vec![0x01, 0x02, 0x03, 0x04]; rt.store.put_keyed(&bytecode_cid, other_bytecode.as_slice()).unwrap(); - rt.expect_validate_caller_any(); rt.expect_send( target, evm::Method::GetBytecode as u64, @@ -179,9 +139,6 @@ fn test_extcodecopy() { ExitCode::OK, ); - let result = rt - .call::(evm::Method::InvokeContract as u64, &Default::default()) - .unwrap(); - + let result = util::invoke_contract(&mut rt, RawBytes::default()); assert_eq!(other_bytecode.as_slice(), result.bytes()); } diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs new file mode 100644 index 000000000..2d5721b72 --- /dev/null +++ b/actors/evm/tests/misc.rs @@ -0,0 +1,225 @@ +mod asm; +mod util; + +use cid::Cid; +use evm::interpreter::U256; +use fil_actor_evm as evm; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::econ::TokenAmount; + +#[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, RawBytes::default()); + assert_eq!(U256::from_big_endian(&result), U256::from(123)); +} + +#[test] +fn test_blockhash() { + let contract = asm::new_contract( + "blockhash", + "", + r#" +push1 0x00 +blockhash +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + + let test_cid = + Cid::try_from("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); + rt.tipset_cids = vec![test_cid]; + let result = util::invoke_contract(&mut rt, RawBytes::default()); + assert_eq!(result.to_vec(), test_cid.hash().digest()); +} + +#[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); + let result = util::invoke_contract(&mut rt, RawBytes::default()); + assert_eq!(U256::from_big_endian(&result), U256::from(31415926)); +} + +#[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, RawBytes::default()); + 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, RawBytes::default()); + 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, RawBytes::from(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, RawBytes::from(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, RawBytes::default()); + 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, RawBytes::default()); + assert_eq!(U256::from_big_endian(&result), U256::from(123)); +} diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 81a03369b..7bd5ec5d6 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -2,9 +2,10 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; +mod util; + #[allow(dead_code)] pub fn magic_precompile_contract() -> Vec { let init = r#" @@ -39,31 +40,13 @@ return #[test] fn test_precompile_hash() { let contract = magic_precompile_contract(); - - let mut rt = MockRuntime::default(); - - // invoke constructor - rt.expect_validate_caller_any(); - - let params = evm::ConstructorParams { bytecode: contract.into() }; - - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + let mut rt = util::construct_and_verify(contract); // invoke contract let contract_params = vec![0u8; 32]; let input_data = RawBytes::from(contract_params); - rt.expect_validate_caller_any(); - let result = - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); - + let result = util::invoke_contract(&mut rt, input_data); let expected = hex_literal::hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); assert_eq!( diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index 721f9de93..4db707383 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -1,38 +1,27 @@ -use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; +mod util; + #[test] fn test_selfdestruct() { - let mut rt = MockRuntime::default(); + let bytecode = hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap(); let contract = Address::new_id(100); let beneficiary = Address::new_id(1001); - let params = evm::ConstructorParams { - bytecode: hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap().into(), - }; - - // invoke constructor - rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); - rt.expect_validate_caller_any(); - rt.set_origin(contract); - - let result = rt - .call::( - evm::Method::Constructor as u64, - &RawBytes::serialize(params).unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); + 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(); let input_data = RawBytes::from(solidity_params); rt.expect_validate_caller_any(); rt.expect_delete_actor(beneficiary); - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap(); + let result = util::invoke_contract(&mut rt, input_data); + expect_empty(result); rt.verify(); } diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs new file mode 100644 index 000000000..713fbb278 --- /dev/null +++ b/actors/evm/tests/util.rs @@ -0,0 +1,38 @@ +use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; + +#[allow(dead_code)] +pub fn construct_and_verify(initcode: Vec) -> MockRuntime { + init_construct_and_verify(initcode, |_| {}) +} + +pub fn init_construct_and_verify( + initcode: Vec, + initrt: F, +) -> MockRuntime { + let mut rt = MockRuntime::default(); + + // invoke constructor + rt.expect_validate_caller_any(); + initrt(&mut rt); + + let params = evm::ConstructorParams { bytecode: initcode.into() }; + + let result = rt + .call::( + evm::Method::Constructor as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + + rt +} + +#[allow(dead_code)] +pub fn invoke_contract(rt: &mut MockRuntime, input_data: RawBytes) -> RawBytes { + rt.expect_validate_caller_any(); + rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap() +} diff --git a/runtime/src/runtime/chainid.rs b/runtime/src/runtime/chainid.rs new file mode 100644 index 000000000..2237366b2 --- /dev/null +++ b/runtime/src/runtime/chainid.rs @@ -0,0 +1,37 @@ +#[cfg(feature = "mainnet")] +pub const CHAINID: u64 = 314; + +// TODO buildernet +// #[cfg(feature = "buildernet")] +// pub const CHAINID: u64 = 3141; + +#[cfg(feature = "wallaby")] +pub const CHAINID: u64 = 31415; + +#[cfg(feature = "calibrationnet")] +pub const CHAINID: u64 = 314159; + +#[cfg(any(feature = "caterpillarnet", feature = "butterflynet"))] +pub const CHAINID: u64 = 3141592; + +#[cfg(any( + feature = "devnet", + feature = "devnet-wasm", + feature = "testing", + feature = "testing-fake-proofs", +))] +pub const CHAINID: u64 = 31415926; + +// default build is same as a devnet +#[cfg(not(any( + feature = "mainnet", + feature = "wallaby", + feature = "calibrationnet", + feature = "caterpillarnet", + feature = "butterflynet", + feature = "devnet", + feature = "devnet-wasm", + feature = "testing", + feature = "testing-fake-proofs", +)))] +pub const CHAINID: u64 = 31415926; diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 71d2ef043..437ab793c 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -93,6 +93,14 @@ impl MessageInfo for FvmMessage { fn value_received(&self) -> TokenAmount { fvm::message::value_received() } + + fn gas_limit(&self) -> u64 { + fvm::message::gas_limit() + } + + fn gas_premium(&self) -> TokenAmount { + fvm::message::gas_premium() + } } impl Runtime for FvmRuntime @@ -158,6 +166,10 @@ where fvm::sself::current_balance() } + fn actor_balance(&self, id: ActorID) -> TokenAmount { + fvm::actor::balance_of(id) + } + fn resolve_address(&self, address: &Address) -> Option { fvm::actor::resolve_address(address) } @@ -386,6 +398,18 @@ where fn base_fee(&self) -> TokenAmount { fvm::network::base_fee() } + + fn gas_available(&self) -> u64 { + fvm::gas::available() + } + + fn tipset_timestamp(&self) -> u64 { + fvm::network::tipset_timestamp() + } + + fn tipset_cid(&self, epoch: i64) -> Option { + fvm::network::tipset_cid(epoch) + } } impl Primitives for FvmRuntime diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index be45e7c15..5331be8bf 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -29,6 +29,7 @@ use crate::ActorError; mod actor_code; pub mod builtins; +pub mod chainid; pub mod policy; mod randomness; @@ -65,6 +66,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The balance of the receiver. fn current_balance(&self) -> TokenAmount; + /// The balance of an actor. + fn actor_balance(&self, id: ActorID) -> TokenAmount; + /// Resolves an address of any protocol to an ID address (via the Init actor's table). /// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form. /// If the argument is an ID address it is returned directly. @@ -166,7 +170,17 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// `name` provides information about gas charging point fn charge_gas(&mut self, name: &'static str, compute: i64); + /// The current network base fee fn base_fee(&self) -> TokenAmount; + + /// The gas still available for computation + fn gas_available(&self) -> u64; + + /// The current tipset's timestamp, as UNIX seconds + fn tipset_timestamp(&self) -> u64; + + /// The hash of on of the last 256 blocks + fn tipset_cid(&self, epoch: i64) -> Option; } /// Message information available to the actor about executing message. @@ -183,6 +197,12 @@ pub trait MessageInfo { /// The value attached to the message being processed, implicitly /// added to current_balance() before method invocation. fn value_received(&self) -> TokenAmount; + + /// The message gas limit + fn gas_limit(&self) -> u64; + + /// The message gas premium + fn gas_premium(&self) -> TokenAmount; } /// Pure functions implemented as primitives by the runtime. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index c77c2444a..8387327b5 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -151,6 +151,13 @@ pub struct MockRuntime { pub policy: Policy, pub circulating_supply: TokenAmount, + + // iron + pub gas_limit: u64, + pub gas_premium: TokenAmount, + pub actor_balances: HashMap, + pub tipset_timestamp: u64, + pub tipset_cids: Vec, } #[derive(Default)] @@ -172,6 +179,7 @@ pub struct Expectations { pub expect_aggregate_verify_seals: Option, pub expect_replica_verify: Option, pub expect_gas_charge: VecDeque, + pub expect_gas_available: VecDeque, } impl Expectations { @@ -261,6 +269,11 @@ impl Expectations { "expect_gas_charge {:?}, not received", self.expect_gas_charge ); + assert!( + self.expect_gas_available.is_empty(), + "expect_gas_charge {:?}, not received", + self.expect_gas_available + ); } } @@ -295,6 +308,11 @@ impl MockRuntime { expectations: Default::default(), policy: Default::default(), circulating_supply: Default::default(), + gas_limit: 10_000_000_000u64, + gas_premium: Default::default(), + actor_balances: Default::default(), + tipset_timestamp: Default::default(), + tipset_cids: Default::default(), } } } @@ -669,6 +687,11 @@ impl MockRuntime { self.expectations.borrow_mut().expect_gas_charge.push_back(value); } + #[allow(dead_code)] + pub fn expect_gas_available(&mut self, value: u64) { + self.expectations.borrow_mut().expect_gas_available.push_back(value); + } + ///// Private helpers ///// fn require_in_call(&self) { @@ -697,6 +720,12 @@ impl MessageInfo for MockRuntime { fn value_received(&self) -> TokenAmount { self.value_received.clone() } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn gas_premium(&self) -> TokenAmount { + self.gas_premium.clone() + } } impl Runtime> for MockRuntime { @@ -794,6 +823,11 @@ impl Runtime> for MockRuntime { self.balance.borrow().clone() } + fn actor_balance(&self, id: ActorID) -> TokenAmount { + self.require_in_call(); + self.actor_balances.get(&id).cloned().unwrap_or_default() + } + fn resolve_address(&self, address: &Address) -> Option { self.require_in_call(); if let &Payload::ID(id) = address.payload() { @@ -1040,6 +1074,28 @@ impl Runtime> for MockRuntime { fn base_fee(&self) -> TokenAmount { self.base_fee.clone() } + + fn gas_available(&self) -> u64 { + let mut exs = self.expectations.borrow_mut(); + assert!(!exs.expect_gas_available.is_empty(), "unexpected gas available call"); + exs.expect_gas_available.pop_front().unwrap() + } + + fn tipset_timestamp(&self) -> u64 { + self.tipset_timestamp + } + + fn tipset_cid(&self, epoch: i64) -> Option { + if !(0..900).contains(&epoch) { + panic!("ivalidn epoch {}", epoch); + } + + let epoch_index = epoch as usize; + if epoch_index < self.tipset_cids.len() { + return Some(self.tipset_cids[epoch_index]); + } + None + } } impl Primitives for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 0b0d19d7e..345333304 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -523,6 +523,12 @@ impl MessageInfo for InvocationCtx<'_, '_> { fn value_received(&self) -> TokenAmount { self.msg.value.clone() } + fn gas_limit(&self) -> u64 { + todo!() + } + fn gas_premium(&self) -> TokenAmount { + todo!() + } } pub const TEST_VM_RAND_STRING: &str = "i_am_random_____i_am_random_____"; @@ -923,6 +929,22 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat fn base_fee(&self) -> TokenAmount { TokenAmount::zero() } + + fn actor_balance(&self, _id: ActorID) -> TokenAmount { + todo!() + } + + fn gas_available(&self) -> u64 { + todo!() + } + + fn tipset_timestamp(&self) -> u64 { + todo!() + } + + fn tipset_cid(&self, _epoch: i64) -> Option { + todo!() + } } impl Primitives for VM<'_> { From f93cbc702077793ec6d91f107ab13abd2df546e3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 10 Oct 2022 19:50:36 -0700 Subject: [PATCH 073/339] chore: update fvm_sdk and fvm_shared (#748) This updates fvm_sdk and fvm_shared to the latest released version ahead of some f4 changes (so we can atomically back out of those changes). --- Cargo.lock | 37 +++++++++++-------- Cargo.toml | 16 ++++---- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- .../evm/src/interpreter/instructions/state.rs | 10 ++--- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- runtime/src/runtime/fvm.rs | 4 +- runtime/src/runtime/mod.rs | 2 +- runtime/src/test_utils.rs | 4 +- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 2 +- 23 files changed, 59 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9395b4802..d75832760 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1846,7 +1846,8 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.4.2" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d09e5aa7de45452676d18fcb70b750acd65faae7a4fe18fe784b4c85f869fb" dependencies = [ "ahash", "anyhow", @@ -1862,7 +1863,8 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.3" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dfb38431e301bf063a412f870a3d9ebb541d581d1884f95e921f5ea823d29d" dependencies = [ "cs_serde_bytes", "fvm_ipld_encoding", @@ -1874,7 +1876,8 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" dependencies = [ "anyhow", "cid", @@ -1899,7 +1902,8 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.2.2" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce9cfcb4ae444801009182fded790dd56aff9c9a567e54510e2ccf14fb9daf8e" dependencies = [ "anyhow", "cid", @@ -1916,7 +1920,8 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.5.1" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b5c939897aa1bfd63e7cb9c458ba10689371af3278ff20d66c6f8ca152c6c0" dependencies = [ "anyhow", "byteorder", @@ -1935,8 +1940,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.2" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +version = "3.0.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab114165b001c7ff18de384d56c05992b636a29180f704c1bb09e28fe57e6a3" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1949,8 +1955,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.2" -source = "git+https://github.com/filecoin-project/ref-fvm#19ad768ce45c1408c1e1695b92305ac2e9a745d6" +version = "3.0.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f383d7556da494339f507766bc1caa0cf612fa1986cc94bb76509e05ad7037d7" dependencies = [ "anyhow", "blake2b_simd", @@ -2342,9 +2349,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.134" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libipld-core" @@ -3519,9 +3526,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -3784,9 +3791,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" diff --git a/Cargo.toml b/Cargo.toml index 9b476d5d8..be138b0b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,14 +55,14 @@ members = [ "test_vm", ] -[patch.crates-io] -fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } -fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } +#[patch.crates-io] +#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } +#fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } ## 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. diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 00fa25516..4487b3238 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 5fc30f610..9f5a200d3 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 134fd0289..f8cf81e0b 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.2", optional = true } -fvm_shared = { version = "3.0.0-alpha.2", optional = true } +fvm_sdk = { version = "3.0.0-alpha.3", optional = true } +fvm_shared = { version = "3.0.0-alpha.3", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 3cc4fe6a2..41777a15c 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index b8d2100a5..dba197243 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -13,11 +13,11 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( ) -> Result<(), StatusCode> { let actor = state.stack.pop(); - let balance = if let Some(id) = EthAddress::try_from(actor).ok().and_then(|addr| addr.as_id()) { - U256::from(&platform.rt.actor_balance(id)) - } else { - U256::zero() - }; + let balance = EthAddress::try_from(actor) + .ok() + .and_then(|addr| addr.as_id()) + .and_then(|id| platform.rt.actor_balance(id).as_ref().map(U256::from)) + .unwrap_or_default(); state.stack.push(balance); Ok(()) diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index dc581ebb8..e4ca9664e 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 46328c384..ea33fecf3 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_bitfield = "0.5.2" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 95a567a3c..8bb85a530 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_hamt = "0.5.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index e61761f8b..9fd2028b7 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index d259c5683..df201195c 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 60f448780..6ba19b13a 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index dce1395ea..bd1bf55bc 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index addf5b99b..a0b13271d 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 0337e94c3..6d1f6ffd0 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a5d3a5579..211ea0e69 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.5.1" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.2", optional = true } +fvm_sdk = { version = "3.0.0-alpha.3", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 437ab793c..bacb0258f 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -166,7 +166,7 @@ where fvm::sself::current_balance() } - fn actor_balance(&self, id: ActorID) -> TokenAmount { + fn actor_balance(&self, id: ActorID) -> Option { fvm::actor::balance_of(id) } @@ -408,7 +408,7 @@ where } fn tipset_cid(&self, epoch: i64) -> Option { - fvm::network::tipset_cid(epoch) + fvm::network::tipset_cid(epoch).ok() } } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 5331be8bf..de5c71e1b 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -67,7 +67,7 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { fn current_balance(&self) -> TokenAmount; /// The balance of an actor. - fn actor_balance(&self, id: ActorID) -> TokenAmount; + fn actor_balance(&self, id: ActorID) -> Option; /// Resolves an address of any protocol to an ID address (via the Init actor's table). /// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 8387327b5..469ed0a6d 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -823,9 +823,9 @@ impl Runtime> for MockRuntime { self.balance.borrow().clone() } - fn actor_balance(&self, id: ActorID) -> TokenAmount { + fn actor_balance(&self, id: ActorID) -> Option { self.require_in_call(); - self.actor_balances.get(&id).cloned().unwrap_or_default() + self.actor_balances.get(&id).cloned() } fn resolve_address(&self, address: &Address) -> Option { diff --git a/state/Cargo.toml b/state/Cargo.toml index 1643149e9..6f1c0ccff 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 8e58180af..55d7f554e 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,7 +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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.3", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 345333304..64b73a52a 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -930,7 +930,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat TokenAmount::zero() } - fn actor_balance(&self, _id: ActorID) -> TokenAmount { + fn actor_balance(&self, _id: ActorID) -> Option { todo!() } From 184e59069a12d02209412a416ddaa65c66d2394e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 10 Oct 2022 21:13:32 -0700 Subject: [PATCH 074/339] Store/lookup predictable addresses (#743) feat: save predictable addresses in the actor tree --- Cargo.lock | 8 ++--- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +-- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/init/src/lib.rs | 4 +-- actors/init/tests/init_actor_test.rs | 19 ++++++----- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +-- runtime/src/runtime/fvm.rs | 9 +++-- runtime/src/runtime/mod.rs | 9 +++-- runtime/src/test_utils.rs | 25 +++++++++++--- runtime/src/util/chaos/mod.rs | 2 +- state/Cargo.toml | 2 +- state/src/check.rs | 4 +++ test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 49 ++++++++++++++++++++-------- test_vm/tests/init_test.rs | 2 +- test_vm/tests/test_vm_test.rs | 17 ++++++++-- 27 files changed, 123 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d75832760..2ae56ecac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1940,9 +1940,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab114165b001c7ff18de384d56c05992b636a29180f704c1bb09e28fe57e6a3" +checksum = "52916d828994181d1e61b01ad254d3502291869e92892ddd0624dd49951e47cc" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1955,9 +1955,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f383d7556da494339f507766bc1caa0cf612fa1986cc94bb76509e05ad7037d7" +checksum = "6806b52893ee4da415d47136ffb3c495af82c6e830fcb803a325a3b573b4893b" dependencies = [ "anyhow", "blake2b_simd", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 4487b3238..9c1d13e4b 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 9f5a200d3..78858262b 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index f8cf81e0b..a556effed 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.3", optional = true } -fvm_shared = { version = "3.0.0-alpha.3", optional = true } +fvm_sdk = { version = "3.0.0-alpha.4", optional = true } +fvm_shared = { version = "3.0.0-alpha.4", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 41777a15c..6d1f4d9f7 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index e4ca9664e..50440fccb 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 5587d0dbd..af327b986 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -93,7 +93,7 @@ impl Actor { })?; // Create an empty actor - rt.create_actor(params.code_cid, id_address)?; + rt.create_actor(params.code_cid, id_address, None)?; // Invoke constructor rt.send( @@ -144,7 +144,7 @@ impl Actor { })?; // Create an empty actor - rt.create_actor(params.code_cid, id_address)?; + rt.create_actor(params.code_cid, id_address, Some(delegated_address))?; // Invoke constructor rt.send( diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index d968eb239..03e558091 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -67,7 +67,7 @@ fn repeated_robust_address() { // Next id let expected_id = 100; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, None); // Expect a send to the multisig actor constructor rt.expect_send( @@ -125,7 +125,7 @@ fn create_2_payment_channels() { let expected_id = 100 + n; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*PAYCH_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*PAYCH_ACTOR_CODE_ID, expected_id, None); let fake_params = ConstructorParams { network_name: String::from("fake_param") }; @@ -169,7 +169,7 @@ fn create_storage_miner() { let expected_id = 100; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id, None); let fake_params = ConstructorParams { network_name: String::from("fake_param") }; @@ -218,7 +218,7 @@ fn create_multisig_actor() { // Next id let expected_id = 100; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, None); let fake_params = ConstructorParams { network_name: String::from("fake_param") }; // Expect a send to the multisig actor constructor @@ -253,7 +253,7 @@ fn sending_constructor_failure() { // Create the next id address let expected_id = 100; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id, None); let fake_params = ConstructorParams { network_name: String::from("fake_param") }; rt.expect_send( @@ -292,10 +292,14 @@ fn call_exec4() { let unique_address = Address::new_actor(b"test"); rt.new_actor_addr = Some(unique_address); + // Make the f4 addr + let subaddr = b"foobar"; + let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); + // Next id let expected_id = 100; let expected_id_addr = Address::new_id(expected_id); - rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, Some(f4_addr)); let fake_params = ConstructorParams { network_name: String::from("fake_param") }; // Expect a send to the multisig actor constructor @@ -308,9 +312,6 @@ fn call_exec4() { ExitCode::OK, ); - let subaddr = b"foobar"; - let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); - // Return should have been successful. Check the returned addresses let exec_ret = exec4_and_verify(&mut rt, subaddr, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap(); diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index ea33fecf3..9f4d63a39 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_bitfield = "0.5.2" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 8bb85a530..2b028f680 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } fvm_ipld_hamt = "0.5.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 9fd2028b7..bc88cbe99 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index df201195c..e5ed4e920 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 6ba19b13a..5dd3a0f55 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index bd1bf55bc..d89cd853a 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index a0b13271d..47680d608 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 6d1f6ffd0..b9ae682f3 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 211ea0e69..1fc704160 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.5.1" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.3", optional = true } +fvm_sdk = { version = "3.0.0-alpha.4", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index bacb0258f..14b75a602 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -363,13 +363,18 @@ where Ok(fvm::actor::new_actor_address()) } - fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> { + fn create_actor( + &mut self, + code_id: Cid, + actor_id: ActorID, + predictable_address: Option

, + ) -> Result<(), ActorError> { if self.in_transaction { return Err( actor_error!(assertion_failed; "create_actor is not allowed during transaction"), ); } - fvm::actor::create_actor(actor_id, &code_id).map_err(|e| match e { + fvm::actor::create_actor(actor_id, &code_id, predictable_address).map_err(|e| match e { ErrorNumber::IllegalArgument => { ActorError::illegal_argument("failed to create actor".into()) } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index de5c71e1b..7eb36a7fc 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -140,9 +140,14 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Always an ActorExec address. fn new_actor_address(&mut self) -> Result; - /// Creates an actor with code `codeID` and address `address`, with empty state. + /// Creates an actor with code `codeID`, an empty state, id `actor_id`, and an optional predictable address. /// May only be called by Init actor. - fn create_actor(&mut self, code_id: Cid, address: ActorID) -> Result<(), ActorError>; + fn create_actor( + &mut self, + code_id: Cid, + actor_id: ActorID, + predictable_address: Option
, + ) -> Result<(), ActorError>; /// Deletes the executing actor from the state tree, transferring any balance to beneficiary. /// Aborts if the beneficiary does not exist. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 469ed0a6d..5245a4e0c 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -317,10 +317,11 @@ impl MockRuntime { } } -#[derive(Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug)] pub struct ExpectCreateActor { pub code_id: Cid, pub actor_id: ActorID, + pub predictable_address: Option
, } #[derive(Clone, Debug)] @@ -595,8 +596,13 @@ impl MockRuntime { } #[allow(dead_code)] - pub fn expect_create_actor(&mut self, code_id: Cid, actor_id: ActorID) { - let a = ExpectCreateActor { code_id, actor_id }; + pub fn expect_create_actor( + &mut self, + code_id: Cid, + actor_id: ActorID, + predictable_address: Option
, + ) { + let a = ExpectCreateActor { code_id, actor_id, predictable_address }; self.expectations.borrow_mut().expect_create_actor = Some(a); } @@ -1015,7 +1021,12 @@ impl Runtime> for MockRuntime { Ok(ret) } - fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> { + fn create_actor( + &mut self, + code_id: Cid, + actor_id: ActorID, + predictable_address: Option
, + ) -> Result<(), ActorError> { self.require_in_call(); if self.in_transaction { return Err(actor_error!(assertion_failed; "side-effect within transaction")); @@ -1027,7 +1038,11 @@ impl Runtime> for MockRuntime { .take() .expect("unexpected call to create actor"); - assert!(expect_create_actor.code_id == code_id && expect_create_actor.actor_id == actor_id, "unexpected actor being created, expected code: {:?} address: {:?}, actual code: {:?} address: {:?}", expect_create_actor.code_id, expect_create_actor.actor_id, code_id, actor_id); + assert_eq!( + expect_create_actor, + ExpectCreateActor { code_id, actor_id, predictable_address }, + "unexpected actor being created" + ); Ok(()) } diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs index de8f4a807..335e119ea 100644 --- a/runtime/src/util/chaos/mod.rs +++ b/runtime/src/util/chaos/mod.rs @@ -123,7 +123,7 @@ impl Actor { let actor_address = arg.actor_id; - rt.create_actor(actor_cid, actor_address) + rt.create_actor(actor_cid, actor_address, None) } /// Resolves address, and returns the resolved address (defaulting to 0 ID) and success boolean. diff --git a/state/Cargo.toml b/state/Cargo.toml index 6f1c0ccff..a8e533114 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_encoding = "0.2.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/state/src/check.rs b/state/src/check.rs index 331f0e89c..daf3cc6a3 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -60,6 +60,10 @@ pub struct Actor { pub call_seq_num: u64, /// Token balance of the actor pub balance: TokenAmount, + /// The actor's "predictable" address, if assigned. + /// + /// This field is set on actor creation and never modified. + pub address: Option
, } /// A specialization of a map of ID-addresses to actor heads. diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 55d7f554e..602ca0eda 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,7 +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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.3", default-features = false } +fvm_shared = { version = "3.0.0-alpha.4", default-features = false } fvm_ipld_encoding = { version = "0.2.2", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.2" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 64b73a52a..b95699081 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -140,17 +140,23 @@ impl<'bs> VM<'bs> { let sys_st = SystemState::new(store).unwrap(); let sys_head = v.put_store(&sys_st); let sys_value = faucet_total.clone(); // delegate faucet funds to system so we can construct faucet by sending to bls addr - v.set_actor(SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value)); + v.set_actor(SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value, None)); // init let init_st = InitState::new(store, "integration-test".to_string()).unwrap(); let init_head = v.put_store(&init_st); - v.set_actor(INIT_ACTOR_ADDR, actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero())); + v.set_actor( + INIT_ACTOR_ADDR, + actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero(), None), + ); // reward let reward_head = v.put_store(&RewardState::new(StoragePower::zero())); - v.set_actor(REWARD_ACTOR_ADDR, actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total)); + v.set_actor( + REWARD_ACTOR_ADDR, + actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total, None), + ); // cron let builtin_entries = vec![ @@ -164,20 +170,23 @@ impl<'bs> VM<'bs> { }, ]; let cron_head = v.put_store(&CronState { entries: builtin_entries }); - v.set_actor(CRON_ACTOR_ADDR, actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero())); + v.set_actor( + CRON_ACTOR_ADDR, + actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero(), None), + ); // power let power_head = v.put_store(&PowerState::new(&v.store).unwrap()); v.set_actor( STORAGE_POWER_ACTOR_ADDR, - actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero()), + actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero(), None), ); // market let market_head = v.put_store(&MarketState::new(&v.store).unwrap()); v.set_actor( STORAGE_MARKET_ACTOR_ADDR, - actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero()), + actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero(), None), ); // verifreg @@ -225,14 +234,14 @@ impl<'bs> VM<'bs> { let verifreg_head = v.put_store(&VerifRegState::new(&v.store, root_msig_addr).unwrap()); v.set_actor( VERIFIED_REGISTRY_ACTOR_ADDR, - actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero()), + actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero(), None), ); // burnt funds let burnt_funds_head = v.put_store(&AccountState { address: BURNT_FUNDS_ACTOR_ADDR }); v.set_actor( BURNT_FUNDS_ACTOR_ADDR, - actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero()), + actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero(), None), ); // create a faucet with 1 billion FIL for setting up test accounts @@ -593,7 +602,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { subinvocations: RefCell::new(vec![]), }; if is_account { - new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id).unwrap(); + new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); let res = new_ctx.invoke(); let invoc = new_ctx.gather_trace(res); RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { @@ -601,7 +610,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { subinvocs }); } else { - new_ctx.create_actor(*EMBRYO_ACTOR_CODE_ID, target_id).unwrap(); + new_ctx.create_actor(*EMBRYO_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); } } @@ -686,7 +695,12 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocation, 'bs> { - fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> { + fn create_actor( + &mut self, + code_id: Cid, + actor_id: ActorID, + predictable_address: Option
, + ) -> Result<(), ActorError> { match NON_SINGLETON_CODES.get(&code_id) { Some(_) => (), None => { @@ -702,7 +716,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat act.code = code_id; act } - None => actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero()), + None => actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero(), predictable_address), _ => { // can happen if an actor is deployed to an f4 address. return Err(ActorError::unchecked( @@ -1093,10 +1107,17 @@ pub struct Actor { pub head: Cid, pub call_seq_num: u64, pub balance: TokenAmount, + pub predictable_address: Option
, } -pub fn actor(code: Cid, head: Cid, seq: u64, bal: TokenAmount) -> Actor { - Actor { code, head, call_seq_num: seq, balance: bal } +pub fn actor( + code: Cid, + head: Cid, + call_seq_num: u64, + balance: TokenAmount, + predictable_address: Option
, +) -> Actor { + Actor { code, head, call_seq_num, balance, predictable_address } } #[derive(Clone)] diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs index ae9b5a440..3775b4be7 100644 --- a/test_vm/tests/init_test.rs +++ b/test_vm/tests/init_test.rs @@ -26,7 +26,7 @@ fn embryo_deploy() { // Create a "fake" eam. v.set_actor( EAM_ACTOR_ADDR, - actor(*ACCOUNT_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero()), + actor(*ACCOUNT_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), ); // Create an embryo. diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 5fb0c01f2..84f5ade06 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -21,14 +21,25 @@ fn state_control() { let addr2 = Address::new_id(2222); // set actor - let a1 = - actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from_atto(10u8)); + let a1 = actor( + *ACCOUNT_ACTOR_CODE_ID, + make_builtin(b"a1-head"), + 42, + TokenAmount::from_atto(10u8), + None, + ); v.set_actor(addr1, a1.clone()); let out = v.get_actor(addr1).unwrap(); assert_eq!(out, a1); let check = v.checkpoint(); - let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from_atto(1u8)); + let a2 = actor( + *PAYCH_ACTOR_CODE_ID, + make_builtin(b"a2-head"), + 88, + 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 From 4860e2fcae19a212d4377f6496ad1f36fab6b03b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Oct 2022 21:24:58 -0700 Subject: [PATCH 075/339] chore: update fvm_ipld_encoding (#750) * chore: update fvm encoding This switches to a bundled "strict_bytes" module instead of the "cs_serde_bytes" crate. This: - cs_serde_bytes was already a fork of serde_bytes. - We only needed a very small fraction of this crate. - We can now support fixed byte arrays. * refactor: import strict_bytes instead of serde_bytes --- Cargo.lock | 67 +++--- Cargo.toml | 2 +- actors/account/Cargo.toml | 4 +- actors/cron/Cargo.toml | 4 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 6 +- .../tests/measurements/array_read_n1.jsonline | 200 +++++++++--------- .../measurements/array_read_n100.jsonline | 200 +++++++++--------- .../measurements/mapping_add_n1.jsonline | 4 +- .../measurements/mapping_add_n100.jsonline | 4 +- .../measurements/mapping_overwrite.jsonline | 200 +++++++++--------- .../measurements/mapping_read_n1.jsonline | 200 +++++++++--------- .../measurements/mapping_read_n100.jsonline | 200 +++++++++--------- actors/init/Cargo.toml | 6 +- actors/market/Cargo.toml | 10 +- actors/miner/Cargo.toml | 10 +- actors/miner/src/state.rs | 6 +- actors/miner/src/types.rs | 20 +- actors/multisig/Cargo.toml | 6 +- actors/multisig/src/types.rs | 4 +- actors/paych/Cargo.toml | 6 +- actors/paych/src/types.rs | 10 +- actors/power/Cargo.toml | 6 +- actors/power/src/ext.rs | 6 +- actors/power/src/types.rs | 4 +- actors/reward/Cargo.toml | 4 +- actors/system/Cargo.toml | 4 +- actors/verifreg/Cargo.toml | 6 +- runtime/Cargo.toml | 10 +- state/Cargo.toml | 4 +- test_vm/Cargo.toml | 8 +- 31 files changed, 612 insertions(+), 613 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ae56ecac..5b9ab24b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,17 +24,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[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.19" @@ -1343,9 +1332,9 @@ dependencies = [ [[package]] name = "fil_actor_bundler" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1e563d609dede95fb0a847ea4ebfe71f5d7074703fc3e92736c74be4b237c2" +checksum = "6d0550a13a20decf920aeeb630a648b13174af5acf6b513895996ff1cd09393d" dependencies = [ "anyhow", "async-std", @@ -1453,7 +1442,7 @@ dependencies = [ "fvm_ipld_hamt", "fvm_shared", "itertools", - "libipld-core", + "libipld-core 0.13.1", "log", "multihash", "num-derive", @@ -1845,11 +1834,10 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09e5aa7de45452676d18fcb70b750acd65faae7a4fe18fe784b4c85f869fb" +checksum = "21efabb8ae696f1cfd90b176a3600176010bd6ad3fb9919b16a24b43ba866b3d" dependencies = [ - "ahash", "anyhow", "cid", "fvm_ipld_blockstore", @@ -1862,11 +1850,10 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dfb38431e301bf063a412f870a3d9ebb541d581d1884f95e921f5ea823d29d" +checksum = "d1950291f40d2d1047eb0a4568f7ef6d5b4973452dcef012dffb1957fe483ff7" dependencies = [ - "cs_serde_bytes", "fvm_ipld_encoding", "serde", "thiserror", @@ -1886,9 +1873,9 @@ dependencies = [ [[package]] name = "fvm_ipld_car" -version = "0.4.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade8c2f76a9e014f2fdaeb693a0884f49eca5ee663958e1c8c1db8d23290817c" +checksum = "c60423568393a284de6d7c342cd664690611f27d223eb78629fa568ddd4e7951" dependencies = [ "cid", "futures", @@ -1901,13 +1888,12 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9cfcb4ae444801009182fded790dd56aff9c9a567e54510e2ccf14fb9daf8e" +checksum = "cf531c59e7c9a4c9044a34d1b44c9d544fab1eb0ba50aaa18121fe698d4fec35" dependencies = [ "anyhow", "cid", - "cs_serde_bytes", "fvm_ipld_blockstore", "multihash", "serde", @@ -1919,18 +1905,17 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b5c939897aa1bfd63e7cb9c458ba10689371af3278ff20d66c6f8ca152c6c0" +checksum = "e48ac0db0c676fe9663f34782d448320b0c71d5e0afb7df38914983ae4d46faa" dependencies = [ "anyhow", "byteorder", "cid", - "cs_serde_bytes", "forest_hash_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "libipld-core", + "libipld-core 0.14.0", "multihash", "once_cell", "serde", @@ -1940,9 +1925,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.4" +version = "3.0.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52916d828994181d1e61b01ad254d3502291869e92892ddd0624dd49951e47cc" +checksum = "fddbad8bddf3fcc961e8f2884d8eb5610485606f808b50955945dcd3cc5c31eb" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1955,15 +1940,14 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.4" +version = "3.0.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806b52893ee4da415d47136ffb3c495af82c6e830fcb803a325a3b573b4893b" +checksum = "4f2a195a55012c943894f71f8ed505dd3efe73b13f2cab70fb9e4d335c63d0f3" dependencies = [ "anyhow", "blake2b_simd", "byteorder", "cid", - "cs_serde_bytes", "data-encoding", "data-encoding-macro", "fvm_ipld_blockstore", @@ -2368,6 +2352,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "libipld-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" +dependencies = [ + "anyhow", + "cid", + "core2", + "multibase", + "multihash", + "serde", + "thiserror", +] + [[package]] name = "libsecp256k1" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index be138b0b9..77571660b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ fil_actor_embryo = { version = "10.0.0-alpha.1", path = "./actors/embryo", featu fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] } [build-dependencies] -fil_actor_bundler = "4.0.0" +fil_actor_bundler = "4.1.0" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } fil_actors_runtime = { version = "10.0.0-alpha.1", path = "runtime" } num-traits = "0.2.15" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 9c1d13e4b..7b128d901 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,12 +14,12 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", 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.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 78858262b..f31448b75 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", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", 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.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index a556effed..095e89b17 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.4", optional = true } -fvm_shared = { version = "3.0.0-alpha.4", optional = true } +fvm_sdk = { version = "3.0.0-alpha.5", optional = true } +fvm_shared = { version = "3.0.0-alpha.5", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 6d1f4d9f7..13d3d667e 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_hamt = "0.5.1" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" num-traits = "0.2.14" @@ -25,7 +25,7 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] anyhow = "1.0.56" log = "0.4.14" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" sha3 = { version = "0.10", default-features = false } rlp = { version = "0.5.1", default-features = false } bytes = { version = "1.1.0", features = ["serde"], default-features = false } diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 43ad56113..258429acb 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":31004,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":31004,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index a63852f7b..2b8cf5561 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":42071,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":42389,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":43499,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":42714,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":42309,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":39006,"get_count":13,"put_bytes":31004,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":41130,"get_count":13,"put_bytes":31004,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":41853,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":44292,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":46580,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":41306,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":43658,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":44263,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":43508,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":42418,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":45239,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":44415,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":42952,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":46498,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":44904,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":43648,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":45119,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":43298,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":43814,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":40601,"get_count":13,"put_bytes":31004,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":43921,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":44288,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":45164,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":43774,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":43065,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":44063,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":44174,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":43940,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":43621,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":41546,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":45997,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":45285,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":43236,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":45813,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":41085,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":42985,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":41989,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":45401,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":43754,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":39809,"get_count":13,"put_bytes":31004,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":42601,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":44114,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":43108,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":44068,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":44180,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":43966,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":43556,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":41907,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":40240,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":41887,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":42994,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":43109,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":41296,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":42255,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":42860,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":42656,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":43085,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":44355,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":43239,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":42948,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":43936,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":41964,"get_count":14,"put_bytes":31004,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":44570,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":40879,"get_count":13,"put_bytes":31004,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":44374,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":44481,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":43900,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":44752,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":42693,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":46248,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":44650,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":43511,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":42993,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":44146,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":45293,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":41858,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":43920,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":42883,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":46903,"get_count":18,"put_bytes":31004,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":43542,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":43959,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":43281,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":42727,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":42435,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":43920,"get_count":17,"put_bytes":31004,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":43933,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":43791,"get_count":16,"put_bytes":31004,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":42548,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":41899,"get_count":15,"put_bytes":31004,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":42711,"get_count":16,"put_bytes":31004,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":42071,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":42389,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":43499,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":42714,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":42309,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":39006,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":41130,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":41853,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":44292,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":46580,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":41306,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":43658,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":44263,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":43508,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":42418,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":45239,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":44415,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":42952,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":46498,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":44904,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":43648,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":45119,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":43298,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":43814,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":40601,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":43921,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":44288,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":45164,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":43774,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":43065,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":44063,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":44174,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":43940,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":43621,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":41546,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":45997,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":45285,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":43236,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":45813,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":41085,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":42985,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":41989,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":45401,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":43754,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":39809,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":42601,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":44114,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":43108,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":44068,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":44180,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":43966,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":43556,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":41907,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":40240,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":41887,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":42994,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":43109,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":41296,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":42255,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":42860,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":42656,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":43085,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":44355,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":43239,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":42948,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":43936,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":41964,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":44570,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":40879,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":44374,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":44481,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":43900,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":44752,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":42693,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":46248,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":44650,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":43511,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":42993,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":44146,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":45293,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":41858,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":43920,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":42883,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":46903,"get_count":18,"put_bytes":87,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":43542,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":43959,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":43281,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":42727,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":42435,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":43920,"get_count":17,"put_bytes":87,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":43933,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":43791,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":42548,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":41899,"get_count":15,"put_bytes":87,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":42711,"get_count":16,"put_bytes":87,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 47f8b6edf..e6b2f5a5c 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,4 +1,4 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2188,"put_count":5}} +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2185,"put_count":4}} {"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":180,"put_count":2}} {"i":2,"series":1,"stats":{"get_bytes":2275,"get_count":4,"put_bytes":261,"put_count":2}} {"i":3,"series":1,"stats":{"get_bytes":2356,"get_count":4,"put_bytes":345,"put_count":2}} @@ -98,7 +98,7 @@ {"i":97,"series":1,"stats":{"get_bytes":9383,"get_count":4,"put_bytes":7362,"put_count":2}} {"i":98,"series":1,"stats":{"get_bytes":9457,"get_count":4,"put_bytes":7437,"put_count":2}} {"i":99,"series":1,"stats":{"get_bytes":9532,"get_count":4,"put_bytes":7511,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7511,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":87,"put_count":1}} {"i":1,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7585,"put_count":2}} {"i":2,"series":2,"stats":{"get_bytes":9680,"get_count":4,"put_bytes":7658,"put_count":2}} {"i":3,"series":2,"stats":{"get_bytes":9753,"get_count":4,"put_bytes":7732,"put_count":2}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index d131c2554..3dca70654 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,4 +1,4 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2188,"put_count":5}} +{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2185,"put_count":4}} {"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":7494,"put_count":2}} {"i":2,"series":1,"stats":{"get_bytes":9589,"get_count":4,"put_bytes":15001,"put_count":4}} {"i":3,"series":1,"stats":{"get_bytes":16765,"get_count":5,"put_bytes":22582,"put_count":11}} @@ -98,7 +98,7 @@ {"i":97,"series":1,"stats":{"get_bytes":254250,"get_count":88,"put_bytes":259638,"put_count":86}} {"i":98,"series":1,"stats":{"get_bytes":245365,"get_count":83,"put_bytes":250747,"put_count":81}} {"i":99,"series":1,"stats":{"get_bytes":265670,"get_count":91,"put_bytes":271053,"put_count":89}} -{"i":0,"series":2,"stats":{"get_bytes":262559,"get_count":87,"put_bytes":11133,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":262559,"get_count":87,"put_bytes":87,"put_count":1}} {"i":1,"series":2,"stats":{"get_bytes":256304,"get_count":86,"put_bytes":261594,"put_count":84}} {"i":2,"series":2,"stats":{"get_bytes":259549,"get_count":88,"put_bytes":264832,"put_count":86}} {"i":3,"series":2,"stats":{"get_bytes":258453,"get_count":87,"put_bytes":263745,"put_count":85}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index 67f978723..8ef30b107 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -98,103 +98,103 @@ {"i":97,"series":1,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":26333,"put_count":7}} {"i":98,"series":1,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":27600,"put_count":9}} {"i":99,"series":1,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":27547,"put_count":9}} -{"i":0,"series":2,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":24138,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":24138,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":24138,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":24138,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":24138,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":24138,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":24138,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":24138,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":71,"series":2,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":24138,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":77,"series":2,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":24138,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":24138,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":82,"series":2,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":24138,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":24138,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":24138,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":24138,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":24138,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":24138,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":24138,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":24138,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":24138,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":2,"series":2,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":3,"series":2,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":4,"series":2,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":5,"series":2,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":6,"series":2,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":7,"series":2,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":8,"series":2,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":9,"series":2,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":10,"series":2,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":11,"series":2,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":12,"series":2,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":13,"series":2,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":14,"series":2,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":15,"series":2,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":16,"series":2,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":17,"series":2,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":18,"series":2,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":19,"series":2,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":20,"series":2,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":21,"series":2,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":22,"series":2,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":23,"series":2,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":24,"series":2,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":25,"series":2,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":26,"series":2,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":27,"series":2,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":28,"series":2,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":29,"series":2,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":30,"series":2,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":31,"series":2,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":87,"put_count":1}} +{"i":32,"series":2,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":33,"series":2,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":34,"series":2,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":35,"series":2,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":36,"series":2,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":37,"series":2,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":38,"series":2,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":39,"series":2,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":40,"series":2,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":41,"series":2,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":42,"series":2,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":43,"series":2,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":44,"series":2,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":45,"series":2,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":87,"put_count":1}} +{"i":46,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":47,"series":2,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":48,"series":2,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":49,"series":2,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":50,"series":2,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":87,"put_count":1}} +{"i":51,"series":2,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":52,"series":2,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":53,"series":2,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":54,"series":2,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":87,"put_count":1}} +{"i":55,"series":2,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":56,"series":2,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":57,"series":2,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":58,"series":2,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":59,"series":2,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":60,"series":2,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":61,"series":2,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":62,"series":2,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":63,"series":2,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":64,"series":2,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":65,"series":2,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":66,"series":2,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":67,"series":2,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":68,"series":2,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":69,"series":2,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":70,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":71,"series":2,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":87,"put_count":1}} +{"i":72,"series":2,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":73,"series":2,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":74,"series":2,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":75,"series":2,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":76,"series":2,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":77,"series":2,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":78,"series":2,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":87,"put_count":1}} +{"i":79,"series":2,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":80,"series":2,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":87,"put_count":1}} +{"i":81,"series":2,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":82,"series":2,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":83,"series":2,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":84,"series":2,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":87,"put_count":1}} +{"i":85,"series":2,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":87,"put_count":1}} +{"i":86,"series":2,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":87,"series":2,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":88,"series":2,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":89,"series":2,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":90,"series":2,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":87,"put_count":1}} +{"i":91,"series":2,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":87,"put_count":1}} +{"i":92,"series":2,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":93,"series":2,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":94,"series":2,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":87,"put_count":1}} +{"i":95,"series":2,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":96,"series":2,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":87,"put_count":1}} +{"i":97,"series":2,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":87,"put_count":1}} +{"i":98,"series":2,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":99,"series":2,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":87,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index b83e223ee..dc913e2fd 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":16957,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":16889,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":15923,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":15695,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":15629,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":15551,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":16513,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":15921,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":16072,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":16439,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":16294,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":16363,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":15924,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":15999,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":15991,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":17322,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":16296,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":17175,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":17037,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":16069,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":16740,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":16298,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":15483,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":16070,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":16665,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":15626,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":15925,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":16142,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":16000,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":16808,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":16436,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":17619,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":15849,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":17178,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":15702,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":16290,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":16071,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":15997,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":16657,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":16440,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":16293,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":16884,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":16588,"get_count":5,"put_bytes":11133,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":11133,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":16957,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":16889,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":15923,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":15695,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":15629,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":15551,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":16513,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":15921,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":16072,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":16439,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":16294,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":16363,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":15924,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":15999,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":15991,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":17322,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":16296,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":17175,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":17037,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":16069,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":16740,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":16298,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":15483,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":16070,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":16665,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":15626,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":15925,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":16142,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":16000,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":16808,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":16436,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":17619,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":15849,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":17178,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":15702,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":16290,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":16071,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":15997,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":16657,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":16440,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":16293,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":16884,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":16588,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":87,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index d84eb86fc..0521b6cf7 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":266491,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":257836,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":246528,"get_count":81,"put_bytes":11133,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":258731,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":269892,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":277895,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":253688,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":244118,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":261238,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":267250,"get_count":90,"put_bytes":11133,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":247536,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":260116,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":251442,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":249516,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":266163,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":262195,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":272231,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":265168,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":255856,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":269006,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":250187,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":282751,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":252070,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":260660,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":260160,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":239583,"get_count":79,"put_bytes":11133,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":265024,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":266671,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":274442,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":256022,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":277883,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":266982,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":262070,"get_count":90,"put_bytes":11133,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":256448,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":249022,"get_count":81,"put_bytes":11133,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":266876,"get_count":90,"put_bytes":11133,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":245415,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":269056,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":264272,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":253375,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":258091,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":243447,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":249141,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":251888,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":264174,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":258121,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":241572,"get_count":80,"put_bytes":11133,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":254362,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":246996,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":261938,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":269981,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":258752,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":259423,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":263537,"get_count":90,"put_bytes":11133,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":257615,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":260749,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":255598,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":278597,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":253717,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":269693,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":258100,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":251759,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":278232,"get_count":94,"put_bytes":11133,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":260531,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":261868,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":262696,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":272423,"get_count":90,"put_bytes":11133,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":270334,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":261406,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":220833,"get_count":75,"put_bytes":11133,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":237960,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":251797,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":247447,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":278770,"get_count":93,"put_bytes":11133,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":246382,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":246300,"get_count":82,"put_bytes":11133,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":253088,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":252908,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":259910,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":267248,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":259625,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":272858,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":255094,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":281548,"get_count":94,"put_bytes":11133,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":255832,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":268789,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":264134,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":245157,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":257535,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":246060,"get_count":84,"put_bytes":11133,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":251084,"get_count":86,"put_bytes":11133,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":259741,"get_count":87,"put_bytes":11133,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":256740,"get_count":85,"put_bytes":11133,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":272969,"get_count":91,"put_bytes":11133,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":263014,"get_count":89,"put_bytes":11133,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":257747,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":273134,"get_count":92,"put_bytes":11133,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":266752,"get_count":88,"put_bytes":11133,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":254375,"get_count":83,"put_bytes":11133,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":271999,"get_count":91,"put_bytes":11133,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":266491,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":257836,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":246528,"get_count":81,"put_bytes":87,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":258731,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":269892,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":277895,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":253688,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":244118,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":261238,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":267250,"get_count":90,"put_bytes":87,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":247536,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":260116,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":251442,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":249516,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":266163,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":262195,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":272231,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":265168,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":255856,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":269006,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":250187,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":282751,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":252070,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":260660,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":260160,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":239583,"get_count":79,"put_bytes":87,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":265024,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":266671,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":274442,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":256022,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":277883,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":266982,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":262070,"get_count":90,"put_bytes":87,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":256448,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":249022,"get_count":81,"put_bytes":87,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":266876,"get_count":90,"put_bytes":87,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":245415,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":269056,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":264272,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":253375,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":258091,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":243447,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":249141,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":251888,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":264174,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":258121,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":241572,"get_count":80,"put_bytes":87,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":254362,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":246996,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":261938,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":269981,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":258752,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":259423,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":263537,"get_count":90,"put_bytes":87,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":257615,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":260749,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":255598,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":278597,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":253717,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":269693,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":258100,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":251759,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":278232,"get_count":94,"put_bytes":87,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":260531,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":261868,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":262696,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":272423,"get_count":90,"put_bytes":87,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":270334,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":261406,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":220833,"get_count":75,"put_bytes":87,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":237960,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":251797,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":247447,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":278770,"get_count":93,"put_bytes":87,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":246382,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":246300,"get_count":82,"put_bytes":87,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":253088,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":252908,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":259910,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":267248,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":259625,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":272858,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":255094,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":281548,"get_count":94,"put_bytes":87,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":255832,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":268789,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":264134,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":245157,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":257535,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":246060,"get_count":84,"put_bytes":87,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":251084,"get_count":86,"put_bytes":87,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":259741,"get_count":87,"put_bytes":87,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":256740,"get_count":85,"put_bytes":87,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":272969,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":263014,"get_count":89,"put_bytes":87,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":257747,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":273134,"get_count":92,"put_bytes":87,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":266752,"get_count":88,"put_bytes":87,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":254375,"get_count":83,"put_bytes":87,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":271999,"get_count":91,"put_bytes":87,"put_count":1}} diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 50440fccb..d12105b72 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_hamt = "0.5.1" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" @@ -24,7 +24,7 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] anyhow = "1.0.56" log = "0.4.14" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 9f4d63a39..39af159b7 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -15,9 +15,9 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_ipld_hamt = "0.5.1" -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_bitfield = "0.5.2" +fvm_ipld_hamt = "0.6.0" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_bitfield = "0.5.4" num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -25,7 +25,7 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] log = "0.4.14" anyhow = "1.0.56" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" libipld-core = { version = "0.13.1", features = ["serde-codec"] } [dev-dependencies] @@ -33,7 +33,7 @@ fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector fil_actor_power = { path = "../power" } fil_actor_reward = { path = "../reward" } fil_actor_verifreg = { path = "../verifreg" } -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } +fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } multihash = { version = "0.16.1", default-features = false } regex = "1" itertools = "0.10" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 2b028f680..142658f88 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,10 +15,10 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_bitfield = "0.5.2" -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_ipld_hamt = "0.5.1" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_bitfield = "0.5.4" +fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } +fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } num-traits = "0.2.14" @@ -29,7 +29,7 @@ byteorder = "1.4.3" anyhow = "1.0.56" itertools = "0.10.3" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 883ebbe9b..4c3437de1 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -16,7 +16,7 @@ use fvm_ipld_amt::Error as AmtError; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, CborStore}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor, CborStore}; use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::Address; @@ -1207,7 +1207,7 @@ pub struct AdvanceDeadlineResult { } /// Static information about miner -#[derive(Debug, PartialEq, 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 @@ -1227,7 +1227,7 @@ pub struct MinerInfo { pub pending_worker_key: Option, /// Libp2p identity that should be used when connecting to this miner - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub peer_id: Vec, /// Vector of byte arrays representing Libp2p multi-addresses used for establishing a connection with this miner. diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 4934320da..f4d92a56e 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -5,7 +5,7 @@ use cid::Cid; use fil_actors_runtime::DealWeight; use fvm_ipld_bitfield::UnvalidatedBitField; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::ChainEpoch; @@ -32,7 +32,7 @@ pub struct MinerConstructorParams { pub worker: Address, pub control_addresses: Vec
, pub window_post_proof_type: RegisteredPoStProof, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub peer_id: Vec, pub multi_addresses: Vec, } @@ -63,7 +63,7 @@ pub struct ChangeWorkerAddressParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ChangePeerIDParams { - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub new_id: Vec, } @@ -83,7 +83,7 @@ pub struct ConfirmSectorProofsParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct DeferredCronEventParams { - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub event_payload: Vec, pub reward_smoothed: FilterEstimate, pub quality_adj_power_smoothed: FilterEstimate, @@ -118,7 +118,7 @@ impl Cbor for SubmitWindowedPoStParams {} #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ProveCommitSectorParams { pub sector_number: SectorNumber, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub proof: Vec, } @@ -215,11 +215,11 @@ pub struct CompactSectorNumbersParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ReportConsensusFaultParams { - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub header1: Vec, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub header2: Vec, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub header_extra: Vec, } @@ -344,7 +344,7 @@ impl Cbor for ProveCommitAggregateParams {} #[derive(Debug, Serialize_tuple, Deserialize_tuple)] pub struct ProveCommitAggregateParams { pub sector_numbers: UnvalidatedBitField, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub aggregate_proof: Vec, } @@ -356,7 +356,7 @@ pub struct ReplicaUpdate { pub new_sealed_cid: Cid, pub deals: Vec, pub update_proof_type: RegisteredUpdateProof, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub replica_proof: Vec, } diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index bc88cbe99..360cdaf09 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_hamt = "0.5.1" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } @@ -25,7 +25,7 @@ integer-encoding = { version = "3.0.3", default-features = false } serde = { version = "1.0.136", features = ["derive"] } anyhow = "1.0.56" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/multisig/src/types.rs b/actors/multisig/src/types.rs index 51ed78992..a961de9fa 100644 --- a/actors/multisig/src/types.rs +++ b/actors/multisig/src/types.rs @@ -4,7 +4,7 @@ use std::fmt::Display; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, Cbor, RawBytes}; +use fvm_ipld_encoding::{strict_bytes, Cbor, RawBytes}; use fvm_ipld_hamt::BytesKey; use fvm_shared::address::Address; @@ -105,7 +105,7 @@ pub struct TxnIDParams { pub id: TxnID, /// Optional hash of proposal to ensure an operation can only apply to a /// specific proposal. - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub proposal_hash: Vec, } diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index e5ed4e920..a7aa1a666 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,18 +15,18 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", 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.56" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } +fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } derive_builder = "0.10.2" [features] fil-actor = [] diff --git a/actors/paych/src/types.rs b/actors/paych/src/types.rs index f09d9e47e..918468eea 100644 --- a/actors/paych/src/types.rs +++ b/actors/paych/src/types.rs @@ -3,7 +3,7 @@ use fil_actors_runtime::network::EPOCHS_IN_HOUR; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, to_vec, Error, RawBytes}; +use fvm_ipld_encoding::{strict_bytes, to_vec, Error, RawBytes}; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; @@ -40,7 +40,7 @@ pub struct SignedVoucher { /// set to 0 means no timeout pub time_lock_max: ChainEpoch, /// (optional) Used by `to` to validate - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub secret_pre_image: Vec, /// (optional) Specified by `from` to add a verification method to the voucher pub extra: Option, @@ -68,7 +68,7 @@ impl SignedVoucher { pub channel_addr: &'a Address, pub time_lock_min: ChainEpoch, pub time_lock_max: ChainEpoch, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub secret_pre_image: &'a [u8], pub extra: &'a Option, pub lane: u64, @@ -108,14 +108,14 @@ pub struct ModVerifyParams { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct PaymentVerifyParams { pub extra: RawBytes, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub proof: Vec, } #[derive(Serialize_tuple, Deserialize_tuple)] pub struct UpdateChannelStateParams { pub sv: SignedVoucher, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub secret: Vec, // * proof removed in v2 } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 5dd3a0f55..5cd684f63 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_hamt = "0.5.1" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" @@ -27,7 +27,7 @@ lazy_static = "1.4.0" serde = { version = "1.0.136", features = ["derive"] } anyhow = "1.0.56" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/power/src/ext.rs b/actors/power/src/ext.rs index 91ab19eca..f650ba279 100644 --- a/actors/power/src/ext.rs +++ b/actors/power/src/ext.rs @@ -1,6 +1,6 @@ use cid::Cid; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, BytesDe, RawBytes}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, RawBytes}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser; use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, StoragePower}; @@ -51,14 +51,14 @@ pub mod miner { pub worker: Address, pub control_addresses: Vec
, pub window_post_proof_type: RegisteredPoStProof, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub peer_id: Vec, pub multi_addresses: Vec, } #[derive(Serialize_tuple, Deserialize_tuple)] pub struct DeferredCronEventParams { - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub event_payload: Vec, pub reward_smoothed: FilterEstimate, pub quality_adj_power_smoothed: FilterEstimate, diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 521be8f83..74537f7ad 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, RawBytes}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor, RawBytes}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser; use fvm_shared::clock::ChainEpoch; @@ -28,7 +28,7 @@ pub struct CreateMinerParams { pub owner: Address, pub worker: Address, pub window_post_proof_type: RegisteredPoStProof, - #[serde(with = "serde_bytes")] + #[serde(with = "strict_bytes")] pub peer: Vec, pub multiaddrs: Vec, } diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index d89cd853a..0ea16cbc8 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", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", 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.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 47680d608..9bfc688be 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_encoding = "0.2.2" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" anyhow = "1.0.56" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index b9ae682f3..13f29fbbf 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,16 +15,16 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } 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"] } lazy_static = "1.4.0" anyhow = "1.0.56" -fvm_ipld_hamt = "0.5.1" +fvm_ipld_hamt = "0.6.0" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1fc704160..72a889c0b 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] -fvm_ipld_hamt = "0.5.1" -fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } +fvm_ipld_hamt = "0.6.0" +fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -22,7 +22,7 @@ log = "0.4.14" thiserror = "1.0.30" anyhow = "1.0.56" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.2.2" +fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } serde_repr = "0.1.8" regex = "1" @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.4", optional = true } +fvm_sdk = { version = "3.0.0-alpha.5", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index a8e533114..6f033e13e 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,8 +26,8 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_encoding = "0.2.2" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" anyhow = "1.0.56" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 602ca0eda..089742604 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,11 +26,11 @@ 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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.4", default-features = false } -fvm_ipld_encoding = { version = "0.2.2", default-features = false } +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } -fvm_ipld_bitfield = "0.5.2" -fvm_ipld_hamt = "0.5.1" +fvm_ipld_bitfield = "0.5.4" +fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" From 8c85c8e47ad2ad1036b4e5f31164cf8ca45e1bc6 Mon Sep 17 00:00:00 2001 From: mriise Date: Fri, 14 Oct 2022 16:41:57 -0700 Subject: [PATCH 076/339] EVM: Implement the EAM (#695) Co-authored-by: Steven Allen --- Cargo.lock | 33 +- actors/eam/Cargo.toml | 37 ++ actors/eam/src/ext.rs | 38 ++ actors/eam/src/lib.rs | 270 ++++++++++++ actors/eam/tests/create.rs | 160 +++++++ actors/evm/src/interpreter/address.rs | 34 +- actors/evm/src/interpreter/execution.rs | 14 +- .../src/interpreter/instructions/context.rs | 5 +- .../src/interpreter/instructions/lifecycle.rs | 150 ++++++- actors/evm/src/interpreter/opcode.rs | 1 + actors/evm/src/interpreter/system.rs | 42 ++ actors/evm/src/lib.rs | 91 +++- actors/evm/src/state.rs | 5 +- actors/evm/tests/calc.rs | 2 +- actors/evm/tests/create.rs | 187 ++++++++ actors/evm/tests/env.rs | 24 +- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../measurements/array_read_n100.jsonline | 200 ++++----- .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../measurements/inc_one_vs_all.jsonline | 40 +- .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../measurements/mapping_read_n100.jsonline | 200 ++++----- actors/evm/tests/util.rs | 21 +- runtime/src/runtime/fvm.rs | 35 +- runtime/src/runtime/mod.rs | 15 + runtime/src/test_utils.rs | 81 +++- test_vm/src/lib.rs | 51 +++ 32 files changed, 2867 insertions(+), 1669 deletions(-) create mode 100644 actors/eam/Cargo.toml create mode 100644 actors/eam/src/ext.rs create mode 100644 actors/eam/src/lib.rs create mode 100644 actors/eam/tests/create.rs create mode 100644 actors/evm/tests/create.rs diff --git a/Cargo.lock b/Cargo.lock index 5b9ab24b4..da4fd8e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1363,6 +1363,27 @@ dependencies = [ "serde", ] +[[package]] +name = "fil_actor_eam" +version = "10.0.0-alpha.1" +dependencies = [ + "anyhow", + "cid", + "fil_actor_evm", + "fil_actors_runtime", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "hex-literal", + "log", + "multihash", + "num-derive", + "num-traits", + "rlp", + "serde", + "serde_tuple", +] + [[package]] name = "fil_actor_embryo" version = "10.0.0-alpha.1" @@ -2333,9 +2354,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.135" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libipld-core" @@ -3525,9 +3546,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ "proc-macro2", "quote", @@ -3790,9 +3811,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-normalization" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml new file mode 100644 index 000000000..e2dc4c23e --- /dev/null +++ b/actors/eam/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "fil_actor_eam" +description = "Builtin Ethereum address manager actor for Filecoin" +version = "10.0.0-alpha.1" +license = "MIT OR Apache-2.0" +authors = ["Protocol Labs", "Filecoin Core Devs"] +edition = "2021" +repository = "https://github.com/filecoin-project/builtin-actors" +keywords = ["filecoin", "web3", "wasm", "evm"] + +[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" } +serde = { version = "1.0.136", features = ["derive"] } +serde_tuple = "0.5" +rlp = { version = "0.5.1", default-features = false } +anyhow = "1.0.56" +log = "0.4.14" +fvm_ipld_blockstore = "0.1.1" +fvm_ipld_encoding = "0.3.0" +multihash = { version = "0.16.1", default-features = false } +cid = "0.8.6" +fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +num-traits = "0.2.15" +num-derive = "0.3.3" +hex-literal = "0.3.4" + +[dev-dependencies] +fil_actor_evm = { version = "10.0.0-alpha.1", path = "../evm"} +fil_actors_runtime = { path = "../../runtime", features = ["test_utils"] } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/eam/src/ext.rs b/actors/eam/src/ext.rs new file mode 100644 index 000000000..0650f8695 --- /dev/null +++ b/actors/eam/src/ext.rs @@ -0,0 +1,38 @@ +use cid::Cid; +use fvm_ipld_encoding::tuple::*; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; + +pub mod init { + + use super::*; + + pub const EXEC4_METHOD: u64 = 3; + + /// Init actor Exec4 Params + #[derive(Serialize_tuple, Deserialize_tuple, Debug)] + pub struct Exec4Params { + pub code_cid: Cid, + pub constructor_params: RawBytes, + pub subaddress: RawBytes, + } + + /// Init actor Exec4 Return value + #[derive(Serialize_tuple, Deserialize_tuple, Debug)] + pub struct Exec4Return { + /// ID based address for created actor + pub id_address: Address, + /// Reorg safe address for actor + pub robust_address: Address, + } +} + +pub mod evm { + use super::*; + + #[derive(Serialize_tuple, Deserialize_tuple)] + pub struct ConstructorParams { + pub bytecode: RawBytes, + pub input_data: RawBytes, + } +} diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs new file mode 100644 index 000000000..8601a9605 --- /dev/null +++ b/actors/eam/src/lib.rs @@ -0,0 +1,270 @@ +use std::iter; + +use ext::init::{Exec4Params, Exec4Return}; +use rlp::Encodable; + +pub mod ext; + +use { + fil_actors_runtime::{ + actor_error, cbor, + runtime::builtins::Type, + runtime::{ActorCode, Runtime}, + ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, + }, + fvm_ipld_blockstore::Blockstore, + fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}, + fvm_shared::{ + address::{Address, Payload, SECP_PUB_LEN}, + crypto::hash::SupportedHashes, + ActorID, MethodNum, METHOD_CONSTRUCTOR, + }, + num_derive::FromPrimitive, + num_traits::FromPrimitive, +}; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(EamActor); + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + Create = 2, + Create2 = 3, + // CreateAccount = 4, +} + +#[derive(Debug)] +/// Intermediate type between RLP encoding for CREATE +pub struct RlpCreateAddress { + pub address: EthAddress, + pub nonce: u64, +} + +impl rlp::Encodable for RlpCreateAddress { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + s.encoder().encode_value(&self.address.0); + s.append(&self.nonce); + } +} + +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, PartialEq, Eq)] +pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct CreateParams { + #[serde(with = "strict_bytes")] + pub initcode: Vec, + pub nonce: u64, +} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct Create2Params { + #[serde(with = "strict_bytes")] + pub initcode: Vec, + #[serde(with = "strict_bytes")] + pub salt: [u8; 32], +} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct InitAccountParams { + #[serde(with = "strict_bytes")] + pub pubkey: [u8; SECP_PUB_LEN], +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq)] +pub struct Return { + pub actor_id: ActorID, + pub robust_address: Address, + pub eth_address: EthAddress, +} +pub type CreateReturn = Return; +pub type Create2Return = Return; + +impl Return { + fn from_exec4(exec4: Exec4Return, eth_address: EthAddress) -> Self { + Self { + actor_id: exec4.id_address.id().unwrap(), + robust_address: exec4.robust_address, + eth_address, + } + } +} + +#[derive(Serialize_tuple, Deserialize_tuple, Clone)] +pub struct EvmConstructorParams { + /// The actor's "creator" (specified by the EAM). + pub creator: EthAddress, + /// The initcode that will construct the new EVM actor. + pub initcode: RawBytes, +} + +/// hash of data with Keccack256, with first 12 bytes cropped +fn hash_20(rt: &RT, data: &[u8]) -> [u8; 20] +where + BS: Blockstore + Clone, + RT: Runtime, +{ + rt.hash(SupportedHashes::Keccak256, data)[12..32].try_into().unwrap() +} + +fn create_actor( + rt: &mut RT, + creator: EthAddress, + new_addr: EthAddress, + initcode: Vec, +) -> Result +where + BS: Blockstore + Clone, + RT: Runtime, +{ + let constructor_params = + RawBytes::serialize(EvmConstructorParams { creator, initcode: initcode.into() })?; + + let init_params = Exec4Params { + code_cid: rt.get_code_cid_for_type(Type::EVM), + constructor_params, + subaddress: new_addr.0.to_vec().into(), + }; + + let ret: ext::init::Exec4Return = rt + .send( + &INIT_ACTOR_ADDR, + ext::init::EXEC4_METHOD, + RawBytes::serialize(&init_params)?, + rt.message().value_received(), + )? + .deserialize()?; + + Ok(Return::from_exec4(ret, new_addr)) +} + +/// lookup caller's raw ETH address +fn get_caller_address(rt: &RT) -> Result +where + BS: Blockstore + Clone, + RT: Runtime, +{ + let caller_id = rt.message().caller().id().unwrap(); + + let addr = rt.lookup_address(caller_id).unwrap(); + match addr.payload() { + Payload::Delegated(eth) => Ok(EthAddress(eth.subaddress().try_into().unwrap())), + _ => unreachable!(), + } +} + +pub struct EamActor; +impl EamActor { + pub fn constructor(rt: &mut RT) -> Result<(), ActorError> + where + BS: Blockstore + Clone, + RT: Runtime, + { + let actor_id = rt.resolve_address(&rt.message().receiver()).unwrap(); + if actor_id != EAM_ACTOR_ID { + return Err(ActorError::forbidden(format!( + "The Ethereum Address Manager must be deployed at {EAM_ACTOR_ID}, was deployed at {actor_id}" + ))); + } + rt.validate_immediate_caller_type(std::iter::once(&Type::Init)) + } + + pub fn create(rt: &mut RT, params: CreateParams) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + rt.validate_immediate_caller_type(iter::once(&Type::EVM))?; + + let caller_addr = get_caller_address(rt)?; + // CREATE logic + let rlp = RlpCreateAddress { address: caller_addr, nonce: params.nonce }; + let eth_addr = EthAddress(hash_20(rt, &rlp.rlp_bytes())); + + // send to init actor + create_actor(rt, caller_addr, eth_addr, params.initcode) + } + + pub fn create2(rt: &mut RT, params: Create2Params) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + rt.validate_immediate_caller_type(iter::once(&Type::EVM))?; + + // CREATE2 logic + let inithash = rt.hash(SupportedHashes::Keccak256, ¶ms.initcode); + + let caller_addr = get_caller_address(rt)?; + + let eth_addr = EthAddress(hash_20( + rt, + &[&[0xff], &caller_addr.0[..], ¶ms.salt, &inithash].concat(), + )); + + // send to init actor + create_actor(rt, caller_addr, eth_addr, params.initcode) + } + + pub fn create_account( + rt: &mut RT, + params: InitAccountParams, + ) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + // First, validate that we're receiving this message from the filecoin account that maps to + // this ethereum account. + // + // We don't need to validate that the _key_ is well formed or anything, because the fact + // that we're receiving a message from the account proves that to be the case anyways. + // + // TODO: allow off-chain deployment! + let key_addr = Address::new_secp256k1(¶ms.pubkey) + .map_err(|e| ActorError::illegal_argument(format!("not a valid public key: {e}")))?; + + rt.validate_immediate_caller_is(iter::once(&key_addr))?; + + // Compute the equivalent eth address + let eth_address = EthAddress(hash_20(rt, ¶ms.pubkey[1..])); + + // TODO: Check reserved ranges (id, precompile, etc.). + + // Attempt to deploy an account there. + // TODO + create_actor(rt, EthAddress([0u8; 20]), eth_address, Vec::new()).ok(); + todo!() + } +} + +impl ActorCode for EamActor { + fn invoke_method( + rt: &mut RT, + method: MethodNum, + params: &RawBytes, + ) -> Result + where + BS: Blockstore + Clone, + RT: Runtime, + { + match FromPrimitive::from_u64(method) { + Some(Method::Constructor) => { + Self::constructor(rt)?; + Ok(RawBytes::default()) + } + Some(Method::Create) => { + Ok(RawBytes::serialize(Self::create(rt, cbor::deserialize_params(params)?)?)?) + } + Some(Method::Create2) => { + Ok(RawBytes::serialize(Self::create2(rt, cbor::deserialize_params(params)?)?)?) + } + // Some(Method::CreateAccount) => { + // Self::create_account(rt, cbor::deserialize_params(params)?) + // } + None => Err(actor_error!(unhandled_message; "Invalid method")), + } + } +} diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs new file mode 100644 index 000000000..98460745b --- /dev/null +++ b/actors/eam/tests/create.rs @@ -0,0 +1,160 @@ +use eam::ext::init::{Exec4Params, Exec4Return, EXEC4_METHOD}; +use eam::{ + Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, RlpCreateAddress, +}; +use fil_actor_eam as eam; +use fil_actors_runtime::runtime::builtins::Type; +use fil_actors_runtime::runtime::Primitives; +use fil_actors_runtime::test_utils::{ + expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, +}; +use fil_actors_runtime::{EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use rlp::Encodable; + +#[test] +fn call_create() { + let mut rt = construct_and_verify(); + + let id_addr = Address::new_id(110); + let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); + rt.add_delegated_address(id_addr, f4_eth_addr); + + rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); + rt.expect_validate_caller_type(vec![Type::EVM]); + + let initcode = vec![0xff]; + + let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; + + let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; + + let rlp_params = RlpCreateAddress { address: eth_addr, nonce: 0 }; + let mut subaddress = + rt.hash(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &rlp_params.rlp_bytes()); + subaddress.drain(..12); + + let params = Exec4Params { + code_cid: *EVM_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(evm_params).unwrap(), + subaddress: subaddress.clone().into(), + }; + + let send_return = RawBytes::serialize(Exec4Return { + id_address: Address::new_id(111), + robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks + }) + .unwrap(); + + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC4_METHOD, + RawBytes::serialize(params).unwrap(), + TokenAmount::from_atto(0), + send_return, + ExitCode::OK, + ); + + let result = rt + .call::( + eam::Method::Create as u64, + &RawBytes::serialize(create_params).unwrap(), + ) + .unwrap() + .deserialize::() + .unwrap(); + + let expected_return = Return { + actor_id: 111, + robust_address: Address::new_id(0), + eth_address: EthAddress(subaddress.try_into().unwrap()), + }; + + assert_eq!(result, expected_return) +} + +#[test] +fn call_create2() { + let mut rt = construct_and_verify(); + + let id_addr = Address::new_id(110); + let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); + rt.add_delegated_address(id_addr, f4_eth_addr); + + rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); + rt.expect_validate_caller_type(vec![Type::EVM]); + rt.expect_validate_caller_namespace(vec![EAM_ACTOR_ID]); + + let initcode = vec![0xff]; + + let create2_params = Create2Params { initcode: initcode.clone(), salt: [0; 32] }; + + let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.clone().into() }; + + let inithash = rt.hash(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &initcode); + let mut subaddress = rt.hash( + fvm_shared::crypto::hash::SupportedHashes::Keccak256, + &[&[0xff], ð_addr.0[..], &create2_params.salt, &inithash].concat(), + ); + subaddress.drain(..12); + + let params = Exec4Params { + code_cid: *EVM_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(evm_params).unwrap(), + subaddress: subaddress.clone().into(), + }; + + let send_return = RawBytes::serialize(Exec4Return { + id_address: Address::new_id(111), + robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks + }) + .unwrap(); + + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC4_METHOD, + RawBytes::serialize(params).unwrap(), + TokenAmount::from_atto(0), + send_return, + ExitCode::OK, + ); + + let result = rt + .call::( + eam::Method::Create2 as u64, + &RawBytes::serialize(create2_params).unwrap(), + ) + .unwrap() + .deserialize::() + .unwrap(); + + let expected_return = Return { + actor_id: 111, + robust_address: Address::new_id(0), + eth_address: EthAddress(subaddress.try_into().unwrap()), + }; + + assert_eq!(result, expected_return) +} + +pub fn construct_and_verify() -> MockRuntime { + let mut rt = MockRuntime { receiver: Address::new_id(10), ..Default::default() }; + + // construct EAM singleton actor + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + + rt.expect_validate_caller_type(vec![Type::Init]); + + let result = + rt.call::(eam::Method::Constructor as u64, &RawBytes::default()).unwrap(); + expect_empty(result); + rt.verify(); + rt.reset(); + + rt +} diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index 3a10081e0..c60865159 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -1,13 +1,15 @@ use crate::StatusCode; use crate::U256; -use fvm_shared::address::Address as FilecoinAddress; +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). /// /// TODO this type will eventually handle f4 address detection. -#[derive(PartialEq, Eq, Clone)] -pub struct EthAddress([u8; 20]); +#[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone, Copy)] +pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); impl TryFrom for EthAddress { type Error = StatusCode; @@ -31,9 +33,29 @@ impl std::fmt::Debug for EthAddress { } } +impl TryFrom<&EthAddress> for Address { + type Error = anyhow::Error; + fn try_from(addr: &EthAddress) -> Result { + if addr.0[..19] == [0; 19] { + return Err(anyhow::anyhow!( + "Cannot convert a precompile address {:X?} to an f4 address.", + addr + )); + } + + let f4_addr = if let Some(addr) = addr.as_id_address() { + addr + } else { + Address::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap() + }; + + Ok(f4_addr) + } +} + impl EthAddress { /// Expect a Filecoin address type containing an ID address, and return an address in EVM-form. - pub fn from_id_address(addr: &FilecoinAddress) -> Option { + pub fn from_id_address(addr: &Address) -> Option { addr.id().ok().map(EthAddress::from_id) } @@ -53,11 +75,11 @@ impl EthAddress { /// /// 0 1-11 12 /// 0xff \[0x00...] [id address...] - pub fn as_id_address(&self) -> Option { + pub fn as_id_address(&self) -> Option
{ if (self.0[0] != 0xff) || !self.0[1..12].iter().all(|&byte| byte == 0) { return None; } - Some(FilecoinAddress::new_id(u64::from_be_bytes(self.0[12..].try_into().unwrap()))) + Some(Address::new_id(u64::from_be_bytes(self.0[12..].try_into().unwrap()))) } /// Same as as_id_address, but go the extra mile and return the ActorID. diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 22d2ec474..ee73251ed 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] use fvm_shared::address::Address as FilecoinAddress; + +use super::address::EthAddress; use { super::instructions::*, super::opcode::OpCode, @@ -27,10 +29,14 @@ pub struct ExecutionState { pub output_data: Bytes, /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. pub selfdestroyed: Option, + /// The EVM address of the caller. + pub caller: EthAddress, + /// The EVM address of the receiver. + pub receiver: EthAddress, } impl ExecutionState { - pub fn new(method: u64, input_data: Bytes) -> Self { + pub fn new(caller: EthAddress, receiver: EthAddress, method: u64, input_data: Bytes) -> Self { Self { stack: Stack::default(), memory: Memory::default(), @@ -39,6 +45,8 @@ impl ExecutionState { return_data: Default::default(), output_data: Bytes::new(), selfdestroyed: None, + caller, + receiver, } } } @@ -803,7 +811,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } CREATE(m) { - lifecycle::create(m.runtime, m.system, false)?; + lifecycle::create(m.runtime, m.system)?; Ok(ControlFlow::Continue) } @@ -828,7 +836,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } CREATE2(m) { - lifecycle::create(m.runtime, m.system, true)?; + lifecycle::create2(m.runtime, m.system)?; Ok(ControlFlow::Continue) } diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 17d820979..5f1dee2b7 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -31,10 +31,9 @@ pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn caller<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + _: &'r System<'r, BS, RT>, ) { - let id = platform.rt.message().caller().id().unwrap(); - state.stack.push(EthAddress::from_id(id).as_evm_word()) + state.stack.push(state.caller.as_evm_word()) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index e61dc9cc2..136a31f07 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,17 +1,157 @@ -use crate::interpreter::address::EthAddress; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::EAM_ACTOR_ADDR; +use fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}; +use fvm_shared::MethodNum; +use fvm_shared::{address::Address, econ::TokenAmount}; +use serde_tuple::{Deserialize_tuple, Serialize_tuple}; + +use crate::interpreter::stack::Stack; +use crate::interpreter::{address::EthAddress, U256}; +use crate::state::State; + +use super::memory::{get_memory_region, MemoryRegion}; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, }; +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, + pub salt: [u8; 32], +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Copy, PartialEq, Eq)] +pub struct EamReturn { + pub actor_id: u64, + pub robust_address: Address, + pub eth_address: EthAddress, +} + #[inline] pub fn create<'r, BS: Blockstore, RT: Runtime>( - _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, - _create2: bool, + state: &mut ExecutionState, + platform: &'r mut System<'r, BS, RT>, +) -> Result<(), StatusCode> { + let ExecutionState { stack, memory, .. } = state; + + let value = stack.pop(); + let (offset, size) = (stack.pop(), stack.pop()); + + let value = TokenAmount::from(&value); + let input_region = + get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + return Err(StatusCode::ActorError(ActorError::assertion_failed( + "inicode not in memory range".to_string(), + ))); + }; + + // bump nonce and flush state before send + let nonce = platform.rt.transaction(|state: &mut State, _rt| { + let nonce = state.nonce; + // this may be redundant if we are compiling with checked integer math + state.nonce = state.nonce.checked_add(1).unwrap(); + Ok(nonce) + })?; + + let params = CreateParams { code: input_data.to_vec(), nonce }; + + create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) +} + +pub fn create2<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + platform: &'r mut System<'r, BS, RT>, +) -> Result<(), StatusCode> { + let ExecutionState { stack, memory, .. } = state; + + // see `create()` overall TODOs + + let endowment = stack.pop(); + let (offset, size) = (stack.pop(), stack.pop()); + let salt = stack.pop(); + + let endowment = TokenAmount::from(&endowment); + let input_region = + get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + + // 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 { + return Err(StatusCode::ActorError(ActorError::illegal_argument( + "initcode not in memory range".to_string(), + ))); + }; + let params = Create2Params { code: input_data.to_vec(), salt }; + + platform.rt.transaction(|state: &mut State, _rt| { + // this may be redundant if we are compiling with checked integer math + state.nonce = state.nonce.checked_add(1).unwrap(); + Ok(()) + })?; + + create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) +} + +/// call into Ethereum Address Manager to make the new account +fn create_init<'r, BS: Blockstore, RT: Runtime>( + stack: &mut Stack, + platform: &'r mut System<'r, BS, RT>, + params: RawBytes, + method: MethodNum, + value: TokenAmount, ) -> Result<(), StatusCode> { - todo!() + // send bytecode & params to EAM to generate the address and contract + let ret = platform.rt.send(&EAM_ACTOR_ADDR, method, params, value); + + // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 + // Normally EVM will do some checks here to ensure that a contract has the capability + // to create an actor, but here FVM does these checks for us, including: + // - execution depth, equal to FVM's max call depth (FVM) + // - account has enough value to send (FVM) + // - ensuring there isn't an existing account at the generated f4 address (INIT) + // - constructing smart contract on chain (INIT) + // - checks if max code size is exceeded (EAM & EVM) + // - gas cost of deployment (FVM) + // - EIP-3541 (EVM) + // + // However these errors are flattened to a 0 pushed on the stack. + + // TODO revert state if error was returned (revert nonce bump) + // https://github.com/filecoin-project/ref-fvm/issues/956 + + // TODO Exit with revert if sys out of gas when subcall gas limits are introduced + // https://github.com/filecoin-project/ref-fvm/issues/966 + + let word = match ret { + Ok(eam_ret) => { + let ret: EamReturn = eam_ret.deserialize()?; + ret.eth_address.as_evm_word() + } + Err(_) => U256::zero(), + }; + + stack.push(word); + Ok(()) } #[inline] diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs index d687d80cf..734a6d736 100644 --- a/actors/evm/src/interpreter/opcode.rs +++ b/actors/evm/src/interpreter/opcode.rs @@ -219,6 +219,7 @@ def_opcodes! { 0xb0: CALLACTOR(6, -5), 0xb1: METHODNUM(0, 1), ////////////////////////////////////////////////////////// + // 0xEF Reserved for EIP-3541 0xf0: CREATE(3, -2), 0xf1: CALL(7, -6), 0xf2: CALLCODE(7, -6), diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 2ec74fa3e..0eaea7052 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] +use fil_actors_runtime::EAM_ACTOR_ID; +use fvm_shared::address::{Address, Payload}; + +use super::address::EthAddress; + use { crate::interpreter::{StatusCode, U256}, cid::Cid, @@ -78,4 +83,41 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { Ok(storage_status) } + + /// Resolve the address to the ethereum equivalent, if possible. + 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().map_err(|_| { + StatusCode::BadAddress("invalid ethereum address length".into()) + })?; + return Ok(EthAddress(subaddr)); + } + _ => {} + } + + // Otherwise, resolve to an ID address. + let actor_id = self.rt.resolve_address(addr).ok_or_else(|| { + StatusCode::BadAddress(format!( + "non-ethereum address {addr} cannot be resolved to an ID address" + )) + })?; + + // Then attempt to resolve back into an EVM address. + // + // TODO: this method doesn't differentiate between "actor doesn't have a predictable + // address" and "actor doesn't exist". We should probably fix that and return an error if + // the actor doesn't exist. + match self.rt.lookup_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().map_err(|_| { + StatusCode::BadAddress("invalid ethereum address length".into()) + })?; + Ok(EthAddress(subaddr)) + } + // But use an EVM address as the fallback. + _ => Ok(EthAddress::from_id(actor_id)), + } + } } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 95bfe3f1b..b4dede83b 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,3 +1,9 @@ +use std::iter; + +use fil_actors_runtime::{runtime::builtins::Type, EAM_ACTOR_ID}; +use fvm_shared::address::{Address, Payload}; +use interpreter::address::EthAddress; + pub mod interpreter; mod state; @@ -46,17 +52,48 @@ impl EvmContractActor { BS: Blockstore + Clone, RT: Runtime, { - rt.validate_immediate_caller_accept_any()?; + // TODO ideally we would be checking that we are constructed by the EAM actor, + // but instead we check for init and then assert that we have a delegated address. + // https://github.com/filecoin-project/ref-fvm/issues/746 + // rt.validate_immediate_caller_is(vec![&EAM_ACTOR_ADDR])?; + rt.validate_immediate_caller_type(iter::once(&Type::Init))?; + + // Assert we are constructed with a delegated address from the EAM + let receiver = rt.message().receiver(); + let delegated_addr = rt.lookup_address(receiver.id().unwrap()).ok_or_else(|| { + ActorError::assertion_failed(format!( + "EVM actor {} created without a delegated address", + receiver + )) + })?; + let delegated_addr = match delegated_addr.payload() { + Payload::Delegated(delegated) if delegated.namespace() == EAM_ACTOR_ID => { + // sanity check + assert_eq!(delegated.subaddress().len(), 20); + Ok(*delegated) + } + _ => Err(ActorError::assertion_failed(format!( + "EVM actor with delegated address {} created not namespaced to the EAM {}", + delegated_addr, EAM_ACTOR_ID, + ))), + }?; + let receiver_eth_addr = { + let subaddr: [u8; 20] = delegated_addr.subaddress().try_into().map_err(|_| { + ActorError::assertion_failed(format!( + "expected 20 byte EVM address, found {} bytes", + delegated_addr.subaddress().len() + )) + })?; + EthAddress(subaddr) + }; - if params.bytecode.len() > MAX_CODE_SIZE { + if params.initcode.len() > MAX_CODE_SIZE { return Err(ActorError::illegal_argument(format!( "EVM byte code length ({}) is exceeding the maximum allowed of {MAX_CODE_SIZE}", - params.bytecode.len() + params.initcode.len() ))); - } - - if params.bytecode.is_empty() { - return Err(ActorError::illegal_argument("no bytecode provided".into())); + } else if params.initcode.is_empty() { + return Err(ActorError::illegal_argument("no initcode provided".into())); } // create an empty storage HAMT to pass it down for execution. @@ -68,15 +105,20 @@ impl EvmContractActor { })?; // create a new execution context - let mut exec_state = ExecutionState::new(Method::Constructor as u64, Bytes::new()); + let mut exec_state = ExecutionState::new( + params.creator, + receiver_eth_addr, + Method::Constructor as u64, + Bytes::new(), + ); // identify bytecode valid jump destinations - let bytecode = Bytecode::new(¶ms.bytecode) + let initcode = Bytecode::new(¶ms.initcode) .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; // invoke the contract constructor let exec_status = - execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { + execute(&initcode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { StatusCode::ActorError(e) => e, _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; @@ -94,6 +136,13 @@ impl EvmContractActor { // the resulting bytecode. let contract_bytecode = exec_status.output_data; + // Reject code starting with 0xEF, EIP-3541 + if !contract_bytecode.is_empty() && contract_bytecode[0] == 0xEF { + return Err(ActorError::illegal_argument( + "EIP-3541: Contract code starting with the 0xEF byte is disallowed.".into(), + )); + } + let contract_state_cid = system.flush_state()?; let state = State::new( @@ -147,7 +196,20 @@ impl EvmContractActor { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; - let mut exec_state = ExecutionState::new(method, input_data.to_vec().into()); + // Resolve the caller's ethereum address. If the caller doesn't have one, the caller's ID is used instead. + let caller_fil_addr = system.rt.message().caller(); + let caller_eth_addr = system.resolve_ethereum_address(&caller_fil_addr).unwrap(); + + // 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_eth_addr, + receiver_eth_addr, + method, + input_data.to_vec().into(), + ); let exec_status = execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { @@ -205,7 +267,7 @@ impl EvmContractActor { { // 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([&fvm_shared::address::Address::new_id(0)])?; + rt.validate_immediate_caller_is([&Address::new_id(0)])?; let state: State = rt.state()?; let blockstore = rt.store().clone(); @@ -262,7 +324,10 @@ impl ActorCode for EvmContractActor { #[derive(Serialize_tuple, Deserialize_tuple)] pub struct ConstructorParams { - pub bytecode: RawBytes, + /// The actor's "creator" (specified by the EAM). + pub creator: EthAddress, + /// The initcode that will construct the new EVM actor. + pub initcode: RawBytes, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 992bc81ad..10c2ab131 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -23,6 +23,9 @@ pub struct State { /// /// HAMT pub contract_state: Cid, + + /// The EVM nonce used to track how many times CREATE or CREATE2 have been called. + pub nonce: u64, } impl Cbor for State {} @@ -34,6 +37,6 @@ impl State { contract_state: Cid, ) -> anyhow::Result { let bytecode_cid = store.put(Code::Blake2b256, &Block::new(RAW, bytecode.to_vec()))?; - Ok(Self { bytecode: bytecode_cid, contract_state }) + Ok(Self { bytecode: bytecode_cid, contract_state, nonce: 0 }) } } diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index 8741e9503..a509b3a72 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -51,7 +51,7 @@ return mul_magic: jumpdest -push1 0x20 # length of return dataa +push1 0x20 # length of return data push1 0x04 calldataload # arg1 push1 0x00 # key of magic diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs new file mode 100644 index 000000000..44c5f9311 --- /dev/null +++ b/actors/evm/tests/create.rs @@ -0,0 +1,187 @@ +mod asm; + +use evm::interpreter::address::EthAddress; +use evm::interpreter::instructions::lifecycle::{ + Create2Params, CreateParams, EamReturn, CREATE2_METHOD_NUM, CREATE_METHOD_NUM, +}; +use fil_actor_evm as evm; +use fil_actors_runtime::EAM_ACTOR_ADDR; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; + +mod util; + +#[allow(dead_code)] +pub fn magic_precompile_contract() -> Vec { + let init = ""; + let body = r#" + + +%macro return_stack_word() + push1 0x00 + mstore + push1 0x20 # always return a full word + push1 0x00 + return +%end + +# 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() { + 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 = EamReturn { + actor_id: 12345, + eth_address: fake_eth_addr, + robust_address: (&fake_eth_addr).try_into().unwrap(), + }; + + let salt = + hex_literal::hex!("0000000000000000000000000000000000000000000000796573206D616E6921"); + + let create2_params = Create2Params { + code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), + salt, + }; + + let mut create_params = CreateParams { + code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), + nonce: 0, + }; + + // byte 3 is method num + let mut contract_params = [0u8; 32]; + + // invoke contract -- create + { + rt.add_balance(TokenAmount::from_atto(1)); + + rt.expect_send( + EAM_ACTOR_ADDR, + CREATE_METHOD_NUM, + RawBytes::serialize(create_params.clone()).unwrap(), + TokenAmount::from_atto(1), + RawBytes::serialize(fake_ret).unwrap(), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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_send( + EAM_ACTOR_ADDR, + CREATE_METHOD_NUM, + RawBytes::serialize(create_params.clone()).unwrap(), + TokenAmount::from_atto(1), + RawBytes::serialize(fake_ret).unwrap(), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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_send( + EAM_ACTOR_ADDR, + CREATE2_METHOD_NUM, + RawBytes::serialize(create2_params.clone()).unwrap(), + TokenAmount::from_atto(1), + RawBytes::serialize(fake_ret).unwrap(), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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 + { + rt.expect_send( + EAM_ACTOR_ADDR, + CREATE2_METHOD_NUM, + RawBytes::serialize(create2_params).unwrap(), + TokenAmount::from_atto(1), + RawBytes::serialize(fake_ret).unwrap(), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + assert_eq!(&result[..], &[0; 32]); + } + + contract_params[3] = 0x00; + + // not enough funds -- create + { + // TODO this nonce is broken + create_params.nonce += 3; + rt.expect_send( + EAM_ACTOR_ADDR, + CREATE_METHOD_NUM, + RawBytes::serialize(create_params).unwrap(), + TokenAmount::from_atto(1), + RawBytes::serialize(fake_ret).unwrap(), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + assert_eq!(&result[..], &[0; 32]); + } +} diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs index 385f1f41a..664d40f94 100644 --- a/actors/evm/tests/env.rs +++ b/actors/evm/tests/env.rs @@ -3,8 +3,12 @@ use ethers::{ prelude::{builders::ContractCall, decode_function_data}, providers::{MockProvider, Provider}, }; +use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, EVM_ACTOR_CODE_ID}; +use fil_actors_runtime::{ + runtime::builtins::Type, + test_utils::{expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID}, +}; use fvm_ipld_blockstore::tracking::{BSStats, TrackingBlockstore}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; @@ -39,10 +43,24 @@ impl TestEnv { /// Deploy a contract into the EVM actor. pub fn deploy(&mut self, contract_hex: &str) { - let params = evm::ConstructorParams { bytecode: hex::decode(contract_hex).unwrap().into() }; + 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_any(); + self.runtime.expect_validate_caller_type(vec![Type::Init]); + self.runtime.caller_type = *INIT_ACTOR_CODE_ID; + self.runtime.set_origin(self.evm_address); + // first actor created is 0 + self.runtime.add_delegated_address( + Address::new_id(0), + Address::new_delegated( + 10, + &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"), + ) + .unwrap(), + ); let result = self .runtime diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 21a8dcc60..99d4334c9 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2286,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2283,"get_count":4,"put_bytes":196,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2291,"get_count":4,"put_bytes":204,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2299,"get_count":4,"put_bytes":213,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2308,"get_count":4,"put_bytes":221,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2316,"get_count":4,"put_bytes":229,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2324,"get_count":4,"put_bytes":237,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2332,"get_count":4,"put_bytes":245,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2340,"get_count":4,"put_bytes":333,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2428,"get_count":4,"put_bytes":341,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2436,"get_count":4,"put_bytes":349,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2444,"get_count":4,"put_bytes":358,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2453,"get_count":4,"put_bytes":366,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2461,"get_count":4,"put_bytes":374,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":382,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":391,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":2486,"get_count":4,"put_bytes":465,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":2560,"get_count":4,"put_bytes":473,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2568,"get_count":4,"put_bytes":481,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2576,"get_count":4,"put_bytes":490,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":2585,"get_count":4,"put_bytes":498,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2593,"get_count":4,"put_bytes":506,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2601,"get_count":4,"put_bytes":514,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2609,"get_count":4,"put_bytes":522,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":2617,"get_count":4,"put_bytes":596,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":2691,"get_count":4,"put_bytes":604,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":2699,"get_count":4,"put_bytes":612,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":2707,"get_count":4,"put_bytes":621,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":2716,"get_count":4,"put_bytes":629,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":2724,"get_count":4,"put_bytes":637,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":2732,"get_count":4,"put_bytes":645,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":2740,"get_count":4,"put_bytes":653,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":2748,"get_count":4,"put_bytes":726,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":2821,"get_count":4,"put_bytes":734,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":2829,"get_count":4,"put_bytes":742,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":2837,"get_count":4,"put_bytes":751,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":2846,"get_count":4,"put_bytes":759,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":2854,"get_count":4,"put_bytes":767,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":2862,"get_count":4,"put_bytes":775,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":2870,"get_count":4,"put_bytes":783,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":2878,"get_count":4,"put_bytes":857,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":2952,"get_count":4,"put_bytes":865,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":2960,"get_count":4,"put_bytes":873,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":2968,"get_count":4,"put_bytes":882,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":2977,"get_count":4,"put_bytes":890,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":2985,"get_count":4,"put_bytes":898,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":2993,"get_count":4,"put_bytes":906,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":3001,"get_count":4,"put_bytes":914,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":3009,"get_count":4,"put_bytes":989,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3084,"get_count":4,"put_bytes":997,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":3092,"get_count":4,"put_bytes":1005,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3100,"get_count":4,"put_bytes":1014,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":3109,"get_count":4,"put_bytes":1022,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3117,"get_count":4,"put_bytes":1030,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":3125,"get_count":4,"put_bytes":1038,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":3133,"get_count":4,"put_bytes":1046,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":3141,"get_count":4,"put_bytes":1120,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3215,"get_count":4,"put_bytes":1128,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3223,"get_count":4,"put_bytes":1136,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":3231,"get_count":4,"put_bytes":1145,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":3240,"get_count":4,"put_bytes":1153,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":3248,"get_count":4,"put_bytes":1161,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":3256,"get_count":4,"put_bytes":1169,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3264,"get_count":4,"put_bytes":1177,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":3272,"get_count":4,"put_bytes":1251,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":3346,"get_count":4,"put_bytes":1259,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":3354,"get_count":4,"put_bytes":1267,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":3362,"get_count":4,"put_bytes":1276,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":3371,"get_count":4,"put_bytes":1284,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":3379,"get_count":4,"put_bytes":1292,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":3387,"get_count":4,"put_bytes":1300,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":3395,"get_count":4,"put_bytes":1308,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":3403,"get_count":4,"put_bytes":1382,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":3477,"get_count":4,"put_bytes":1390,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":3485,"get_count":4,"put_bytes":1398,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":3493,"get_count":4,"put_bytes":1407,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":3502,"get_count":4,"put_bytes":1415,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":3510,"get_count":4,"put_bytes":1423,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":3518,"get_count":4,"put_bytes":1431,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":3526,"get_count":4,"put_bytes":1439,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":3534,"get_count":4,"put_bytes":1513,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":3608,"get_count":4,"put_bytes":1521,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":3616,"get_count":4,"put_bytes":1529,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":3624,"get_count":4,"put_bytes":1538,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":3633,"get_count":4,"put_bytes":1546,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":3641,"get_count":4,"put_bytes":1554,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":3649,"get_count":4,"put_bytes":1562,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":3657,"get_count":4,"put_bytes":1570,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":3665,"get_count":4,"put_bytes":1644,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":3739,"get_count":4,"put_bytes":1652,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1660,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":3755,"get_count":4,"put_bytes":1669,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":3764,"get_count":4,"put_bytes":1677,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":3772,"get_count":4,"put_bytes":1685,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1693,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":3788,"get_count":4,"put_bytes":1701,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":3796,"get_count":4,"put_bytes":1775,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1783,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1791,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":3886,"get_count":4,"put_bytes":1800,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":3895,"get_count":4,"put_bytes":1884,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":3979,"get_count":4,"put_bytes":1892,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3987,"get_count":4,"put_bytes":1900,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":1909,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":4004,"get_count":4,"put_bytes":1917,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":4012,"get_count":4,"put_bytes":1925,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":4020,"get_count":4,"put_bytes":1933,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":4028,"get_count":4,"put_bytes":1941,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":4036,"get_count":4,"put_bytes":2015,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":4110,"get_count":4,"put_bytes":2023,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":4118,"get_count":4,"put_bytes":2031,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":4126,"get_count":4,"put_bytes":2040,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":4135,"get_count":4,"put_bytes":2048,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":4143,"get_count":4,"put_bytes":2056,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":4151,"get_count":4,"put_bytes":2064,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":4159,"get_count":4,"put_bytes":2073,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":4168,"get_count":4,"put_bytes":2146,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":4241,"get_count":4,"put_bytes":2154,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":4249,"get_count":4,"put_bytes":2162,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2171,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2179,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":4274,"get_count":4,"put_bytes":2187,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":4282,"get_count":4,"put_bytes":2195,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":4290,"get_count":4,"put_bytes":2203,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":4298,"get_count":4,"put_bytes":2277,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":4372,"get_count":4,"put_bytes":2285,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":4380,"get_count":4,"put_bytes":2293,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":4388,"get_count":4,"put_bytes":2302,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":4397,"get_count":4,"put_bytes":2310,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2318,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":4413,"get_count":4,"put_bytes":2326,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":4421,"get_count":4,"put_bytes":2334,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":4429,"get_count":4,"put_bytes":2408,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":4503,"get_count":4,"put_bytes":2416,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":4511,"get_count":4,"put_bytes":2424,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":4519,"get_count":4,"put_bytes":2433,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":4528,"get_count":4,"put_bytes":2441,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":4536,"get_count":4,"put_bytes":2449,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":4544,"get_count":4,"put_bytes":2457,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":4552,"get_count":4,"put_bytes":2465,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":4560,"get_count":4,"put_bytes":2539,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":4634,"get_count":4,"put_bytes":2547,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":4642,"get_count":4,"put_bytes":2555,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":4650,"get_count":4,"put_bytes":2564,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":4659,"get_count":4,"put_bytes":2572,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":4667,"get_count":4,"put_bytes":2580,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":4675,"get_count":4,"put_bytes":2588,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":4683,"get_count":4,"put_bytes":2596,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":4691,"get_count":4,"put_bytes":2672,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":4767,"get_count":4,"put_bytes":2680,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":4775,"get_count":4,"put_bytes":2688,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":4783,"get_count":4,"put_bytes":2697,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":4792,"get_count":4,"put_bytes":2705,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":4800,"get_count":4,"put_bytes":2713,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":4808,"get_count":4,"put_bytes":2721,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":4816,"get_count":4,"put_bytes":2729,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":4824,"get_count":4,"put_bytes":2803,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":4898,"get_count":4,"put_bytes":2811,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":4906,"get_count":4,"put_bytes":2819,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":4914,"get_count":4,"put_bytes":2828,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":4923,"get_count":4,"put_bytes":2836,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":4931,"get_count":4,"put_bytes":2844,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":4939,"get_count":4,"put_bytes":2852,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":4947,"get_count":4,"put_bytes":2860,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":4955,"get_count":4,"put_bytes":2934,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":5029,"get_count":4,"put_bytes":2942,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":5037,"get_count":4,"put_bytes":2950,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":5045,"get_count":4,"put_bytes":2959,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":5054,"get_count":4,"put_bytes":2967,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":5062,"get_count":4,"put_bytes":2975,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":5070,"get_count":4,"put_bytes":2983,"put_count":2}} -{"i":71,"series":2,"stats":{"get_bytes":5078,"get_count":4,"put_bytes":2991,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":5086,"get_count":4,"put_bytes":3065,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":5160,"get_count":4,"put_bytes":3073,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":5168,"get_count":4,"put_bytes":3081,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":5176,"get_count":4,"put_bytes":3090,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":5185,"get_count":4,"put_bytes":3098,"put_count":2}} -{"i":77,"series":2,"stats":{"get_bytes":5193,"get_count":4,"put_bytes":3106,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":5201,"get_count":4,"put_bytes":3114,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":5209,"get_count":4,"put_bytes":3122,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":5217,"get_count":4,"put_bytes":3197,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":5292,"get_count":4,"put_bytes":3205,"put_count":2}} -{"i":82,"series":2,"stats":{"get_bytes":5300,"get_count":4,"put_bytes":3213,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":5308,"get_count":4,"put_bytes":3222,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":5317,"get_count":4,"put_bytes":3230,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":5325,"get_count":4,"put_bytes":3238,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":5333,"get_count":4,"put_bytes":3246,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":5341,"get_count":4,"put_bytes":3254,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":5349,"get_count":4,"put_bytes":3327,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":5422,"get_count":4,"put_bytes":3335,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":5430,"get_count":4,"put_bytes":3343,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":5438,"get_count":4,"put_bytes":3352,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":5447,"get_count":4,"put_bytes":3360,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":5455,"get_count":4,"put_bytes":3368,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":5463,"get_count":4,"put_bytes":3376,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":5471,"get_count":4,"put_bytes":3384,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":5479,"get_count":4,"put_bytes":3458,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":5553,"get_count":4,"put_bytes":3466,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":5561,"get_count":4,"put_bytes":3474,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":5569,"get_count":4,"put_bytes":3483,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2288,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2285,"get_count":4,"put_bytes":197,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2293,"get_count":4,"put_bytes":205,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2301,"get_count":4,"put_bytes":214,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2310,"get_count":4,"put_bytes":222,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2318,"get_count":4,"put_bytes":230,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2326,"get_count":4,"put_bytes":238,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2334,"get_count":4,"put_bytes":246,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2342,"get_count":4,"put_bytes":334,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":342,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2438,"get_count":4,"put_bytes":350,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2446,"get_count":4,"put_bytes":359,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2455,"get_count":4,"put_bytes":367,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":375,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2471,"get_count":4,"put_bytes":383,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":2479,"get_count":4,"put_bytes":392,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":466,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":2562,"get_count":4,"put_bytes":474,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2570,"get_count":4,"put_bytes":482,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2578,"get_count":4,"put_bytes":491,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":2587,"get_count":4,"put_bytes":499,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":507,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2603,"get_count":4,"put_bytes":515,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2611,"get_count":4,"put_bytes":523,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":2619,"get_count":4,"put_bytes":597,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":2693,"get_count":4,"put_bytes":605,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":2701,"get_count":4,"put_bytes":613,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":2709,"get_count":4,"put_bytes":622,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":2718,"get_count":4,"put_bytes":630,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2726,"get_count":4,"put_bytes":638,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2734,"get_count":4,"put_bytes":646,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":2742,"get_count":4,"put_bytes":654,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":2750,"get_count":4,"put_bytes":727,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":2823,"get_count":4,"put_bytes":735,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":2831,"get_count":4,"put_bytes":743,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":2839,"get_count":4,"put_bytes":752,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":2848,"get_count":4,"put_bytes":760,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":2856,"get_count":4,"put_bytes":768,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":2864,"get_count":4,"put_bytes":776,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":2872,"get_count":4,"put_bytes":784,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":2880,"get_count":4,"put_bytes":858,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":2954,"get_count":4,"put_bytes":866,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":2962,"get_count":4,"put_bytes":874,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":2970,"get_count":4,"put_bytes":883,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":2979,"get_count":4,"put_bytes":891,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":2987,"get_count":4,"put_bytes":899,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":2995,"get_count":4,"put_bytes":907,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":3003,"get_count":4,"put_bytes":915,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":3011,"get_count":4,"put_bytes":990,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3086,"get_count":4,"put_bytes":998,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":3094,"get_count":4,"put_bytes":1006,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3102,"get_count":4,"put_bytes":1015,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":3111,"get_count":4,"put_bytes":1023,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3119,"get_count":4,"put_bytes":1031,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":3127,"get_count":4,"put_bytes":1039,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":3135,"get_count":4,"put_bytes":1047,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":3143,"get_count":4,"put_bytes":1121,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3217,"get_count":4,"put_bytes":1129,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3225,"get_count":4,"put_bytes":1137,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":3233,"get_count":4,"put_bytes":1146,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":3242,"get_count":4,"put_bytes":1154,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3250,"get_count":4,"put_bytes":1162,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":3258,"get_count":4,"put_bytes":1170,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3266,"get_count":4,"put_bytes":1178,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":3274,"get_count":4,"put_bytes":1252,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":3348,"get_count":4,"put_bytes":1260,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":3356,"get_count":4,"put_bytes":1268,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":3364,"get_count":4,"put_bytes":1277,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":3373,"get_count":4,"put_bytes":1285,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":3381,"get_count":4,"put_bytes":1293,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":3389,"get_count":4,"put_bytes":1301,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":3397,"get_count":4,"put_bytes":1309,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":3405,"get_count":4,"put_bytes":1383,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":3479,"get_count":4,"put_bytes":1391,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":3487,"get_count":4,"put_bytes":1399,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":3495,"get_count":4,"put_bytes":1408,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":3504,"get_count":4,"put_bytes":1416,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":3512,"get_count":4,"put_bytes":1424,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":3520,"get_count":4,"put_bytes":1432,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":3528,"get_count":4,"put_bytes":1440,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":3536,"get_count":4,"put_bytes":1514,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":3610,"get_count":4,"put_bytes":1522,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":3618,"get_count":4,"put_bytes":1530,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":3626,"get_count":4,"put_bytes":1539,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":3635,"get_count":4,"put_bytes":1547,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":3643,"get_count":4,"put_bytes":1555,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":3651,"get_count":4,"put_bytes":1563,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":3659,"get_count":4,"put_bytes":1571,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":3667,"get_count":4,"put_bytes":1645,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":3741,"get_count":4,"put_bytes":1653,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1661,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":3757,"get_count":4,"put_bytes":1670,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":3766,"get_count":4,"put_bytes":1678,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":3774,"get_count":4,"put_bytes":1686,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":3782,"get_count":4,"put_bytes":1694,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":3790,"get_count":4,"put_bytes":1702,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1776,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":3872,"get_count":4,"put_bytes":1784,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":3880,"get_count":4,"put_bytes":1792,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":3888,"get_count":4,"put_bytes":1801,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":3897,"get_count":4,"put_bytes":1885,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3981,"get_count":4,"put_bytes":1893,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3989,"get_count":4,"put_bytes":1901,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3997,"get_count":4,"put_bytes":1910,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":1918,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":4014,"get_count":4,"put_bytes":1926,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":4022,"get_count":4,"put_bytes":1934,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":4030,"get_count":4,"put_bytes":1942,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":4038,"get_count":4,"put_bytes":2016,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":4112,"get_count":4,"put_bytes":2024,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":4120,"get_count":4,"put_bytes":2032,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":4128,"get_count":4,"put_bytes":2041,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":4137,"get_count":4,"put_bytes":2049,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":4145,"get_count":4,"put_bytes":2057,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":4153,"get_count":4,"put_bytes":2065,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":4161,"get_count":4,"put_bytes":2074,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":4170,"get_count":4,"put_bytes":2147,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":4243,"get_count":4,"put_bytes":2155,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":4251,"get_count":4,"put_bytes":2163,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2172,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":4268,"get_count":4,"put_bytes":2180,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2188,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2196,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":4292,"get_count":4,"put_bytes":2204,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2278,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":4374,"get_count":4,"put_bytes":2286,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":4382,"get_count":4,"put_bytes":2294,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":4390,"get_count":4,"put_bytes":2303,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2311,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":4407,"get_count":4,"put_bytes":2319,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2327,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2335,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":4431,"get_count":4,"put_bytes":2409,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":4505,"get_count":4,"put_bytes":2417,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":4513,"get_count":4,"put_bytes":2425,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":4521,"get_count":4,"put_bytes":2434,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2442,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":4538,"get_count":4,"put_bytes":2450,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":4546,"get_count":4,"put_bytes":2458,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":4554,"get_count":4,"put_bytes":2466,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":4562,"get_count":4,"put_bytes":2540,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":4636,"get_count":4,"put_bytes":2548,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":4644,"get_count":4,"put_bytes":2556,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2565,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":4661,"get_count":4,"put_bytes":2573,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":4669,"get_count":4,"put_bytes":2581,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":4677,"get_count":4,"put_bytes":2589,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":4685,"get_count":4,"put_bytes":2597,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2673,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":4769,"get_count":4,"put_bytes":2681,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":4777,"get_count":4,"put_bytes":2689,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":4785,"get_count":4,"put_bytes":2698,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":4794,"get_count":4,"put_bytes":2706,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":4802,"get_count":4,"put_bytes":2714,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":4810,"get_count":4,"put_bytes":2722,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":4818,"get_count":4,"put_bytes":2730,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":4826,"get_count":4,"put_bytes":2804,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":4900,"get_count":4,"put_bytes":2812,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":4908,"get_count":4,"put_bytes":2820,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":4916,"get_count":4,"put_bytes":2829,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":4925,"get_count":4,"put_bytes":2837,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":4933,"get_count":4,"put_bytes":2845,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":4941,"get_count":4,"put_bytes":2853,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":4949,"get_count":4,"put_bytes":2861,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":4957,"get_count":4,"put_bytes":2935,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":5031,"get_count":4,"put_bytes":2943,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":5039,"get_count":4,"put_bytes":2951,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":5047,"get_count":4,"put_bytes":2960,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":5056,"get_count":4,"put_bytes":2968,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":5064,"get_count":4,"put_bytes":2976,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":5072,"get_count":4,"put_bytes":2984,"put_count":2}} +{"i":71,"series":2,"stats":{"get_bytes":5080,"get_count":4,"put_bytes":2992,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":5088,"get_count":4,"put_bytes":3066,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":5162,"get_count":4,"put_bytes":3074,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":5170,"get_count":4,"put_bytes":3082,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":5178,"get_count":4,"put_bytes":3091,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":5187,"get_count":4,"put_bytes":3099,"put_count":2}} +{"i":77,"series":2,"stats":{"get_bytes":5195,"get_count":4,"put_bytes":3107,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":5203,"get_count":4,"put_bytes":3115,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":5211,"get_count":4,"put_bytes":3123,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":5219,"get_count":4,"put_bytes":3198,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":5294,"get_count":4,"put_bytes":3206,"put_count":2}} +{"i":82,"series":2,"stats":{"get_bytes":5302,"get_count":4,"put_bytes":3214,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":5310,"get_count":4,"put_bytes":3223,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":5319,"get_count":4,"put_bytes":3231,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":5327,"get_count":4,"put_bytes":3239,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":5335,"get_count":4,"put_bytes":3247,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":5343,"get_count":4,"put_bytes":3255,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":5351,"get_count":4,"put_bytes":3328,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":5424,"get_count":4,"put_bytes":3336,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":5432,"get_count":4,"put_bytes":3344,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":5440,"get_count":4,"put_bytes":3353,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":5449,"get_count":4,"put_bytes":3361,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":5457,"get_count":4,"put_bytes":3369,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":5465,"get_count":4,"put_bytes":3377,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":5473,"get_count":4,"put_bytes":3385,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":5481,"get_count":4,"put_bytes":3459,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":5555,"get_count":4,"put_bytes":3467,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":5563,"get_count":4,"put_bytes":3475,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":5571,"get_count":4,"put_bytes":3484,"put_count":2}} diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 17e6151a0..49c4792cf 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":3910,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":3907,"get_count":4,"put_bytes":3428,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":5523,"get_count":4,"put_bytes":5113,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":7208,"get_count":4,"put_bytes":6723,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":8818,"get_count":4,"put_bytes":8405,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":10500,"get_count":4,"put_bytes":10019,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":12114,"get_count":4,"put_bytes":11697,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":13792,"get_count":4,"put_bytes":13309,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":15404,"get_count":4,"put_bytes":14987,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":17082,"get_count":4,"put_bytes":16742,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":17727,"get_count":4,"put_bytes":17308,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":19403,"get_count":4,"put_bytes":18917,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":21012,"get_count":4,"put_bytes":20594,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":22689,"get_count":4,"put_bytes":22265,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":23815,"get_count":4,"put_bytes":23396,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":25491,"get_count":4,"put_bytes":25004,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":27099,"get_count":4,"put_bytes":26760,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":28297,"get_count":4,"put_bytes":28039,"put_count":5}} -{"i":18,"series":1,"stats":{"get_bytes":28455,"get_count":4,"put_bytes":28035,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":30130,"get_count":4,"put_bytes":29720,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":31816,"get_count":5,"put_bytes":31396,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":32796,"get_count":4,"put_bytes":32373,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":33918,"get_count":4,"put_bytes":33498,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":36151,"get_count":5,"put_bytes":35660,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":37066,"get_count":4,"put_bytes":36717,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":38259,"get_count":4,"put_bytes":37839,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":39383,"get_count":4,"put_bytes":38960,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":41605,"get_count":5,"put_bytes":41282,"put_count":5}} -{"i":28,"series":1,"stats":{"get_bytes":41566,"get_count":4,"put_bytes":41217,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":43872,"get_count":6,"put_bytes":43605,"put_count":7}} -{"i":30,"series":1,"stats":{"get_bytes":43204,"get_count":5,"put_bytes":43017,"put_count":6}} -{"i":31,"series":1,"stats":{"get_bytes":43304,"get_count":5,"put_bytes":43185,"put_count":8}} -{"i":32,"series":1,"stats":{"get_bytes":42512,"get_count":5,"put_bytes":42161,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":44125,"get_count":6,"put_bytes":43763,"put_count":6}} -{"i":34,"series":1,"stats":{"get_bytes":44509,"get_count":6,"put_bytes":44173,"put_count":5}} -{"i":35,"series":1,"stats":{"get_bytes":45439,"get_count":6,"put_bytes":45108,"put_count":6}} -{"i":36,"series":1,"stats":{"get_bytes":45809,"get_count":6,"put_bytes":45462,"put_count":5}} -{"i":37,"series":1,"stats":{"get_bytes":47559,"get_count":7,"put_bytes":47305,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":47728,"get_count":7,"put_bytes":47442,"put_count":7}} -{"i":39,"series":1,"stats":{"get_bytes":46395,"get_count":5,"put_bytes":46071,"put_count":5}} -{"i":40,"series":1,"stats":{"get_bytes":48984,"get_count":8,"put_bytes":48641,"put_count":7}} -{"i":41,"series":1,"stats":{"get_bytes":48124,"get_count":6,"put_bytes":47867,"put_count":7}} -{"i":42,"series":1,"stats":{"get_bytes":48298,"get_count":6,"put_bytes":48101,"put_count":7}} -{"i":43,"series":1,"stats":{"get_bytes":48809,"get_count":7,"put_bytes":48520,"put_count":8}} -{"i":44,"series":1,"stats":{"get_bytes":49378,"get_count":7,"put_bytes":49330,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":49909,"get_count":10,"put_bytes":49423,"put_count":8}} -{"i":46,"series":1,"stats":{"get_bytes":48241,"get_count":6,"put_bytes":47898,"put_count":5}} -{"i":47,"series":1,"stats":{"get_bytes":49560,"get_count":6,"put_bytes":49304,"put_count":7}} -{"i":48,"series":1,"stats":{"get_bytes":50015,"get_count":7,"put_bytes":49895,"put_count":9}} -{"i":49,"series":1,"stats":{"get_bytes":50185,"get_count":8,"put_bytes":49753,"put_count":7}} -{"i":50,"series":1,"stats":{"get_bytes":51196,"get_count":8,"put_bytes":50849,"put_count":7}} -{"i":51,"series":1,"stats":{"get_bytes":53049,"get_count":10,"put_bytes":52724,"put_count":10}} -{"i":52,"series":1,"stats":{"get_bytes":50435,"get_count":6,"put_bytes":50386,"put_count":9}} -{"i":53,"series":1,"stats":{"get_bytes":51496,"get_count":9,"put_bytes":51335,"put_count":11}} -{"i":54,"series":1,"stats":{"get_bytes":50132,"get_count":8,"put_bytes":49874,"put_count":8}} -{"i":55,"series":1,"stats":{"get_bytes":50032,"get_count":8,"put_bytes":49781,"put_count":9}} -{"i":56,"series":1,"stats":{"get_bytes":50931,"get_count":10,"put_bytes":50589,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":52130,"get_count":10,"put_bytes":51805,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":51523,"get_count":9,"put_bytes":51424,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":49672,"get_count":8,"put_bytes":49271,"put_count":7}} -{"i":60,"series":1,"stats":{"get_bytes":52854,"get_count":10,"put_bytes":52443,"put_count":8}} -{"i":61,"series":1,"stats":{"get_bytes":52099,"get_count":9,"put_bytes":51785,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":54025,"get_count":11,"put_bytes":53844,"put_count":12}} -{"i":63,"series":1,"stats":{"get_bytes":52890,"get_count":10,"put_bytes":52641,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":52240,"get_count":9,"put_bytes":51819,"put_count":7}} -{"i":65,"series":1,"stats":{"get_bytes":52700,"get_count":8,"put_bytes":52655,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":51048,"get_count":10,"put_bytes":50704,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":53909,"get_count":12,"put_bytes":53580,"put_count":12}} -{"i":68,"series":1,"stats":{"get_bytes":52681,"get_count":11,"put_bytes":52418,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":52012,"get_count":10,"put_bytes":51754,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":52329,"get_count":10,"put_bytes":52228,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":51716,"get_count":11,"put_bytes":51307,"put_count":10}} -{"i":72,"series":1,"stats":{"get_bytes":53181,"get_count":13,"put_bytes":52991,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":52220,"get_count":13,"put_bytes":51894,"put_count":13}} -{"i":74,"series":1,"stats":{"get_bytes":49843,"get_count":9,"put_bytes":49858,"put_count":13}} -{"i":75,"series":1,"stats":{"get_bytes":51407,"get_count":14,"put_bytes":51004,"put_count":13}} -{"i":76,"series":1,"stats":{"get_bytes":48188,"get_count":10,"put_bytes":48105,"put_count":12}} -{"i":77,"series":1,"stats":{"get_bytes":50864,"get_count":15,"put_bytes":50482,"put_count":14}} -{"i":78,"series":1,"stats":{"get_bytes":49043,"get_count":13,"put_bytes":48853,"put_count":14}} -{"i":79,"series":1,"stats":{"get_bytes":49468,"get_count":14,"put_bytes":49127,"put_count":14}} -{"i":80,"series":1,"stats":{"get_bytes":47506,"get_count":11,"put_bytes":47232,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":47446,"get_count":12,"put_bytes":47203,"put_count":13}} -{"i":82,"series":1,"stats":{"get_bytes":46969,"get_count":12,"put_bytes":46555,"put_count":10}} -{"i":83,"series":1,"stats":{"get_bytes":49194,"get_count":13,"put_bytes":48860,"put_count":13}} -{"i":84,"series":1,"stats":{"get_bytes":49473,"get_count":15,"put_bytes":49308,"put_count":16}} -{"i":85,"series":1,"stats":{"get_bytes":46204,"get_count":12,"put_bytes":45874,"put_count":12}} -{"i":86,"series":1,"stats":{"get_bytes":47696,"get_count":13,"put_bytes":47431,"put_count":13}} -{"i":87,"series":1,"stats":{"get_bytes":46046,"get_count":14,"put_bytes":45798,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":50421,"get_count":18,"put_bytes":50020,"put_count":16}} -{"i":89,"series":1,"stats":{"get_bytes":45194,"get_count":12,"put_bytes":44946,"put_count":13}} -{"i":90,"series":1,"stats":{"get_bytes":45573,"get_count":14,"put_bytes":45397,"put_count":15}} -{"i":91,"series":1,"stats":{"get_bytes":43669,"get_count":13,"put_bytes":43414,"put_count":14}} -{"i":92,"series":1,"stats":{"get_bytes":42756,"get_count":12,"put_bytes":42653,"put_count":14}} -{"i":93,"series":1,"stats":{"get_bytes":41303,"get_count":12,"put_bytes":41121,"put_count":14}} -{"i":94,"series":1,"stats":{"get_bytes":42378,"get_count":15,"put_bytes":42121,"put_count":15}} -{"i":95,"series":1,"stats":{"get_bytes":42470,"get_count":15,"put_bytes":42067,"put_count":14}} -{"i":96,"series":1,"stats":{"get_bytes":42832,"get_count":16,"put_bytes":42432,"put_count":14}} -{"i":97,"series":1,"stats":{"get_bytes":41669,"get_count":14,"put_bytes":41272,"put_count":13}} -{"i":98,"series":1,"stats":{"get_bytes":40421,"get_count":13,"put_bytes":40159,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":40459,"get_count":13,"put_bytes":40202,"put_count":14}} -{"i":0,"series":2,"stats":{"get_bytes":38736,"get_count":12,"put_bytes":38480,"put_count":12}} -{"i":1,"series":2,"stats":{"get_bytes":42380,"get_count":17,"put_bytes":41975,"put_count":16}} -{"i":2,"series":2,"stats":{"get_bytes":40417,"get_count":15,"put_bytes":40088,"put_count":14}} -{"i":3,"series":2,"stats":{"get_bytes":43359,"get_count":16,"put_bytes":42956,"put_count":15}} -{"i":4,"series":2,"stats":{"get_bytes":40458,"get_count":14,"put_bytes":40130,"put_count":13}} -{"i":5,"series":2,"stats":{"get_bytes":36954,"get_count":11,"put_bytes":36630,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":40074,"get_count":14,"put_bytes":39742,"put_count":13}} -{"i":7,"series":2,"stats":{"get_bytes":40115,"get_count":15,"put_bytes":39701,"put_count":14}} -{"i":8,"series":2,"stats":{"get_bytes":38649,"get_count":13,"put_bytes":38317,"put_count":12}} -{"i":9,"series":2,"stats":{"get_bytes":41516,"get_count":15,"put_bytes":41035,"put_count":13}} -{"i":10,"series":2,"stats":{"get_bytes":41568,"get_count":15,"put_bytes":41299,"put_count":15}} -{"i":11,"series":2,"stats":{"get_bytes":37970,"get_count":13,"put_bytes":37646,"put_count":13}} -{"i":12,"series":2,"stats":{"get_bytes":39766,"get_count":15,"put_bytes":39496,"put_count":15}} -{"i":13,"series":2,"stats":{"get_bytes":36939,"get_count":12,"put_bytes":36617,"put_count":12}} -{"i":14,"series":2,"stats":{"get_bytes":42376,"get_count":17,"put_bytes":42047,"put_count":16}} -{"i":15,"series":2,"stats":{"get_bytes":39652,"get_count":14,"put_bytes":39257,"put_count":13}} -{"i":16,"series":2,"stats":{"get_bytes":40827,"get_count":16,"put_bytes":40424,"put_count":14}} -{"i":17,"series":2,"stats":{"get_bytes":41341,"get_count":17,"put_bytes":40958,"put_count":16}} -{"i":18,"series":2,"stats":{"get_bytes":38987,"get_count":15,"put_bytes":38733,"put_count":15}} -{"i":19,"series":2,"stats":{"get_bytes":37462,"get_count":15,"put_bytes":37044,"put_count":14}} -{"i":20,"series":2,"stats":{"get_bytes":41582,"get_count":17,"put_bytes":41169,"put_count":15}} -{"i":21,"series":2,"stats":{"get_bytes":39943,"get_count":17,"put_bytes":39543,"put_count":16}} -{"i":22,"series":2,"stats":{"get_bytes":38613,"get_count":13,"put_bytes":38499,"put_count":15}} -{"i":23,"series":2,"stats":{"get_bytes":35351,"get_count":14,"put_bytes":34949,"put_count":13}} -{"i":24,"series":2,"stats":{"get_bytes":37760,"get_count":18,"put_bytes":37363,"put_count":16}} -{"i":25,"series":2,"stats":{"get_bytes":38128,"get_count":17,"put_bytes":37733,"put_count":16}} -{"i":26,"series":2,"stats":{"get_bytes":35567,"get_count":13,"put_bytes":35381,"put_count":14}} -{"i":27,"series":2,"stats":{"get_bytes":36406,"get_count":16,"put_bytes":36024,"put_count":15}} -{"i":28,"series":2,"stats":{"get_bytes":37283,"get_count":16,"put_bytes":36878,"put_count":14}} -{"i":29,"series":2,"stats":{"get_bytes":35939,"get_count":16,"put_bytes":35605,"put_count":16}} -{"i":30,"series":2,"stats":{"get_bytes":36981,"get_count":16,"put_bytes":36647,"put_count":15}} -{"i":31,"series":2,"stats":{"get_bytes":34151,"get_count":14,"put_bytes":33902,"put_count":15}} -{"i":32,"series":2,"stats":{"get_bytes":34070,"get_count":15,"put_bytes":33807,"put_count":15}} -{"i":33,"series":2,"stats":{"get_bytes":33364,"get_count":16,"put_bytes":32961,"put_count":15}} -{"i":34,"series":2,"stats":{"get_bytes":34795,"get_count":16,"put_bytes":34463,"put_count":15}} -{"i":35,"series":2,"stats":{"get_bytes":36658,"get_count":17,"put_bytes":36253,"put_count":16}} -{"i":36,"series":2,"stats":{"get_bytes":33705,"get_count":16,"put_bytes":33440,"put_count":16}} -{"i":37,"series":2,"stats":{"get_bytes":34183,"get_count":17,"put_bytes":33724,"put_count":15}} -{"i":38,"series":2,"stats":{"get_bytes":33678,"get_count":17,"put_bytes":33338,"put_count":16}} -{"i":39,"series":2,"stats":{"get_bytes":35653,"get_count":17,"put_bytes":35260,"put_count":16}} -{"i":40,"series":2,"stats":{"get_bytes":30665,"get_count":15,"put_bytes":30490,"put_count":16}} -{"i":41,"series":2,"stats":{"get_bytes":27791,"get_count":12,"put_bytes":27531,"put_count":13}} -{"i":42,"series":2,"stats":{"get_bytes":31036,"get_count":18,"put_bytes":30632,"put_count":16}} -{"i":43,"series":2,"stats":{"get_bytes":30403,"get_count":15,"put_bytes":30071,"put_count":15}} -{"i":44,"series":2,"stats":{"get_bytes":29623,"get_count":16,"put_bytes":29369,"put_count":16}} -{"i":45,"series":2,"stats":{"get_bytes":32498,"get_count":18,"put_bytes":32017,"put_count":16}} -{"i":46,"series":2,"stats":{"get_bytes":28694,"get_count":17,"put_bytes":28360,"put_count":16}} -{"i":47,"series":2,"stats":{"get_bytes":27667,"get_count":18,"put_bytes":27196,"put_count":16}} -{"i":48,"series":2,"stats":{"get_bytes":32457,"get_count":18,"put_bytes":32048,"put_count":16}} -{"i":49,"series":2,"stats":{"get_bytes":31612,"get_count":17,"put_bytes":31131,"put_count":15}} -{"i":50,"series":2,"stats":{"get_bytes":31897,"get_count":18,"put_bytes":31493,"put_count":16}} -{"i":51,"series":2,"stats":{"get_bytes":31603,"get_count":17,"put_bytes":31130,"put_count":15}} -{"i":52,"series":2,"stats":{"get_bytes":31811,"get_count":18,"put_bytes":31401,"put_count":16}} -{"i":53,"series":2,"stats":{"get_bytes":31866,"get_count":18,"put_bytes":31394,"put_count":16}} -{"i":54,"series":2,"stats":{"get_bytes":31738,"get_count":17,"put_bytes":31341,"put_count":15}} -{"i":55,"series":2,"stats":{"get_bytes":32007,"get_count":18,"put_bytes":31526,"put_count":16}} -{"i":56,"series":2,"stats":{"get_bytes":31345,"get_count":16,"put_bytes":31017,"put_count":15}} -{"i":57,"series":2,"stats":{"get_bytes":29591,"get_count":15,"put_bytes":29188,"put_count":14}} -{"i":58,"series":2,"stats":{"get_bytes":31048,"get_count":16,"put_bytes":30771,"put_count":16}} -{"i":59,"series":2,"stats":{"get_bytes":30195,"get_count":17,"put_bytes":29713,"put_count":15}} -{"i":60,"series":2,"stats":{"get_bytes":31551,"get_count":18,"put_bytes":31157,"put_count":16}} -{"i":61,"series":2,"stats":{"get_bytes":32179,"get_count":18,"put_bytes":31702,"put_count":16}} -{"i":62,"series":2,"stats":{"get_bytes":31283,"get_count":17,"put_bytes":30873,"put_count":15}} -{"i":63,"series":2,"stats":{"get_bytes":31429,"get_count":18,"put_bytes":30950,"put_count":16}} -{"i":64,"series":2,"stats":{"get_bytes":29158,"get_count":17,"put_bytes":28829,"put_count":16}} -{"i":65,"series":2,"stats":{"get_bytes":30933,"get_count":18,"put_bytes":30454,"put_count":16}} -{"i":66,"series":2,"stats":{"get_bytes":31198,"get_count":17,"put_bytes":30867,"put_count":16}} -{"i":67,"series":2,"stats":{"get_bytes":27636,"get_count":17,"put_bytes":27158,"put_count":15}} -{"i":68,"series":2,"stats":{"get_bytes":31142,"get_count":18,"put_bytes":30737,"put_count":16}} -{"i":69,"series":2,"stats":{"get_bytes":32136,"get_count":17,"put_bytes":31733,"put_count":16}} -{"i":70,"series":2,"stats":{"get_bytes":30132,"get_count":18,"put_bytes":29724,"put_count":16}} -{"i":71,"series":2,"stats":{"get_bytes":31813,"get_count":18,"put_bytes":31331,"put_count":16}} -{"i":72,"series":2,"stats":{"get_bytes":30217,"get_count":17,"put_bytes":29904,"put_count":16}} -{"i":73,"series":2,"stats":{"get_bytes":31052,"get_count":18,"put_bytes":30581,"put_count":16}} -{"i":74,"series":2,"stats":{"get_bytes":28946,"get_count":17,"put_bytes":28617,"put_count":16}} -{"i":75,"series":2,"stats":{"get_bytes":29500,"get_count":17,"put_bytes":29017,"put_count":15}} -{"i":76,"series":2,"stats":{"get_bytes":30230,"get_count":18,"put_bytes":29820,"put_count":16}} -{"i":77,"series":2,"stats":{"get_bytes":30162,"get_count":17,"put_bytes":29682,"put_count":15}} -{"i":78,"series":2,"stats":{"get_bytes":29536,"get_count":17,"put_bytes":29133,"put_count":15}} -{"i":79,"series":2,"stats":{"get_bytes":31375,"get_count":18,"put_bytes":30907,"put_count":16}} -{"i":80,"series":2,"stats":{"get_bytes":29837,"get_count":18,"put_bytes":29432,"put_count":16}} -{"i":81,"series":2,"stats":{"get_bytes":27233,"get_count":17,"put_bytes":26759,"put_count":15}} -{"i":82,"series":2,"stats":{"get_bytes":30087,"get_count":18,"put_bytes":29681,"put_count":16}} -{"i":83,"series":2,"stats":{"get_bytes":31650,"get_count":18,"put_bytes":31173,"put_count":16}} -{"i":84,"series":2,"stats":{"get_bytes":26820,"get_count":15,"put_bytes":26487,"put_count":14}} -{"i":85,"series":2,"stats":{"get_bytes":32329,"get_count":18,"put_bytes":31855,"put_count":16}} -{"i":86,"series":2,"stats":{"get_bytes":32610,"get_count":17,"put_bytes":32219,"put_count":15}} -{"i":87,"series":2,"stats":{"get_bytes":33000,"get_count":18,"put_bytes":32526,"put_count":16}} -{"i":88,"series":2,"stats":{"get_bytes":31466,"get_count":18,"put_bytes":31058,"put_count":16}} -{"i":89,"series":2,"stats":{"get_bytes":34195,"get_count":18,"put_bytes":33721,"put_count":16}} -{"i":90,"series":2,"stats":{"get_bytes":33537,"get_count":17,"put_bytes":33125,"put_count":15}} -{"i":91,"series":2,"stats":{"get_bytes":33658,"get_count":18,"put_bytes":33176,"put_count":16}} -{"i":92,"series":2,"stats":{"get_bytes":32177,"get_count":16,"put_bytes":31846,"put_count":15}} -{"i":93,"series":2,"stats":{"get_bytes":31822,"get_count":17,"put_bytes":31352,"put_count":15}} -{"i":94,"series":2,"stats":{"get_bytes":31446,"get_count":16,"put_bytes":31102,"put_count":15}} -{"i":95,"series":2,"stats":{"get_bytes":32028,"get_count":17,"put_bytes":31546,"put_count":15}} -{"i":96,"series":2,"stats":{"get_bytes":27384,"get_count":16,"put_bytes":26983,"put_count":14}} -{"i":97,"series":2,"stats":{"get_bytes":31918,"get_count":18,"put_bytes":31442,"put_count":16}} -{"i":98,"series":2,"stats":{"get_bytes":31776,"get_count":17,"put_bytes":31367,"put_count":15}} -{"i":99,"series":2,"stats":{"get_bytes":33881,"get_count":18,"put_bytes":33408,"put_count":16}} +{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":3912,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":3909,"get_count":4,"put_bytes":3429,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":5525,"get_count":4,"put_bytes":5114,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":7210,"get_count":4,"put_bytes":6724,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":8820,"get_count":4,"put_bytes":8406,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":10502,"get_count":4,"put_bytes":10020,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":12116,"get_count":4,"put_bytes":11698,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":13794,"get_count":4,"put_bytes":13310,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":15406,"get_count":4,"put_bytes":14988,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":17084,"get_count":4,"put_bytes":16743,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":17729,"get_count":4,"put_bytes":17309,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":19405,"get_count":4,"put_bytes":18918,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":21014,"get_count":4,"put_bytes":20595,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":22691,"get_count":4,"put_bytes":22266,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":23817,"get_count":4,"put_bytes":23397,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":25493,"get_count":4,"put_bytes":25005,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":27101,"get_count":4,"put_bytes":26761,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":28299,"get_count":4,"put_bytes":28040,"put_count":5}} +{"i":18,"series":1,"stats":{"get_bytes":28457,"get_count":4,"put_bytes":28036,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":30132,"get_count":4,"put_bytes":29721,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":31818,"get_count":5,"put_bytes":31397,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":32798,"get_count":4,"put_bytes":32374,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":33920,"get_count":4,"put_bytes":33499,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":36153,"get_count":5,"put_bytes":35661,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":37068,"get_count":4,"put_bytes":36718,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":38261,"get_count":4,"put_bytes":37840,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":39385,"get_count":4,"put_bytes":38961,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":41607,"get_count":5,"put_bytes":41283,"put_count":5}} +{"i":28,"series":1,"stats":{"get_bytes":41568,"get_count":4,"put_bytes":41218,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":43874,"get_count":6,"put_bytes":43606,"put_count":7}} +{"i":30,"series":1,"stats":{"get_bytes":43206,"get_count":5,"put_bytes":43018,"put_count":6}} +{"i":31,"series":1,"stats":{"get_bytes":43306,"get_count":5,"put_bytes":43186,"put_count":8}} +{"i":32,"series":1,"stats":{"get_bytes":42514,"get_count":5,"put_bytes":42162,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":44127,"get_count":6,"put_bytes":43764,"put_count":6}} +{"i":34,"series":1,"stats":{"get_bytes":44511,"get_count":6,"put_bytes":44174,"put_count":5}} +{"i":35,"series":1,"stats":{"get_bytes":45441,"get_count":6,"put_bytes":45109,"put_count":6}} +{"i":36,"series":1,"stats":{"get_bytes":45811,"get_count":6,"put_bytes":45463,"put_count":5}} +{"i":37,"series":1,"stats":{"get_bytes":47561,"get_count":7,"put_bytes":47306,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":47730,"get_count":7,"put_bytes":47443,"put_count":7}} +{"i":39,"series":1,"stats":{"get_bytes":46397,"get_count":5,"put_bytes":46072,"put_count":5}} +{"i":40,"series":1,"stats":{"get_bytes":48986,"get_count":8,"put_bytes":48642,"put_count":7}} +{"i":41,"series":1,"stats":{"get_bytes":48126,"get_count":6,"put_bytes":47868,"put_count":7}} +{"i":42,"series":1,"stats":{"get_bytes":48300,"get_count":6,"put_bytes":48102,"put_count":7}} +{"i":43,"series":1,"stats":{"get_bytes":48811,"get_count":7,"put_bytes":48521,"put_count":8}} +{"i":44,"series":1,"stats":{"get_bytes":49380,"get_count":7,"put_bytes":49331,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":49911,"get_count":10,"put_bytes":49424,"put_count":8}} +{"i":46,"series":1,"stats":{"get_bytes":48243,"get_count":6,"put_bytes":47899,"put_count":5}} +{"i":47,"series":1,"stats":{"get_bytes":49562,"get_count":6,"put_bytes":49305,"put_count":7}} +{"i":48,"series":1,"stats":{"get_bytes":50017,"get_count":7,"put_bytes":49896,"put_count":9}} +{"i":49,"series":1,"stats":{"get_bytes":50187,"get_count":8,"put_bytes":49754,"put_count":7}} +{"i":50,"series":1,"stats":{"get_bytes":51198,"get_count":8,"put_bytes":50850,"put_count":7}} +{"i":51,"series":1,"stats":{"get_bytes":53051,"get_count":10,"put_bytes":52725,"put_count":10}} +{"i":52,"series":1,"stats":{"get_bytes":50437,"get_count":6,"put_bytes":50387,"put_count":9}} +{"i":53,"series":1,"stats":{"get_bytes":51498,"get_count":9,"put_bytes":51336,"put_count":11}} +{"i":54,"series":1,"stats":{"get_bytes":50134,"get_count":8,"put_bytes":49875,"put_count":8}} +{"i":55,"series":1,"stats":{"get_bytes":50034,"get_count":8,"put_bytes":49782,"put_count":9}} +{"i":56,"series":1,"stats":{"get_bytes":50933,"get_count":10,"put_bytes":50590,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":52132,"get_count":10,"put_bytes":51806,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":51525,"get_count":9,"put_bytes":51425,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":49674,"get_count":8,"put_bytes":49272,"put_count":7}} +{"i":60,"series":1,"stats":{"get_bytes":52856,"get_count":10,"put_bytes":52444,"put_count":8}} +{"i":61,"series":1,"stats":{"get_bytes":52101,"get_count":9,"put_bytes":51786,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":54027,"get_count":11,"put_bytes":53845,"put_count":12}} +{"i":63,"series":1,"stats":{"get_bytes":52892,"get_count":10,"put_bytes":52642,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":52242,"get_count":9,"put_bytes":51820,"put_count":7}} +{"i":65,"series":1,"stats":{"get_bytes":52702,"get_count":8,"put_bytes":52656,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":51050,"get_count":10,"put_bytes":50705,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":53911,"get_count":12,"put_bytes":53581,"put_count":12}} +{"i":68,"series":1,"stats":{"get_bytes":52683,"get_count":11,"put_bytes":52419,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":52014,"get_count":10,"put_bytes":51755,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":52331,"get_count":10,"put_bytes":52229,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":51718,"get_count":11,"put_bytes":51308,"put_count":10}} +{"i":72,"series":1,"stats":{"get_bytes":53183,"get_count":13,"put_bytes":52992,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":52222,"get_count":13,"put_bytes":51895,"put_count":13}} +{"i":74,"series":1,"stats":{"get_bytes":49845,"get_count":9,"put_bytes":49859,"put_count":13}} +{"i":75,"series":1,"stats":{"get_bytes":51409,"get_count":14,"put_bytes":51005,"put_count":13}} +{"i":76,"series":1,"stats":{"get_bytes":48190,"get_count":10,"put_bytes":48106,"put_count":12}} +{"i":77,"series":1,"stats":{"get_bytes":50866,"get_count":15,"put_bytes":50483,"put_count":14}} +{"i":78,"series":1,"stats":{"get_bytes":49045,"get_count":13,"put_bytes":48854,"put_count":14}} +{"i":79,"series":1,"stats":{"get_bytes":49470,"get_count":14,"put_bytes":49128,"put_count":14}} +{"i":80,"series":1,"stats":{"get_bytes":47508,"get_count":11,"put_bytes":47233,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":47448,"get_count":12,"put_bytes":47204,"put_count":13}} +{"i":82,"series":1,"stats":{"get_bytes":46971,"get_count":12,"put_bytes":46556,"put_count":10}} +{"i":83,"series":1,"stats":{"get_bytes":49196,"get_count":13,"put_bytes":48861,"put_count":13}} +{"i":84,"series":1,"stats":{"get_bytes":49475,"get_count":15,"put_bytes":49309,"put_count":16}} +{"i":85,"series":1,"stats":{"get_bytes":46206,"get_count":12,"put_bytes":45875,"put_count":12}} +{"i":86,"series":1,"stats":{"get_bytes":47698,"get_count":13,"put_bytes":47432,"put_count":13}} +{"i":87,"series":1,"stats":{"get_bytes":46048,"get_count":14,"put_bytes":45799,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":50423,"get_count":18,"put_bytes":50021,"put_count":16}} +{"i":89,"series":1,"stats":{"get_bytes":45196,"get_count":12,"put_bytes":44947,"put_count":13}} +{"i":90,"series":1,"stats":{"get_bytes":45575,"get_count":14,"put_bytes":45398,"put_count":15}} +{"i":91,"series":1,"stats":{"get_bytes":43671,"get_count":13,"put_bytes":43415,"put_count":14}} +{"i":92,"series":1,"stats":{"get_bytes":42758,"get_count":12,"put_bytes":42654,"put_count":14}} +{"i":93,"series":1,"stats":{"get_bytes":41305,"get_count":12,"put_bytes":41122,"put_count":14}} +{"i":94,"series":1,"stats":{"get_bytes":42380,"get_count":15,"put_bytes":42122,"put_count":15}} +{"i":95,"series":1,"stats":{"get_bytes":42472,"get_count":15,"put_bytes":42068,"put_count":14}} +{"i":96,"series":1,"stats":{"get_bytes":42834,"get_count":16,"put_bytes":42433,"put_count":14}} +{"i":97,"series":1,"stats":{"get_bytes":41671,"get_count":14,"put_bytes":41273,"put_count":13}} +{"i":98,"series":1,"stats":{"get_bytes":40423,"get_count":13,"put_bytes":40160,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":40461,"get_count":13,"put_bytes":40203,"put_count":14}} +{"i":0,"series":2,"stats":{"get_bytes":38738,"get_count":12,"put_bytes":38481,"put_count":12}} +{"i":1,"series":2,"stats":{"get_bytes":42382,"get_count":17,"put_bytes":41976,"put_count":16}} +{"i":2,"series":2,"stats":{"get_bytes":40419,"get_count":15,"put_bytes":40089,"put_count":14}} +{"i":3,"series":2,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":42957,"put_count":15}} +{"i":4,"series":2,"stats":{"get_bytes":40460,"get_count":14,"put_bytes":40131,"put_count":13}} +{"i":5,"series":2,"stats":{"get_bytes":36956,"get_count":11,"put_bytes":36631,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":40076,"get_count":14,"put_bytes":39743,"put_count":13}} +{"i":7,"series":2,"stats":{"get_bytes":40117,"get_count":15,"put_bytes":39702,"put_count":14}} +{"i":8,"series":2,"stats":{"get_bytes":38651,"get_count":13,"put_bytes":38318,"put_count":12}} +{"i":9,"series":2,"stats":{"get_bytes":41518,"get_count":15,"put_bytes":41036,"put_count":13}} +{"i":10,"series":2,"stats":{"get_bytes":41570,"get_count":15,"put_bytes":41300,"put_count":15}} +{"i":11,"series":2,"stats":{"get_bytes":37972,"get_count":13,"put_bytes":37647,"put_count":13}} +{"i":12,"series":2,"stats":{"get_bytes":39768,"get_count":15,"put_bytes":39497,"put_count":15}} +{"i":13,"series":2,"stats":{"get_bytes":36941,"get_count":12,"put_bytes":36618,"put_count":12}} +{"i":14,"series":2,"stats":{"get_bytes":42378,"get_count":17,"put_bytes":42048,"put_count":16}} +{"i":15,"series":2,"stats":{"get_bytes":39654,"get_count":14,"put_bytes":39258,"put_count":13}} +{"i":16,"series":2,"stats":{"get_bytes":40829,"get_count":16,"put_bytes":40425,"put_count":14}} +{"i":17,"series":2,"stats":{"get_bytes":41343,"get_count":17,"put_bytes":40959,"put_count":16}} +{"i":18,"series":2,"stats":{"get_bytes":38989,"get_count":15,"put_bytes":38734,"put_count":15}} +{"i":19,"series":2,"stats":{"get_bytes":37464,"get_count":15,"put_bytes":37045,"put_count":14}} +{"i":20,"series":2,"stats":{"get_bytes":41584,"get_count":17,"put_bytes":41170,"put_count":15}} +{"i":21,"series":2,"stats":{"get_bytes":39945,"get_count":17,"put_bytes":39544,"put_count":16}} +{"i":22,"series":2,"stats":{"get_bytes":38615,"get_count":13,"put_bytes":38500,"put_count":15}} +{"i":23,"series":2,"stats":{"get_bytes":35353,"get_count":14,"put_bytes":34950,"put_count":13}} +{"i":24,"series":2,"stats":{"get_bytes":37762,"get_count":18,"put_bytes":37364,"put_count":16}} +{"i":25,"series":2,"stats":{"get_bytes":38130,"get_count":17,"put_bytes":37734,"put_count":16}} +{"i":26,"series":2,"stats":{"get_bytes":35569,"get_count":13,"put_bytes":35382,"put_count":14}} +{"i":27,"series":2,"stats":{"get_bytes":36408,"get_count":16,"put_bytes":36025,"put_count":15}} +{"i":28,"series":2,"stats":{"get_bytes":37285,"get_count":16,"put_bytes":36879,"put_count":14}} +{"i":29,"series":2,"stats":{"get_bytes":35941,"get_count":16,"put_bytes":35606,"put_count":16}} +{"i":30,"series":2,"stats":{"get_bytes":36983,"get_count":16,"put_bytes":36648,"put_count":15}} +{"i":31,"series":2,"stats":{"get_bytes":34153,"get_count":14,"put_bytes":33903,"put_count":15}} +{"i":32,"series":2,"stats":{"get_bytes":34072,"get_count":15,"put_bytes":33808,"put_count":15}} +{"i":33,"series":2,"stats":{"get_bytes":33366,"get_count":16,"put_bytes":32962,"put_count":15}} +{"i":34,"series":2,"stats":{"get_bytes":34797,"get_count":16,"put_bytes":34464,"put_count":15}} +{"i":35,"series":2,"stats":{"get_bytes":36660,"get_count":17,"put_bytes":36254,"put_count":16}} +{"i":36,"series":2,"stats":{"get_bytes":33707,"get_count":16,"put_bytes":33441,"put_count":16}} +{"i":37,"series":2,"stats":{"get_bytes":34185,"get_count":17,"put_bytes":33725,"put_count":15}} +{"i":38,"series":2,"stats":{"get_bytes":33680,"get_count":17,"put_bytes":33339,"put_count":16}} +{"i":39,"series":2,"stats":{"get_bytes":35655,"get_count":17,"put_bytes":35261,"put_count":16}} +{"i":40,"series":2,"stats":{"get_bytes":30667,"get_count":15,"put_bytes":30491,"put_count":16}} +{"i":41,"series":2,"stats":{"get_bytes":27793,"get_count":12,"put_bytes":27532,"put_count":13}} +{"i":42,"series":2,"stats":{"get_bytes":31038,"get_count":18,"put_bytes":30633,"put_count":16}} +{"i":43,"series":2,"stats":{"get_bytes":30405,"get_count":15,"put_bytes":30072,"put_count":15}} +{"i":44,"series":2,"stats":{"get_bytes":29625,"get_count":16,"put_bytes":29370,"put_count":16}} +{"i":45,"series":2,"stats":{"get_bytes":32500,"get_count":18,"put_bytes":32018,"put_count":16}} +{"i":46,"series":2,"stats":{"get_bytes":28696,"get_count":17,"put_bytes":28361,"put_count":16}} +{"i":47,"series":2,"stats":{"get_bytes":27669,"get_count":18,"put_bytes":27197,"put_count":16}} +{"i":48,"series":2,"stats":{"get_bytes":32459,"get_count":18,"put_bytes":32049,"put_count":16}} +{"i":49,"series":2,"stats":{"get_bytes":31614,"get_count":17,"put_bytes":31132,"put_count":15}} +{"i":50,"series":2,"stats":{"get_bytes":31899,"get_count":18,"put_bytes":31494,"put_count":16}} +{"i":51,"series":2,"stats":{"get_bytes":31605,"get_count":17,"put_bytes":31131,"put_count":15}} +{"i":52,"series":2,"stats":{"get_bytes":31813,"get_count":18,"put_bytes":31402,"put_count":16}} +{"i":53,"series":2,"stats":{"get_bytes":31868,"get_count":18,"put_bytes":31395,"put_count":16}} +{"i":54,"series":2,"stats":{"get_bytes":31740,"get_count":17,"put_bytes":31342,"put_count":15}} +{"i":55,"series":2,"stats":{"get_bytes":32009,"get_count":18,"put_bytes":31527,"put_count":16}} +{"i":56,"series":2,"stats":{"get_bytes":31347,"get_count":16,"put_bytes":31018,"put_count":15}} +{"i":57,"series":2,"stats":{"get_bytes":29593,"get_count":15,"put_bytes":29189,"put_count":14}} +{"i":58,"series":2,"stats":{"get_bytes":31050,"get_count":16,"put_bytes":30772,"put_count":16}} +{"i":59,"series":2,"stats":{"get_bytes":30197,"get_count":17,"put_bytes":29714,"put_count":15}} +{"i":60,"series":2,"stats":{"get_bytes":31553,"get_count":18,"put_bytes":31158,"put_count":16}} +{"i":61,"series":2,"stats":{"get_bytes":32181,"get_count":18,"put_bytes":31703,"put_count":16}} +{"i":62,"series":2,"stats":{"get_bytes":31285,"get_count":17,"put_bytes":30874,"put_count":15}} +{"i":63,"series":2,"stats":{"get_bytes":31431,"get_count":18,"put_bytes":30951,"put_count":16}} +{"i":64,"series":2,"stats":{"get_bytes":29160,"get_count":17,"put_bytes":28830,"put_count":16}} +{"i":65,"series":2,"stats":{"get_bytes":30935,"get_count":18,"put_bytes":30455,"put_count":16}} +{"i":66,"series":2,"stats":{"get_bytes":31200,"get_count":17,"put_bytes":30868,"put_count":16}} +{"i":67,"series":2,"stats":{"get_bytes":27638,"get_count":17,"put_bytes":27159,"put_count":15}} +{"i":68,"series":2,"stats":{"get_bytes":31144,"get_count":18,"put_bytes":30738,"put_count":16}} +{"i":69,"series":2,"stats":{"get_bytes":32138,"get_count":17,"put_bytes":31734,"put_count":16}} +{"i":70,"series":2,"stats":{"get_bytes":30134,"get_count":18,"put_bytes":29725,"put_count":16}} +{"i":71,"series":2,"stats":{"get_bytes":31815,"get_count":18,"put_bytes":31332,"put_count":16}} +{"i":72,"series":2,"stats":{"get_bytes":30219,"get_count":17,"put_bytes":29905,"put_count":16}} +{"i":73,"series":2,"stats":{"get_bytes":31054,"get_count":18,"put_bytes":30582,"put_count":16}} +{"i":74,"series":2,"stats":{"get_bytes":28948,"get_count":17,"put_bytes":28618,"put_count":16}} +{"i":75,"series":2,"stats":{"get_bytes":29502,"get_count":17,"put_bytes":29018,"put_count":15}} +{"i":76,"series":2,"stats":{"get_bytes":30232,"get_count":18,"put_bytes":29821,"put_count":16}} +{"i":77,"series":2,"stats":{"get_bytes":30164,"get_count":17,"put_bytes":29683,"put_count":15}} +{"i":78,"series":2,"stats":{"get_bytes":29538,"get_count":17,"put_bytes":29134,"put_count":15}} +{"i":79,"series":2,"stats":{"get_bytes":31377,"get_count":18,"put_bytes":30908,"put_count":16}} +{"i":80,"series":2,"stats":{"get_bytes":29839,"get_count":18,"put_bytes":29433,"put_count":16}} +{"i":81,"series":2,"stats":{"get_bytes":27235,"get_count":17,"put_bytes":26760,"put_count":15}} +{"i":82,"series":2,"stats":{"get_bytes":30089,"get_count":18,"put_bytes":29682,"put_count":16}} +{"i":83,"series":2,"stats":{"get_bytes":31652,"get_count":18,"put_bytes":31174,"put_count":16}} +{"i":84,"series":2,"stats":{"get_bytes":26822,"get_count":15,"put_bytes":26488,"put_count":14}} +{"i":85,"series":2,"stats":{"get_bytes":32331,"get_count":18,"put_bytes":31856,"put_count":16}} +{"i":86,"series":2,"stats":{"get_bytes":32612,"get_count":17,"put_bytes":32220,"put_count":15}} +{"i":87,"series":2,"stats":{"get_bytes":33002,"get_count":18,"put_bytes":32527,"put_count":16}} +{"i":88,"series":2,"stats":{"get_bytes":31468,"get_count":18,"put_bytes":31059,"put_count":16}} +{"i":89,"series":2,"stats":{"get_bytes":34197,"get_count":18,"put_bytes":33722,"put_count":16}} +{"i":90,"series":2,"stats":{"get_bytes":33539,"get_count":17,"put_bytes":33126,"put_count":15}} +{"i":91,"series":2,"stats":{"get_bytes":33660,"get_count":18,"put_bytes":33177,"put_count":16}} +{"i":92,"series":2,"stats":{"get_bytes":32179,"get_count":16,"put_bytes":31847,"put_count":15}} +{"i":93,"series":2,"stats":{"get_bytes":31824,"get_count":17,"put_bytes":31353,"put_count":15}} +{"i":94,"series":2,"stats":{"get_bytes":31448,"get_count":16,"put_bytes":31103,"put_count":15}} +{"i":95,"series":2,"stats":{"get_bytes":32030,"get_count":17,"put_bytes":31547,"put_count":15}} +{"i":96,"series":2,"stats":{"get_bytes":27386,"get_count":16,"put_bytes":26984,"put_count":14}} +{"i":97,"series":2,"stats":{"get_bytes":31920,"get_count":18,"put_bytes":31443,"put_count":16}} +{"i":98,"series":2,"stats":{"get_bytes":31778,"get_count":17,"put_bytes":31368,"put_count":15}} +{"i":99,"series":2,"stats":{"get_bytes":33883,"get_count":18,"put_bytes":33409,"put_count":16}} diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 258429acb..0551d9e9f 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":34508,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":34099,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":34912,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":34379,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":34642,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":34373,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":34243,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":33546,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":34369,"get_count":6,"put_bytes":87,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index 2b8cf5561..eb288e3f2 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":42071,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":42389,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":43499,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":42714,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":42309,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":39006,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":41130,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":41853,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":44292,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":46580,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":41306,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":43658,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":44263,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":43508,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":42418,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":45239,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":44415,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":42952,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":46498,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":44904,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":43648,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":45119,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":43298,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":43814,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":40601,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":43921,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":44288,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":45164,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":43774,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":43065,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":44063,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":44174,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":43940,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":43621,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":41546,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":45997,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":45285,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":43236,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":45813,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":41085,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":42985,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":41989,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":45401,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":43754,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":39809,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":42601,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":44114,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":43108,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":44068,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":44180,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":43966,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":43556,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":41907,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":40240,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":41887,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":42994,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":43109,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":41296,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":42255,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":42860,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":42656,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":43085,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":44355,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":43239,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":42948,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":43936,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":41964,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":44570,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":40879,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":44374,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":44481,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":43900,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":44752,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":42693,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":46248,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":44650,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":43511,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":42993,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":44146,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":45293,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":41858,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":43920,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":42883,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":46903,"get_count":18,"put_bytes":87,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":43542,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":43959,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":43281,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":42727,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":42435,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":43920,"get_count":17,"put_bytes":87,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":43933,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":43791,"get_count":16,"put_bytes":87,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":42548,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":41899,"get_count":15,"put_bytes":87,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":42711,"get_count":16,"put_bytes":87,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":42073,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":42391,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":43501,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":42552,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":42716,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":42311,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":39008,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":41132,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":41855,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":44294,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":46582,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":41308,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":43660,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":44265,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":43363,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":43510,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":42420,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":45241,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":44417,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":42954,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":46500,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":44906,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":43650,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":45121,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":43300,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":43816,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":40603,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":43923,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":44290,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":45166,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":43776,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":43067,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":44065,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":44176,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":43942,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":43623,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":41548,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":45999,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":45287,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":43238,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":45815,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":41087,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":42987,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":41991,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":45403,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":43756,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":39811,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":42603,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":44070,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":44182,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":43968,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":43558,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":41909,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":40242,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":41889,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":42996,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":43111,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":41298,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":42257,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":42862,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":42658,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":43087,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":44357,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":43241,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":42950,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":43938,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":41966,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":44572,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":40881,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":44376,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":44483,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":43902,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":44754,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":42695,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":46250,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":44652,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":43513,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":43114,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":44118,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":42995,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":44148,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":45295,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":41860,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":43922,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":42885,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":46905,"get_count":18,"put_bytes":88,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":43544,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":43961,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":43283,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":42729,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":42437,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":43922,"get_count":17,"put_bytes":88,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":43935,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":43793,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":41901,"get_count":15,"put_bytes":88,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":42713,"get_count":16,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index 501ab6b36..de8091700 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2436,"get_count":4,"put_bytes":351,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":500,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2742,"get_count":4,"put_bytes":647,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2888,"get_count":4,"put_bytes":793,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":3102,"get_count":4,"put_bytes":1007,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":3250,"get_count":4,"put_bytes":1155,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":3397,"get_count":4,"put_bytes":1302,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":3544,"get_count":4,"put_bytes":1449,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":3757,"get_count":4,"put_bytes":1662,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":3905,"get_count":4,"put_bytes":1810,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":4052,"get_count":4,"put_bytes":1957,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":4201,"get_count":4,"put_bytes":2106,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2319,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":4562,"get_count":4,"put_bytes":2467,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":4709,"get_count":4,"put_bytes":2614,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":4856,"get_count":4,"put_bytes":2762,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":5069,"get_count":4,"put_bytes":2974,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":5217,"get_count":4,"put_bytes":3122,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":5363,"get_count":4,"put_bytes":3268,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":5511,"get_count":4,"put_bytes":3416,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":5724,"get_count":4,"put_bytes":3629,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":5872,"get_count":4,"put_bytes":3777,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":6018,"get_count":4,"put_bytes":3923,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":6165,"get_count":4,"put_bytes":4070,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":6378,"get_count":4,"put_bytes":4283,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":6527,"get_count":4,"put_bytes":4432,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":6676,"get_count":4,"put_bytes":4581,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":6823,"get_count":4,"put_bytes":4728,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":7036,"get_count":4,"put_bytes":4941,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":7184,"get_count":4,"put_bytes":5089,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":7331,"get_count":4,"put_bytes":5236,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":7478,"get_count":4,"put_bytes":5383,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":7691,"get_count":4,"put_bytes":5596,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":7838,"get_count":4,"put_bytes":5743,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":7985,"get_count":4,"put_bytes":5890,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":8132,"get_count":4,"put_bytes":6037,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":8344,"get_count":4,"put_bytes":6249,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":8492,"get_count":4,"put_bytes":6397,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":8638,"get_count":4,"put_bytes":6543,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":8784,"get_count":4,"put_bytes":6689,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":8997,"get_count":4,"put_bytes":6902,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":9144,"get_count":4,"put_bytes":7049,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":9291,"get_count":4,"put_bytes":7196,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":9438,"get_count":4,"put_bytes":7343,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":9651,"get_count":4,"put_bytes":7556,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":9799,"get_count":4,"put_bytes":7704,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":9946,"get_count":4,"put_bytes":7851,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":10093,"get_count":4,"put_bytes":7998,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":10306,"get_count":4,"put_bytes":8211,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":10454,"get_count":4,"put_bytes":8359,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":10601,"get_count":4,"put_bytes":8506,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":10748,"get_count":4,"put_bytes":8653,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":10961,"get_count":4,"put_bytes":8866,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":11109,"get_count":4,"put_bytes":9014,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":11256,"get_count":4,"put_bytes":9161,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":11403,"get_count":4,"put_bytes":9308,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":11616,"get_count":4,"put_bytes":9521,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":11764,"get_count":4,"put_bytes":9669,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":11911,"get_count":4,"put_bytes":9816,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":12058,"get_count":4,"put_bytes":9963,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":12270,"get_count":4,"put_bytes":10175,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":12418,"get_count":4,"put_bytes":10323,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":12564,"get_count":4,"put_bytes":10469,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":12711,"get_count":4,"put_bytes":10616,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":12924,"get_count":4,"put_bytes":10829,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":13071,"get_count":4,"put_bytes":10976,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":13218,"get_count":4,"put_bytes":11123,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":13365,"get_count":4,"put_bytes":11270,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":13577,"get_count":4,"put_bytes":11482,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":13724,"get_count":4,"put_bytes":11629,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":13871,"get_count":4,"put_bytes":11776,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":14017,"get_count":4,"put_bytes":11922,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":14230,"get_count":4,"put_bytes":12135,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":14378,"get_count":4,"put_bytes":12283,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":14525,"get_count":4,"put_bytes":12430,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":14672,"get_count":4,"put_bytes":12577,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":14885,"get_count":4,"put_bytes":12790,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":15033,"get_count":4,"put_bytes":12938,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":15179,"get_count":4,"put_bytes":13084,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":15326,"get_count":4,"put_bytes":13231,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":15538,"get_count":4,"put_bytes":13443,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":15685,"get_count":4,"put_bytes":13590,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":15831,"get_count":4,"put_bytes":13736,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":15977,"get_count":4,"put_bytes":13882,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":16190,"get_count":4,"put_bytes":14095,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":16337,"get_count":4,"put_bytes":14242,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":16484,"get_count":4,"put_bytes":14389,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":16631,"get_count":4,"put_bytes":14536,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":16844,"get_count":4,"put_bytes":14749,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":16992,"get_count":4,"put_bytes":14897,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":17139,"get_count":4,"put_bytes":15044,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":17285,"get_count":4,"put_bytes":15190,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":17019,"get_count":4,"put_bytes":14924,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":17166,"get_count":4,"put_bytes":15071,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":17313,"get_count":4,"put_bytes":15218,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":17460,"get_count":4,"put_bytes":15365,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":17673,"get_count":4,"put_bytes":15578,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":17374,"get_count":4,"put_bytes":15279,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":17488,"get_count":4,"put_bytes":15393,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":17635,"get_count":4,"put_bytes":15540,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":2958,"get_count":4,"put_bytes":873,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1613,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2352,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":5185,"get_count":4,"put_bytes":3090,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":5924,"get_count":4,"put_bytes":3829,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":6664,"get_count":4,"put_bytes":4569,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":7401,"get_count":4,"put_bytes":5306,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":8137,"get_count":4,"put_bytes":6042,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":8872,"get_count":4,"put_bytes":6777,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7511,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":10345,"get_count":4,"put_bytes":8250,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":11076,"get_count":4,"put_bytes":8981,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":11809,"get_count":4,"put_bytes":9714,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":12543,"get_count":4,"put_bytes":10448,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":13027,"get_count":4,"put_bytes":10932,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":13760,"get_count":4,"put_bytes":11666,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":14244,"get_count":4,"put_bytes":12149,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":14477,"get_count":4,"put_bytes":12382,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":15213,"get_count":4,"put_bytes":13118,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":15449,"get_count":4,"put_bytes":13354,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":16181,"get_count":4,"put_bytes":14086,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":16840,"get_count":4,"put_bytes":14745,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":17499,"get_count":4,"put_bytes":15404,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":18161,"get_count":4,"put_bytes":16066,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":18823,"get_count":4,"put_bytes":16728,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":19309,"get_count":4,"put_bytes":17214,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":19039,"get_count":4,"put_bytes":16944,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":19448,"get_count":4,"put_bytes":17353,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":20182,"get_count":4,"put_bytes":18087,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":20342,"get_count":4,"put_bytes":18247,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":20750,"get_count":4,"put_bytes":18655,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":20733,"get_count":4,"put_bytes":18638,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":21468,"get_count":4,"put_bytes":19373,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":21378,"get_count":4,"put_bytes":19283,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":22037,"get_count":4,"put_bytes":19942,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":22448,"get_count":4,"put_bytes":20353,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":23183,"get_count":4,"put_bytes":21088,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":23843,"get_count":4,"put_bytes":21748,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":24001,"get_count":4,"put_bytes":21906,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":24409,"get_count":4,"put_bytes":22314,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":24818,"get_count":4,"put_bytes":22723,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":25331,"get_count":4,"put_bytes":23236,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":25738,"get_count":4,"put_bytes":23643,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":25750,"get_count":4,"put_bytes":23655,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":26483,"get_count":4,"put_bytes":24388,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":26894,"get_count":4,"put_bytes":24799,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":27375,"get_count":4,"put_bytes":25280,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":27858,"get_count":4,"put_bytes":25763,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":28268,"get_count":4,"put_bytes":26173,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":28501,"get_count":4,"put_bytes":26406,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":28737,"get_count":4,"put_bytes":26642,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":28821,"get_count":4,"put_bytes":26726,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":29227,"get_count":4,"put_bytes":27132,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":29384,"get_count":4,"put_bytes":27289,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":29794,"get_count":4,"put_bytes":27699,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":29701,"get_count":4,"put_bytes":27606,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":29713,"get_count":4,"put_bytes":27618,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":29726,"get_count":4,"put_bytes":27631,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":30061,"get_count":4,"put_bytes":27966,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":30073,"get_count":4,"put_bytes":27978,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":29908,"get_count":4,"put_bytes":27813,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":30171,"get_count":4,"put_bytes":28076,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":30538,"get_count":4,"put_bytes":28443,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":30623,"get_count":4,"put_bytes":28528,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":30458,"get_count":4,"put_bytes":28363,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":30219,"get_count":4,"put_bytes":28124,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":29981,"get_count":4,"put_bytes":27886,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":29638,"get_count":4,"put_bytes":27543,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":29575,"get_count":4,"put_bytes":27480,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":29586,"get_count":4,"put_bytes":27491,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":29666,"get_count":5,"put_bytes":27571,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":30001,"get_count":5,"put_bytes":27906,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":29616,"get_count":5,"put_bytes":27521,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":29481,"get_count":5,"put_bytes":27386,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":29242,"get_count":5,"put_bytes":27147,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":28826,"get_count":5,"put_bytes":26731,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":28515,"get_count":5,"put_bytes":26420,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":28557,"get_count":5,"put_bytes":26462,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":28923,"get_count":5,"put_bytes":26828,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":28434,"get_count":5,"put_bytes":26339,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":27726,"get_count":5,"put_bytes":25631,"put_count":3}} -{"i":81,"series":2,"stats":{"get_bytes":27622,"get_count":5,"put_bytes":25527,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":27632,"get_count":5,"put_bytes":25537,"put_count":3}} -{"i":83,"series":2,"stats":{"get_bytes":27497,"get_count":5,"put_bytes":25402,"put_count":3}} -{"i":84,"series":2,"stats":{"get_bytes":27936,"get_count":5,"put_bytes":25841,"put_count":3}} -{"i":85,"series":2,"stats":{"get_bytes":27948,"get_count":5,"put_bytes":25853,"put_count":3}} -{"i":86,"series":2,"stats":{"get_bytes":27668,"get_count":5,"put_bytes":25573,"put_count":3}} -{"i":87,"series":2,"stats":{"get_bytes":27782,"get_count":5,"put_bytes":25687,"put_count":3}} -{"i":88,"series":2,"stats":{"get_bytes":27575,"get_count":5,"put_bytes":25480,"put_count":3}} -{"i":89,"series":2,"stats":{"get_bytes":27190,"get_count":5,"put_bytes":25095,"put_count":3}} -{"i":90,"series":2,"stats":{"get_bytes":26982,"get_count":5,"put_bytes":24887,"put_count":3}} -{"i":91,"series":2,"stats":{"get_bytes":26597,"get_count":5,"put_bytes":24502,"put_count":3}} -{"i":92,"series":2,"stats":{"get_bytes":26036,"get_count":5,"put_bytes":23941,"put_count":3}} -{"i":93,"series":2,"stats":{"get_bytes":25900,"get_count":5,"put_bytes":23805,"put_count":3}} -{"i":94,"series":2,"stats":{"get_bytes":26162,"get_count":5,"put_bytes":24067,"put_count":3}} -{"i":95,"series":2,"stats":{"get_bytes":26028,"get_count":5,"put_bytes":23933,"put_count":3}} -{"i":96,"series":2,"stats":{"get_bytes":26466,"get_count":5,"put_bytes":24371,"put_count":3}} -{"i":97,"series":2,"stats":{"get_bytes":26477,"get_count":5,"put_bytes":24382,"put_count":3}} -{"i":98,"series":2,"stats":{"get_bytes":26269,"get_count":5,"put_bytes":24174,"put_count":3}} -{"i":99,"series":2,"stats":{"get_bytes":26311,"get_count":5,"put_bytes":24216,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2438,"get_count":4,"put_bytes":352,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2597,"get_count":4,"put_bytes":501,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2744,"get_count":4,"put_bytes":648,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2890,"get_count":4,"put_bytes":794,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":3104,"get_count":4,"put_bytes":1008,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":3252,"get_count":4,"put_bytes":1156,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":3399,"get_count":4,"put_bytes":1303,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":3546,"get_count":4,"put_bytes":1450,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":3759,"get_count":4,"put_bytes":1663,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":3907,"get_count":4,"put_bytes":1811,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":4054,"get_count":4,"put_bytes":1958,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":4203,"get_count":4,"put_bytes":2107,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":4416,"get_count":4,"put_bytes":2320,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":4564,"get_count":4,"put_bytes":2468,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":4711,"get_count":4,"put_bytes":2615,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":4858,"get_count":4,"put_bytes":2763,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":5071,"get_count":4,"put_bytes":2975,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":5219,"get_count":4,"put_bytes":3123,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":5365,"get_count":4,"put_bytes":3269,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":5513,"get_count":4,"put_bytes":3417,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":5726,"get_count":4,"put_bytes":3630,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":5874,"get_count":4,"put_bytes":3778,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":6020,"get_count":4,"put_bytes":3924,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":6167,"get_count":4,"put_bytes":4071,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":6380,"get_count":4,"put_bytes":4284,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":6529,"get_count":4,"put_bytes":4433,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":6678,"get_count":4,"put_bytes":4582,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":6825,"get_count":4,"put_bytes":4729,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":7038,"get_count":4,"put_bytes":4942,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":7186,"get_count":4,"put_bytes":5090,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":7333,"get_count":4,"put_bytes":5237,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":7480,"get_count":4,"put_bytes":5384,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":7693,"get_count":4,"put_bytes":5597,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":7840,"get_count":4,"put_bytes":5744,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":7987,"get_count":4,"put_bytes":5891,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":8134,"get_count":4,"put_bytes":6038,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":8346,"get_count":4,"put_bytes":6250,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":8494,"get_count":4,"put_bytes":6398,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":8640,"get_count":4,"put_bytes":6544,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":8786,"get_count":4,"put_bytes":6690,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":8999,"get_count":4,"put_bytes":6903,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":9146,"get_count":4,"put_bytes":7050,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":9293,"get_count":4,"put_bytes":7197,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":9440,"get_count":4,"put_bytes":7344,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":9653,"get_count":4,"put_bytes":7557,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":9801,"get_count":4,"put_bytes":7705,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":9948,"get_count":4,"put_bytes":7852,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":10095,"get_count":4,"put_bytes":7999,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":10308,"get_count":4,"put_bytes":8212,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":10456,"get_count":4,"put_bytes":8360,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":10603,"get_count":4,"put_bytes":8507,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":10750,"get_count":4,"put_bytes":8654,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":10963,"get_count":4,"put_bytes":8867,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":11111,"get_count":4,"put_bytes":9015,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":11258,"get_count":4,"put_bytes":9162,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":11405,"get_count":4,"put_bytes":9309,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":11618,"get_count":4,"put_bytes":9522,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":11766,"get_count":4,"put_bytes":9670,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":11913,"get_count":4,"put_bytes":9817,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":12060,"get_count":4,"put_bytes":9964,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":12272,"get_count":4,"put_bytes":10176,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":12420,"get_count":4,"put_bytes":10324,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":12566,"get_count":4,"put_bytes":10470,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":12713,"get_count":4,"put_bytes":10617,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":12926,"get_count":4,"put_bytes":10830,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":13073,"get_count":4,"put_bytes":10977,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":13220,"get_count":4,"put_bytes":11124,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":13367,"get_count":4,"put_bytes":11271,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":13579,"get_count":4,"put_bytes":11483,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":13726,"get_count":4,"put_bytes":11630,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":13873,"get_count":4,"put_bytes":11777,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":14019,"get_count":4,"put_bytes":11923,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":14232,"get_count":4,"put_bytes":12136,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":14380,"get_count":4,"put_bytes":12284,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":14527,"get_count":4,"put_bytes":12431,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":14674,"get_count":4,"put_bytes":12578,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":14887,"get_count":4,"put_bytes":12791,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":15035,"get_count":4,"put_bytes":12939,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":15181,"get_count":4,"put_bytes":13085,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":15328,"get_count":4,"put_bytes":13232,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":15540,"get_count":4,"put_bytes":13444,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":15687,"get_count":4,"put_bytes":13591,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":15833,"get_count":4,"put_bytes":13737,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":15979,"get_count":4,"put_bytes":13883,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":16192,"get_count":4,"put_bytes":14096,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":16339,"get_count":4,"put_bytes":14243,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":16486,"get_count":4,"put_bytes":14390,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":16633,"get_count":4,"put_bytes":14537,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":16846,"get_count":4,"put_bytes":14750,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":16994,"get_count":4,"put_bytes":14898,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":17141,"get_count":4,"put_bytes":15045,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":17287,"get_count":4,"put_bytes":15191,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":17021,"get_count":4,"put_bytes":14925,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":17168,"get_count":4,"put_bytes":15072,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":17315,"get_count":4,"put_bytes":15219,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":17462,"get_count":4,"put_bytes":15366,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":17675,"get_count":4,"put_bytes":15579,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":17376,"get_count":4,"put_bytes":15280,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":17490,"get_count":4,"put_bytes":15394,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":17637,"get_count":4,"put_bytes":15541,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":2960,"get_count":4,"put_bytes":874,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1614,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":4449,"get_count":4,"put_bytes":2353,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":5187,"get_count":4,"put_bytes":3091,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":5926,"get_count":4,"put_bytes":3830,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":6666,"get_count":4,"put_bytes":4570,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":7403,"get_count":4,"put_bytes":5307,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":8139,"get_count":4,"put_bytes":6043,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":8874,"get_count":4,"put_bytes":6778,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":9608,"get_count":4,"put_bytes":7512,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":10347,"get_count":4,"put_bytes":8251,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":11078,"get_count":4,"put_bytes":8982,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":11811,"get_count":4,"put_bytes":9715,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":12545,"get_count":4,"put_bytes":10449,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":13029,"get_count":4,"put_bytes":10933,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":13762,"get_count":4,"put_bytes":11667,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":14246,"get_count":4,"put_bytes":12150,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":14479,"get_count":4,"put_bytes":12383,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":15215,"get_count":4,"put_bytes":13119,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":15451,"get_count":4,"put_bytes":13355,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":16183,"get_count":4,"put_bytes":14087,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":16842,"get_count":4,"put_bytes":14746,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":17501,"get_count":4,"put_bytes":15405,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":18163,"get_count":4,"put_bytes":16067,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":18825,"get_count":4,"put_bytes":16729,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":19311,"get_count":4,"put_bytes":17215,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":19041,"get_count":4,"put_bytes":16945,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":19450,"get_count":4,"put_bytes":17354,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":20184,"get_count":4,"put_bytes":18088,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":20344,"get_count":4,"put_bytes":18248,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":20752,"get_count":4,"put_bytes":18656,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":20735,"get_count":4,"put_bytes":18639,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":21470,"get_count":4,"put_bytes":19374,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":21380,"get_count":4,"put_bytes":19284,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":22039,"get_count":4,"put_bytes":19943,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":22450,"get_count":4,"put_bytes":20354,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":23185,"get_count":4,"put_bytes":21089,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":23845,"get_count":4,"put_bytes":21749,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":24003,"get_count":4,"put_bytes":21907,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":24411,"get_count":4,"put_bytes":22315,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":24820,"get_count":4,"put_bytes":22724,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":25333,"get_count":4,"put_bytes":23237,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":25740,"get_count":4,"put_bytes":23644,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":25752,"get_count":4,"put_bytes":23656,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":26485,"get_count":4,"put_bytes":24389,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":26896,"get_count":4,"put_bytes":24800,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":27377,"get_count":4,"put_bytes":25281,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":27860,"get_count":4,"put_bytes":25764,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":28270,"get_count":4,"put_bytes":26174,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":28503,"get_count":4,"put_bytes":26407,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":28739,"get_count":4,"put_bytes":26643,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":28823,"get_count":4,"put_bytes":26727,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":29229,"get_count":4,"put_bytes":27133,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":29386,"get_count":4,"put_bytes":27290,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":29796,"get_count":4,"put_bytes":27700,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":29703,"get_count":4,"put_bytes":27607,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":29715,"get_count":4,"put_bytes":27619,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":29728,"get_count":4,"put_bytes":27632,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":30063,"get_count":4,"put_bytes":27967,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":30075,"get_count":4,"put_bytes":27979,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":29910,"get_count":4,"put_bytes":27814,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":30173,"get_count":4,"put_bytes":28077,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":30540,"get_count":4,"put_bytes":28444,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":30625,"get_count":4,"put_bytes":28529,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":30460,"get_count":4,"put_bytes":28364,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":30221,"get_count":4,"put_bytes":28125,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":29983,"get_count":4,"put_bytes":27887,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":29640,"get_count":4,"put_bytes":27544,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":29577,"get_count":4,"put_bytes":27481,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":29588,"get_count":4,"put_bytes":27492,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":29668,"get_count":5,"put_bytes":27572,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":30003,"get_count":5,"put_bytes":27907,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":29618,"get_count":5,"put_bytes":27522,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":29483,"get_count":5,"put_bytes":27387,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":29244,"get_count":5,"put_bytes":27148,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":28828,"get_count":5,"put_bytes":26732,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":28517,"get_count":5,"put_bytes":26421,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":28559,"get_count":5,"put_bytes":26463,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":28925,"get_count":5,"put_bytes":26829,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":28436,"get_count":5,"put_bytes":26340,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":27728,"get_count":5,"put_bytes":25632,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":27624,"get_count":5,"put_bytes":25528,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":27634,"get_count":5,"put_bytes":25538,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":27499,"get_count":5,"put_bytes":25403,"put_count":3}} +{"i":84,"series":2,"stats":{"get_bytes":27938,"get_count":5,"put_bytes":25842,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":27950,"get_count":5,"put_bytes":25854,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":27670,"get_count":5,"put_bytes":25574,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":27784,"get_count":5,"put_bytes":25688,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":27577,"get_count":5,"put_bytes":25481,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":27192,"get_count":5,"put_bytes":25096,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":26984,"get_count":5,"put_bytes":24888,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":26599,"get_count":5,"put_bytes":24503,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":26038,"get_count":5,"put_bytes":23942,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":25902,"get_count":5,"put_bytes":23806,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":26164,"get_count":5,"put_bytes":24068,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":26030,"get_count":5,"put_bytes":23934,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":26468,"get_count":5,"put_bytes":24372,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":26479,"get_count":5,"put_bytes":24383,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":26271,"get_count":5,"put_bytes":24175,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":26313,"get_count":5,"put_bytes":24217,"put_count":3}} diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index 731cb84e2..b13197609 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2211,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2208,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2233,"get_count":4,"put_bytes":138,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2213,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2210,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index e6b2f5a5c..5728edb94 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2185,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":180,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2275,"get_count":4,"put_bytes":261,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2356,"get_count":4,"put_bytes":345,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2440,"get_count":4,"put_bytes":419,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2514,"get_count":4,"put_bytes":493,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2588,"get_count":4,"put_bytes":567,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2662,"get_count":4,"put_bytes":641,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2736,"get_count":4,"put_bytes":715,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2810,"get_count":4,"put_bytes":789,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2884,"get_count":4,"put_bytes":863,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2958,"get_count":4,"put_bytes":937,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":3032,"get_count":4,"put_bytes":1011,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":3106,"get_count":4,"put_bytes":1085,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":3180,"get_count":4,"put_bytes":1159,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":3254,"get_count":4,"put_bytes":1233,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":3328,"get_count":4,"put_bytes":1308,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":3403,"get_count":4,"put_bytes":1383,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":3478,"get_count":4,"put_bytes":1458,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":3553,"get_count":4,"put_bytes":1533,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":3628,"get_count":4,"put_bytes":1607,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":3702,"get_count":4,"put_bytes":1682,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":3777,"get_count":4,"put_bytes":1757,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":3852,"get_count":4,"put_bytes":1831,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1907,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":4002,"get_count":4,"put_bytes":1982,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":4077,"get_count":4,"put_bytes":2057,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":4152,"get_count":4,"put_bytes":2132,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":4227,"get_count":4,"put_bytes":2207,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":4302,"get_count":4,"put_bytes":2282,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2357,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":4452,"get_count":4,"put_bytes":2432,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":4527,"get_count":4,"put_bytes":2506,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":4601,"get_count":4,"put_bytes":2581,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":4676,"get_count":4,"put_bytes":2656,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":4751,"get_count":4,"put_bytes":2731,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":4826,"get_count":4,"put_bytes":2805,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":4900,"get_count":4,"put_bytes":2880,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":4975,"get_count":4,"put_bytes":2955,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":5050,"get_count":4,"put_bytes":3030,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":5125,"get_count":4,"put_bytes":3105,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":5200,"get_count":4,"put_bytes":3180,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":5275,"get_count":4,"put_bytes":3255,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":5350,"get_count":4,"put_bytes":3330,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":5425,"get_count":4,"put_bytes":3404,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":5499,"get_count":4,"put_bytes":3479,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":5574,"get_count":4,"put_bytes":3554,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":5649,"get_count":4,"put_bytes":3629,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":5724,"get_count":4,"put_bytes":3704,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":5799,"get_count":4,"put_bytes":3779,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":5874,"get_count":4,"put_bytes":3854,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":5949,"get_count":4,"put_bytes":3929,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":6024,"get_count":4,"put_bytes":4004,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":6099,"get_count":4,"put_bytes":4079,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":6174,"get_count":4,"put_bytes":4154,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":6249,"get_count":4,"put_bytes":4229,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":6324,"get_count":4,"put_bytes":4304,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":6399,"get_count":4,"put_bytes":4379,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":6474,"get_count":4,"put_bytes":4454,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":6549,"get_count":4,"put_bytes":4529,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":6624,"get_count":4,"put_bytes":4603,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":6698,"get_count":4,"put_bytes":4677,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":6772,"get_count":4,"put_bytes":4752,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":6847,"get_count":4,"put_bytes":4827,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":6922,"get_count":4,"put_bytes":4902,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":6997,"get_count":4,"put_bytes":4977,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":7072,"get_count":4,"put_bytes":5052,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":7147,"get_count":4,"put_bytes":5126,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":7221,"get_count":4,"put_bytes":5201,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":7296,"get_count":4,"put_bytes":5276,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":7371,"get_count":4,"put_bytes":5351,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":7446,"get_count":4,"put_bytes":5426,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":7521,"get_count":4,"put_bytes":5501,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":7596,"get_count":4,"put_bytes":5576,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":7671,"get_count":4,"put_bytes":5650,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":7745,"get_count":4,"put_bytes":5724,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":7819,"get_count":4,"put_bytes":5799,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":7894,"get_count":4,"put_bytes":5873,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":7968,"get_count":4,"put_bytes":5947,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":8042,"get_count":4,"put_bytes":6022,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":8117,"get_count":4,"put_bytes":6097,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":8192,"get_count":4,"put_bytes":6171,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":8266,"get_count":4,"put_bytes":6246,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":8341,"get_count":4,"put_bytes":6321,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":8416,"get_count":4,"put_bytes":6396,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":8491,"get_count":4,"put_bytes":6470,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":8565,"get_count":4,"put_bytes":6544,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":8639,"get_count":4,"put_bytes":6618,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":8713,"get_count":4,"put_bytes":6692,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":8787,"get_count":4,"put_bytes":6767,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":8862,"get_count":4,"put_bytes":6842,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":8937,"get_count":4,"put_bytes":6916,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":9011,"get_count":4,"put_bytes":6990,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":9085,"get_count":4,"put_bytes":7064,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":9159,"get_count":4,"put_bytes":7139,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":9234,"get_count":4,"put_bytes":7214,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":9309,"get_count":4,"put_bytes":7288,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":9383,"get_count":4,"put_bytes":7362,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":9457,"get_count":4,"put_bytes":7437,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":9532,"get_count":4,"put_bytes":7511,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":87,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":9606,"get_count":4,"put_bytes":7585,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":9680,"get_count":4,"put_bytes":7658,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":9753,"get_count":4,"put_bytes":7732,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":9827,"get_count":4,"put_bytes":7806,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":9901,"get_count":4,"put_bytes":7879,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":9974,"get_count":4,"put_bytes":7952,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":10047,"get_count":4,"put_bytes":8026,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":10121,"get_count":4,"put_bytes":8100,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":10195,"get_count":4,"put_bytes":8174,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":10269,"get_count":4,"put_bytes":8247,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":10342,"get_count":4,"put_bytes":8320,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":10415,"get_count":4,"put_bytes":8394,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":10489,"get_count":4,"put_bytes":8468,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":10563,"get_count":4,"put_bytes":8541,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":10636,"get_count":4,"put_bytes":8614,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":10709,"get_count":4,"put_bytes":8689,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":10784,"get_count":4,"put_bytes":8764,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":10859,"get_count":4,"put_bytes":8839,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":10934,"get_count":4,"put_bytes":8914,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":11009,"get_count":4,"put_bytes":8989,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":11084,"get_count":4,"put_bytes":9064,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":11159,"get_count":4,"put_bytes":9138,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":11233,"get_count":4,"put_bytes":9213,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":11308,"get_count":4,"put_bytes":9287,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":11382,"get_count":4,"put_bytes":9362,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":11457,"get_count":4,"put_bytes":9437,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":11532,"get_count":4,"put_bytes":9511,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":11606,"get_count":4,"put_bytes":9586,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":11681,"get_count":4,"put_bytes":9660,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":11755,"get_count":4,"put_bytes":9735,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":11830,"get_count":4,"put_bytes":9809,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":11904,"get_count":4,"put_bytes":9883,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":11978,"get_count":4,"put_bytes":9957,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":12052,"get_count":4,"put_bytes":10031,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":12126,"get_count":4,"put_bytes":10105,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":12200,"get_count":4,"put_bytes":10179,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":12274,"get_count":4,"put_bytes":10254,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":12349,"get_count":4,"put_bytes":10329,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":12424,"get_count":4,"put_bytes":10403,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":12498,"get_count":4,"put_bytes":10477,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":12572,"get_count":4,"put_bytes":10552,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":12647,"get_count":4,"put_bytes":10627,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":12722,"get_count":4,"put_bytes":10702,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":12797,"get_count":4,"put_bytes":10777,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":12872,"get_count":4,"put_bytes":10852,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":12947,"get_count":4,"put_bytes":10926,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":13021,"get_count":4,"put_bytes":11000,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":13095,"get_count":4,"put_bytes":11074,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":13169,"get_count":4,"put_bytes":11149,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":13244,"get_count":4,"put_bytes":11223,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":13318,"get_count":4,"put_bytes":11297,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":13392,"get_count":4,"put_bytes":11371,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":13466,"get_count":4,"put_bytes":11446,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":13541,"get_count":4,"put_bytes":11521,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":13616,"get_count":4,"put_bytes":11596,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":13691,"get_count":4,"put_bytes":11671,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":13766,"get_count":4,"put_bytes":11745,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":13840,"get_count":4,"put_bytes":11820,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":13915,"get_count":4,"put_bytes":11895,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":13990,"get_count":4,"put_bytes":11969,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":14064,"get_count":4,"put_bytes":12043,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":14138,"get_count":4,"put_bytes":12118,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":14213,"get_count":4,"put_bytes":12192,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":14287,"get_count":4,"put_bytes":12266,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":14361,"get_count":4,"put_bytes":12341,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":14436,"get_count":4,"put_bytes":12416,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":14511,"get_count":4,"put_bytes":12490,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":14585,"get_count":4,"put_bytes":12564,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":14659,"get_count":4,"put_bytes":12639,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":14734,"get_count":4,"put_bytes":12789,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":14554,"get_count":4,"put_bytes":12533,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":14628,"get_count":4,"put_bytes":12608,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":14703,"get_count":4,"put_bytes":12682,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":14777,"get_count":4,"put_bytes":12756,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":14851,"get_count":4,"put_bytes":12830,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":14925,"get_count":4,"put_bytes":12985,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":14746,"get_count":4,"put_bytes":12726,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":14821,"get_count":4,"put_bytes":12800,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":14895,"get_count":4,"put_bytes":12874,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":14969,"get_count":4,"put_bytes":12949,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":15374,"get_count":5,"put_bytes":13354,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":15044,"get_count":4,"put_bytes":13022,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":15117,"get_count":4,"put_bytes":13097,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":15192,"get_count":4,"put_bytes":13171,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":15266,"get_count":4,"put_bytes":13245,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":15340,"get_count":4,"put_bytes":13319,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":15414,"get_count":4,"put_bytes":13393,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":15488,"get_count":4,"put_bytes":13468,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":15563,"get_count":4,"put_bytes":13543,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":15638,"get_count":4,"put_bytes":13618,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":15713,"get_count":4,"put_bytes":13692,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":15787,"get_count":4,"put_bytes":13767,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":15862,"get_count":4,"put_bytes":13841,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":15936,"get_count":4,"put_bytes":13916,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":16011,"get_count":4,"put_bytes":13991,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":16086,"get_count":4,"put_bytes":14065,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":16160,"get_count":4,"put_bytes":14140,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":16235,"get_count":4,"put_bytes":14214,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":16643,"get_count":5,"put_bytes":14623,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2187,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":181,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2277,"get_count":4,"put_bytes":262,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2358,"get_count":4,"put_bytes":346,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2442,"get_count":4,"put_bytes":420,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2516,"get_count":4,"put_bytes":494,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2590,"get_count":4,"put_bytes":568,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2664,"get_count":4,"put_bytes":642,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2738,"get_count":4,"put_bytes":716,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2812,"get_count":4,"put_bytes":790,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2886,"get_count":4,"put_bytes":864,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2960,"get_count":4,"put_bytes":938,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":3034,"get_count":4,"put_bytes":1012,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":3108,"get_count":4,"put_bytes":1086,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":3182,"get_count":4,"put_bytes":1160,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":3256,"get_count":4,"put_bytes":1234,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":3330,"get_count":4,"put_bytes":1309,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":3405,"get_count":4,"put_bytes":1384,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":3480,"get_count":4,"put_bytes":1459,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":3555,"get_count":4,"put_bytes":1534,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":3630,"get_count":4,"put_bytes":1608,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":3704,"get_count":4,"put_bytes":1683,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":3779,"get_count":4,"put_bytes":1758,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":3854,"get_count":4,"put_bytes":1832,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":3928,"get_count":4,"put_bytes":1908,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":4004,"get_count":4,"put_bytes":1983,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":4079,"get_count":4,"put_bytes":2058,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":4154,"get_count":4,"put_bytes":2133,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":4229,"get_count":4,"put_bytes":2208,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":4304,"get_count":4,"put_bytes":2283,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":4379,"get_count":4,"put_bytes":2358,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":4454,"get_count":4,"put_bytes":2433,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2507,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":4603,"get_count":4,"put_bytes":2582,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":4678,"get_count":4,"put_bytes":2657,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":4753,"get_count":4,"put_bytes":2732,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":4828,"get_count":4,"put_bytes":2806,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":4902,"get_count":4,"put_bytes":2881,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":4977,"get_count":4,"put_bytes":2956,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":5052,"get_count":4,"put_bytes":3031,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":5127,"get_count":4,"put_bytes":3106,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":5202,"get_count":4,"put_bytes":3181,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":5277,"get_count":4,"put_bytes":3256,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":5352,"get_count":4,"put_bytes":3331,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":5427,"get_count":4,"put_bytes":3405,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":5501,"get_count":4,"put_bytes":3480,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":5576,"get_count":4,"put_bytes":3555,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":5651,"get_count":4,"put_bytes":3630,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":5726,"get_count":4,"put_bytes":3705,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":5801,"get_count":4,"put_bytes":3780,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":5876,"get_count":4,"put_bytes":3855,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":5951,"get_count":4,"put_bytes":3930,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":6026,"get_count":4,"put_bytes":4005,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":6101,"get_count":4,"put_bytes":4080,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":6176,"get_count":4,"put_bytes":4155,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":6251,"get_count":4,"put_bytes":4230,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":6326,"get_count":4,"put_bytes":4305,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":6401,"get_count":4,"put_bytes":4380,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":6476,"get_count":4,"put_bytes":4455,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":6551,"get_count":4,"put_bytes":4530,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":6626,"get_count":4,"put_bytes":4604,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":6700,"get_count":4,"put_bytes":4678,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":6774,"get_count":4,"put_bytes":4753,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":6849,"get_count":4,"put_bytes":4828,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":6924,"get_count":4,"put_bytes":4903,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":6999,"get_count":4,"put_bytes":4978,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":7074,"get_count":4,"put_bytes":5053,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":7149,"get_count":4,"put_bytes":5127,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":7223,"get_count":4,"put_bytes":5202,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":7298,"get_count":4,"put_bytes":5277,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":7373,"get_count":4,"put_bytes":5352,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":7448,"get_count":4,"put_bytes":5427,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":7523,"get_count":4,"put_bytes":5502,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":7598,"get_count":4,"put_bytes":5577,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":7673,"get_count":4,"put_bytes":5651,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":7747,"get_count":4,"put_bytes":5725,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":7821,"get_count":4,"put_bytes":5800,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":7896,"get_count":4,"put_bytes":5874,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":7970,"get_count":4,"put_bytes":5948,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":8044,"get_count":4,"put_bytes":6023,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":8119,"get_count":4,"put_bytes":6098,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":8194,"get_count":4,"put_bytes":6172,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":8268,"get_count":4,"put_bytes":6247,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":8343,"get_count":4,"put_bytes":6322,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":8418,"get_count":4,"put_bytes":6397,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":8493,"get_count":4,"put_bytes":6471,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":8567,"get_count":4,"put_bytes":6545,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":8641,"get_count":4,"put_bytes":6619,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":8715,"get_count":4,"put_bytes":6693,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":8789,"get_count":4,"put_bytes":6768,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":8864,"get_count":4,"put_bytes":6843,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":8939,"get_count":4,"put_bytes":6917,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":9013,"get_count":4,"put_bytes":6991,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":9087,"get_count":4,"put_bytes":7065,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":9161,"get_count":4,"put_bytes":7140,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":9236,"get_count":4,"put_bytes":7215,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":9311,"get_count":4,"put_bytes":7289,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":9385,"get_count":4,"put_bytes":7363,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":9459,"get_count":4,"put_bytes":7438,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":9534,"get_count":4,"put_bytes":7512,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":9608,"get_count":4,"put_bytes":88,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":9608,"get_count":4,"put_bytes":7586,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":9682,"get_count":4,"put_bytes":7659,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":9755,"get_count":4,"put_bytes":7733,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":9829,"get_count":4,"put_bytes":7807,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":9903,"get_count":4,"put_bytes":7880,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":9976,"get_count":4,"put_bytes":7953,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":10049,"get_count":4,"put_bytes":8027,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":10123,"get_count":4,"put_bytes":8101,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":10197,"get_count":4,"put_bytes":8175,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":10271,"get_count":4,"put_bytes":8248,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":10344,"get_count":4,"put_bytes":8321,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":10417,"get_count":4,"put_bytes":8395,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":10491,"get_count":4,"put_bytes":8469,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":10565,"get_count":4,"put_bytes":8542,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":10638,"get_count":4,"put_bytes":8615,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":10711,"get_count":4,"put_bytes":8690,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":10786,"get_count":4,"put_bytes":8765,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":10861,"get_count":4,"put_bytes":8840,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":10936,"get_count":4,"put_bytes":8915,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":11011,"get_count":4,"put_bytes":8990,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":11086,"get_count":4,"put_bytes":9065,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":11161,"get_count":4,"put_bytes":9139,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":11235,"get_count":4,"put_bytes":9214,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":11310,"get_count":4,"put_bytes":9288,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":11384,"get_count":4,"put_bytes":9363,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":11459,"get_count":4,"put_bytes":9438,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":11534,"get_count":4,"put_bytes":9512,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":11608,"get_count":4,"put_bytes":9587,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":11683,"get_count":4,"put_bytes":9661,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":11757,"get_count":4,"put_bytes":9736,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":11832,"get_count":4,"put_bytes":9810,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":11906,"get_count":4,"put_bytes":9884,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":11980,"get_count":4,"put_bytes":9958,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":12054,"get_count":4,"put_bytes":10032,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":12128,"get_count":4,"put_bytes":10106,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":12202,"get_count":4,"put_bytes":10180,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":12276,"get_count":4,"put_bytes":10255,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":12351,"get_count":4,"put_bytes":10330,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":12426,"get_count":4,"put_bytes":10404,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":12500,"get_count":4,"put_bytes":10478,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":12574,"get_count":4,"put_bytes":10553,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":12649,"get_count":4,"put_bytes":10628,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":12724,"get_count":4,"put_bytes":10703,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":12799,"get_count":4,"put_bytes":10778,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":12874,"get_count":4,"put_bytes":10853,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":12949,"get_count":4,"put_bytes":10927,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":13023,"get_count":4,"put_bytes":11001,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":13097,"get_count":4,"put_bytes":11075,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":13171,"get_count":4,"put_bytes":11150,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":13246,"get_count":4,"put_bytes":11224,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":13320,"get_count":4,"put_bytes":11298,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":13394,"get_count":4,"put_bytes":11372,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":13468,"get_count":4,"put_bytes":11447,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":13543,"get_count":4,"put_bytes":11522,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":13618,"get_count":4,"put_bytes":11597,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":13693,"get_count":4,"put_bytes":11672,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":13768,"get_count":4,"put_bytes":11746,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":13842,"get_count":4,"put_bytes":11821,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":13917,"get_count":4,"put_bytes":11896,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":13992,"get_count":4,"put_bytes":11970,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":14066,"get_count":4,"put_bytes":12044,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":14140,"get_count":4,"put_bytes":12119,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":14215,"get_count":4,"put_bytes":12193,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":14289,"get_count":4,"put_bytes":12267,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":14363,"get_count":4,"put_bytes":12342,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":14438,"get_count":4,"put_bytes":12417,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":14513,"get_count":4,"put_bytes":12491,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":14587,"get_count":4,"put_bytes":12565,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":14661,"get_count":4,"put_bytes":12640,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":14736,"get_count":4,"put_bytes":12790,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":14556,"get_count":4,"put_bytes":12534,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":14630,"get_count":4,"put_bytes":12609,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":14705,"get_count":4,"put_bytes":12683,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":14779,"get_count":4,"put_bytes":12757,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":14853,"get_count":4,"put_bytes":12831,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":14927,"get_count":4,"put_bytes":12986,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":14748,"get_count":4,"put_bytes":12727,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":14823,"get_count":4,"put_bytes":12801,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":14897,"get_count":4,"put_bytes":12875,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":14971,"get_count":4,"put_bytes":12950,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":15376,"get_count":5,"put_bytes":13355,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":15046,"get_count":4,"put_bytes":13023,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":15119,"get_count":4,"put_bytes":13098,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":15194,"get_count":4,"put_bytes":13172,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":15268,"get_count":4,"put_bytes":13246,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":15342,"get_count":4,"put_bytes":13320,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":15416,"get_count":4,"put_bytes":13394,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":15490,"get_count":4,"put_bytes":13469,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":15565,"get_count":4,"put_bytes":13544,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":15640,"get_count":4,"put_bytes":13619,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":15715,"get_count":4,"put_bytes":13693,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":15789,"get_count":4,"put_bytes":13768,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":15864,"get_count":4,"put_bytes":13842,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":15938,"get_count":4,"put_bytes":13917,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":16013,"get_count":4,"put_bytes":13992,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":16088,"get_count":4,"put_bytes":14066,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":16162,"get_count":4,"put_bytes":14141,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":16237,"get_count":4,"put_bytes":14215,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":16645,"get_count":5,"put_bytes":14624,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index 3dca70654..246091a4f 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":2185,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":4,"put_bytes":7494,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":9589,"get_count":4,"put_bytes":15001,"put_count":4}} -{"i":3,"series":1,"stats":{"get_bytes":16765,"get_count":5,"put_bytes":22582,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":22347,"get_count":6,"put_bytes":28124,"put_count":11}} -{"i":5,"series":1,"stats":{"get_bytes":29235,"get_count":10,"put_bytes":35299,"put_count":19}} -{"i":6,"series":1,"stats":{"get_bytes":34719,"get_count":16,"put_bytes":41819,"put_count":38}} -{"i":7,"series":1,"stats":{"get_bytes":36899,"get_count":20,"put_bytes":43687,"put_count":38}} -{"i":8,"series":1,"stats":{"get_bytes":42385,"get_count":35,"put_bytes":49554,"put_count":58}} -{"i":9,"series":1,"stats":{"get_bytes":42057,"get_count":36,"put_bytes":48656,"put_count":52}} -{"i":10,"series":1,"stats":{"get_bytes":45608,"get_count":43,"put_bytes":52209,"put_count":58}} -{"i":11,"series":1,"stats":{"get_bytes":46118,"get_count":45,"put_bytes":52740,"put_count":61}} -{"i":12,"series":1,"stats":{"get_bytes":48132,"get_count":53,"put_bytes":54913,"put_count":71}} -{"i":13,"series":1,"stats":{"get_bytes":45955,"get_count":52,"put_bytes":52796,"put_count":70}} -{"i":14,"series":1,"stats":{"get_bytes":52092,"get_count":71,"put_bytes":58491,"put_count":83}} -{"i":15,"series":1,"stats":{"get_bytes":55710,"get_count":76,"put_bytes":61485,"put_count":80}} -{"i":16,"series":1,"stats":{"get_bytes":54014,"get_count":72,"put_bytes":60324,"put_count":81}} -{"i":17,"series":1,"stats":{"get_bytes":54575,"get_count":75,"put_bytes":60610,"put_count":81}} -{"i":18,"series":1,"stats":{"get_bytes":57974,"get_count":78,"put_bytes":63687,"put_count":80}} -{"i":19,"series":1,"stats":{"get_bytes":64451,"get_count":84,"put_bytes":70094,"put_count":85}} -{"i":20,"series":1,"stats":{"get_bytes":59422,"get_count":76,"put_bytes":65192,"put_count":78}} -{"i":21,"series":1,"stats":{"get_bytes":70175,"get_count":87,"put_bytes":75865,"put_count":88}} -{"i":22,"series":1,"stats":{"get_bytes":65733,"get_count":81,"put_bytes":71319,"put_count":81}} -{"i":23,"series":1,"stats":{"get_bytes":71977,"get_count":85,"put_bytes":77487,"put_count":84}} -{"i":24,"series":1,"stats":{"get_bytes":70109,"get_count":83,"put_bytes":75767,"put_count":84}} -{"i":25,"series":1,"stats":{"get_bytes":69675,"get_count":76,"put_bytes":75356,"put_count":77}} -{"i":26,"series":1,"stats":{"get_bytes":77947,"get_count":86,"put_bytes":83538,"put_count":86}} -{"i":27,"series":1,"stats":{"get_bytes":78937,"get_count":88,"put_bytes":84387,"put_count":86}} -{"i":28,"series":1,"stats":{"get_bytes":86209,"get_count":92,"put_bytes":91635,"put_count":90}} -{"i":29,"series":1,"stats":{"get_bytes":83993,"get_count":85,"put_bytes":89425,"put_count":83}} -{"i":30,"series":1,"stats":{"get_bytes":90627,"get_count":91,"put_bytes":96122,"put_count":90}} -{"i":31,"series":1,"stats":{"get_bytes":91040,"get_count":88,"put_bytes":96441,"put_count":86}} -{"i":32,"series":1,"stats":{"get_bytes":89691,"get_count":89,"put_bytes":95166,"put_count":88}} -{"i":33,"series":1,"stats":{"get_bytes":91840,"get_count":85,"put_bytes":97241,"put_count":83}} -{"i":34,"series":1,"stats":{"get_bytes":91962,"get_count":81,"put_bytes":97364,"put_count":79}} -{"i":35,"series":1,"stats":{"get_bytes":99485,"get_count":90,"put_bytes":104889,"put_count":88}} -{"i":36,"series":1,"stats":{"get_bytes":95514,"get_count":83,"put_bytes":100916,"put_count":81}} -{"i":37,"series":1,"stats":{"get_bytes":107805,"get_count":88,"put_bytes":113208,"put_count":86}} -{"i":38,"series":1,"stats":{"get_bytes":106202,"get_count":88,"put_bytes":111610,"put_count":86}} -{"i":39,"series":1,"stats":{"get_bytes":104285,"get_count":84,"put_bytes":109681,"put_count":82}} -{"i":40,"series":1,"stats":{"get_bytes":109033,"get_count":86,"put_bytes":114421,"put_count":84}} -{"i":41,"series":1,"stats":{"get_bytes":105864,"get_count":82,"put_bytes":111278,"put_count":80}} -{"i":42,"series":1,"stats":{"get_bytes":108991,"get_count":85,"put_bytes":114411,"put_count":83}} -{"i":43,"series":1,"stats":{"get_bytes":114426,"get_count":86,"put_bytes":119836,"put_count":84}} -{"i":44,"series":1,"stats":{"get_bytes":121966,"get_count":87,"put_bytes":127367,"put_count":85}} -{"i":45,"series":1,"stats":{"get_bytes":125866,"get_count":86,"put_bytes":131291,"put_count":84}} -{"i":46,"series":1,"stats":{"get_bytes":117722,"get_count":80,"put_bytes":123126,"put_count":78}} -{"i":47,"series":1,"stats":{"get_bytes":125329,"get_count":85,"put_bytes":130743,"put_count":83}} -{"i":48,"series":1,"stats":{"get_bytes":126394,"get_count":84,"put_bytes":131795,"put_count":82}} -{"i":49,"series":1,"stats":{"get_bytes":134371,"get_count":88,"put_bytes":139778,"put_count":86}} -{"i":50,"series":1,"stats":{"get_bytes":141144,"get_count":91,"put_bytes":146542,"put_count":89}} -{"i":51,"series":1,"stats":{"get_bytes":137743,"get_count":86,"put_bytes":143144,"put_count":84}} -{"i":52,"series":1,"stats":{"get_bytes":136344,"get_count":85,"put_bytes":141754,"put_count":83}} -{"i":53,"series":1,"stats":{"get_bytes":144905,"get_count":90,"put_bytes":150319,"put_count":88}} -{"i":54,"series":1,"stats":{"get_bytes":140411,"get_count":86,"put_bytes":145801,"put_count":84}} -{"i":55,"series":1,"stats":{"get_bytes":142972,"get_count":88,"put_bytes":148366,"put_count":86}} -{"i":56,"series":1,"stats":{"get_bytes":146418,"get_count":87,"put_bytes":151821,"put_count":85}} -{"i":57,"series":1,"stats":{"get_bytes":164396,"get_count":91,"put_bytes":169803,"put_count":89}} -{"i":58,"series":1,"stats":{"get_bytes":149656,"get_count":85,"put_bytes":155057,"put_count":83}} -{"i":59,"series":1,"stats":{"get_bytes":162591,"get_count":91,"put_bytes":167988,"put_count":89}} -{"i":60,"series":1,"stats":{"get_bytes":156642,"get_count":86,"put_bytes":162050,"put_count":84}} -{"i":61,"series":1,"stats":{"get_bytes":158418,"get_count":84,"put_bytes":163827,"put_count":82}} -{"i":62,"series":1,"stats":{"get_bytes":173638,"get_count":94,"put_bytes":179035,"put_count":92}} -{"i":63,"series":1,"stats":{"get_bytes":168876,"get_count":88,"put_bytes":174275,"put_count":86}} -{"i":64,"series":1,"stats":{"get_bytes":169959,"get_count":87,"put_bytes":175366,"put_count":85}} -{"i":65,"series":1,"stats":{"get_bytes":175244,"get_count":86,"put_bytes":180634,"put_count":84}} -{"i":66,"series":1,"stats":{"get_bytes":182458,"get_count":90,"put_bytes":187869,"put_count":88}} -{"i":67,"series":1,"stats":{"get_bytes":184118,"get_count":89,"put_bytes":189519,"put_count":87}} -{"i":68,"series":1,"stats":{"get_bytes":175682,"get_count":89,"put_bytes":181088,"put_count":87}} -{"i":69,"series":1,"stats":{"get_bytes":150359,"get_count":74,"put_bytes":155765,"put_count":72}} -{"i":70,"series":1,"stats":{"get_bytes":169348,"get_count":81,"put_bytes":174835,"put_count":80}} -{"i":71,"series":1,"stats":{"get_bytes":176815,"get_count":85,"put_bytes":182220,"put_count":83}} -{"i":72,"series":1,"stats":{"get_bytes":179781,"get_count":83,"put_bytes":185179,"put_count":81}} -{"i":73,"series":1,"stats":{"get_bytes":204850,"get_count":93,"put_bytes":210251,"put_count":91}} -{"i":74,"series":1,"stats":{"get_bytes":180951,"get_count":86,"put_bytes":186338,"put_count":84}} -{"i":75,"series":1,"stats":{"get_bytes":182832,"get_count":82,"put_bytes":188225,"put_count":80}} -{"i":76,"series":1,"stats":{"get_bytes":190178,"get_count":86,"put_bytes":195585,"put_count":84}} -{"i":77,"series":1,"stats":{"get_bytes":197388,"get_count":84,"put_bytes":202781,"put_count":82}} -{"i":78,"series":1,"stats":{"get_bytes":201793,"get_count":87,"put_bytes":207183,"put_count":85}} -{"i":79,"series":1,"stats":{"get_bytes":207205,"get_count":88,"put_bytes":212672,"put_count":87}} -{"i":80,"series":1,"stats":{"get_bytes":207794,"get_count":89,"put_bytes":213191,"put_count":87}} -{"i":81,"series":1,"stats":{"get_bytes":217720,"get_count":91,"put_bytes":223116,"put_count":89}} -{"i":82,"series":1,"stats":{"get_bytes":206077,"get_count":84,"put_bytes":211466,"put_count":82}} -{"i":83,"series":1,"stats":{"get_bytes":231542,"get_count":94,"put_bytes":236934,"put_count":92}} -{"i":84,"series":1,"stats":{"get_bytes":212690,"get_count":83,"put_bytes":218083,"put_count":81}} -{"i":85,"series":1,"stats":{"get_bytes":224796,"get_count":88,"put_bytes":230179,"put_count":86}} -{"i":86,"series":1,"stats":{"get_bytes":224059,"get_count":88,"put_bytes":229445,"put_count":86}} -{"i":87,"series":1,"stats":{"get_bytes":210313,"get_count":83,"put_bytes":215703,"put_count":81}} -{"i":88,"series":1,"stats":{"get_bytes":225382,"get_count":83,"put_bytes":230772,"put_count":81}} -{"i":89,"series":1,"stats":{"get_bytes":215945,"get_count":84,"put_bytes":221328,"put_count":82}} -{"i":90,"series":1,"stats":{"get_bytes":223357,"get_count":86,"put_bytes":228748,"put_count":84}} -{"i":91,"series":1,"stats":{"get_bytes":233105,"get_count":87,"put_bytes":238496,"put_count":85}} -{"i":92,"series":1,"stats":{"get_bytes":231913,"get_count":85,"put_bytes":237300,"put_count":83}} -{"i":93,"series":1,"stats":{"get_bytes":250888,"get_count":91,"put_bytes":256277,"put_count":89}} -{"i":94,"series":1,"stats":{"get_bytes":244010,"get_count":89,"put_bytes":249392,"put_count":87}} -{"i":95,"series":1,"stats":{"get_bytes":241102,"get_count":88,"put_bytes":246485,"put_count":86}} -{"i":96,"series":1,"stats":{"get_bytes":259402,"get_count":92,"put_bytes":264789,"put_count":90}} -{"i":97,"series":1,"stats":{"get_bytes":254250,"get_count":88,"put_bytes":259638,"put_count":86}} -{"i":98,"series":1,"stats":{"get_bytes":245365,"get_count":83,"put_bytes":250747,"put_count":81}} -{"i":99,"series":1,"stats":{"get_bytes":265670,"get_count":91,"put_bytes":271053,"put_count":89}} -{"i":0,"series":2,"stats":{"get_bytes":262559,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":256304,"get_count":86,"put_bytes":261594,"put_count":84}} -{"i":2,"series":2,"stats":{"get_bytes":259549,"get_count":88,"put_bytes":264832,"put_count":86}} -{"i":3,"series":2,"stats":{"get_bytes":258453,"get_count":87,"put_bytes":263745,"put_count":85}} -{"i":4,"series":2,"stats":{"get_bytes":262085,"get_count":86,"put_bytes":267370,"put_count":84}} -{"i":5,"series":2,"stats":{"get_bytes":264600,"get_count":88,"put_bytes":269892,"put_count":86}} -{"i":6,"series":2,"stats":{"get_bytes":289344,"get_count":94,"put_bytes":294620,"put_count":92}} -{"i":7,"series":2,"stats":{"get_bytes":257470,"get_count":85,"put_bytes":262762,"put_count":83}} -{"i":8,"series":2,"stats":{"get_bytes":283634,"get_count":91,"put_bytes":288925,"put_count":89}} -{"i":9,"series":2,"stats":{"get_bytes":272719,"get_count":87,"put_bytes":278004,"put_count":85}} -{"i":10,"series":2,"stats":{"get_bytes":277843,"get_count":87,"put_bytes":283132,"put_count":85}} -{"i":11,"series":2,"stats":{"get_bytes":286400,"get_count":89,"put_bytes":291691,"put_count":87}} -{"i":12,"series":2,"stats":{"get_bytes":289965,"get_count":88,"put_bytes":295245,"put_count":86}} -{"i":13,"series":2,"stats":{"get_bytes":291491,"get_count":89,"put_bytes":296779,"put_count":87}} -{"i":14,"series":2,"stats":{"get_bytes":287482,"get_count":87,"put_bytes":292767,"put_count":85}} -{"i":15,"series":2,"stats":{"get_bytes":290065,"get_count":86,"put_bytes":295349,"put_count":84}} -{"i":16,"series":2,"stats":{"get_bytes":286312,"get_count":85,"put_bytes":291691,"put_count":83}} -{"i":17,"series":2,"stats":{"get_bytes":302281,"get_count":90,"put_bytes":307663,"put_count":88}} -{"i":18,"series":2,"stats":{"get_bytes":294303,"get_count":86,"put_bytes":299696,"put_count":84}} -{"i":19,"series":2,"stats":{"get_bytes":296316,"get_count":86,"put_bytes":301712,"put_count":84}} -{"i":20,"series":2,"stats":{"get_bytes":325914,"get_count":94,"put_bytes":331291,"put_count":92}} -{"i":21,"series":2,"stats":{"get_bytes":312436,"get_count":90,"put_bytes":317823,"put_count":88}} -{"i":22,"series":2,"stats":{"get_bytes":315108,"get_count":88,"put_bytes":320570,"put_count":87}} -{"i":23,"series":2,"stats":{"get_bytes":287991,"get_count":82,"put_bytes":293377,"put_count":80}} -{"i":24,"series":2,"stats":{"get_bytes":287424,"get_count":82,"put_bytes":292805,"put_count":80}} -{"i":25,"series":2,"stats":{"get_bytes":326188,"get_count":91,"put_bytes":331570,"put_count":89}} -{"i":26,"series":2,"stats":{"get_bytes":323939,"get_count":88,"put_bytes":329314,"put_count":86}} -{"i":27,"series":2,"stats":{"get_bytes":295325,"get_count":81,"put_bytes":300705,"put_count":79}} -{"i":28,"series":2,"stats":{"get_bytes":330729,"get_count":90,"put_bytes":336114,"put_count":88}} -{"i":29,"series":2,"stats":{"get_bytes":286893,"get_count":79,"put_bytes":292279,"put_count":77}} -{"i":30,"series":2,"stats":{"get_bytes":338412,"get_count":91,"put_bytes":343781,"put_count":89}} -{"i":31,"series":2,"stats":{"get_bytes":332854,"get_count":87,"put_bytes":338230,"put_count":85}} -{"i":32,"series":2,"stats":{"get_bytes":324105,"get_count":85,"put_bytes":329486,"put_count":83}} -{"i":33,"series":2,"stats":{"get_bytes":333118,"get_count":86,"put_bytes":338491,"put_count":84}} -{"i":34,"series":2,"stats":{"get_bytes":345411,"get_count":89,"put_bytes":350784,"put_count":87}} -{"i":35,"series":2,"stats":{"get_bytes":335646,"get_count":87,"put_bytes":341033,"put_count":85}} -{"i":36,"series":2,"stats":{"get_bytes":342220,"get_count":87,"put_bytes":347600,"put_count":85}} -{"i":37,"series":2,"stats":{"get_bytes":335888,"get_count":85,"put_bytes":341271,"put_count":83}} -{"i":38,"series":2,"stats":{"get_bytes":354870,"get_count":90,"put_bytes":360253,"put_count":88}} -{"i":39,"series":2,"stats":{"get_bytes":362562,"get_count":89,"put_bytes":367943,"put_count":87}} -{"i":40,"series":2,"stats":{"get_bytes":361173,"get_count":89,"put_bytes":366620,"put_count":88}} -{"i":41,"series":2,"stats":{"get_bytes":378647,"get_count":93,"put_bytes":384028,"put_count":91}} -{"i":42,"series":2,"stats":{"get_bytes":331756,"get_count":83,"put_bytes":337127,"put_count":81}} -{"i":43,"series":2,"stats":{"get_bytes":349359,"get_count":87,"put_bytes":354736,"put_count":85}} -{"i":44,"series":2,"stats":{"get_bytes":372794,"get_count":90,"put_bytes":378178,"put_count":88}} -{"i":45,"series":2,"stats":{"get_bytes":339895,"get_count":80,"put_bytes":345269,"put_count":78}} -{"i":46,"series":2,"stats":{"get_bytes":349793,"get_count":84,"put_bytes":355172,"put_count":82}} -{"i":47,"series":2,"stats":{"get_bytes":391126,"get_count":93,"put_bytes":396503,"put_count":91}} -{"i":48,"series":2,"stats":{"get_bytes":393747,"get_count":93,"put_bytes":399125,"put_count":91}} -{"i":49,"series":2,"stats":{"get_bytes":359497,"get_count":85,"put_bytes":364880,"put_count":83}} -{"i":50,"series":2,"stats":{"get_bytes":335354,"get_count":78,"put_bytes":340737,"put_count":76}} -{"i":51,"series":2,"stats":{"get_bytes":377428,"get_count":88,"put_bytes":382807,"put_count":86}} -{"i":52,"series":2,"stats":{"get_bytes":397377,"get_count":91,"put_bytes":402757,"put_count":89}} -{"i":53,"series":2,"stats":{"get_bytes":377648,"get_count":86,"put_bytes":383023,"put_count":84}} -{"i":54,"series":2,"stats":{"get_bytes":378144,"get_count":85,"put_bytes":383521,"put_count":83}} -{"i":55,"series":2,"stats":{"get_bytes":402297,"get_count":92,"put_bytes":407666,"put_count":90}} -{"i":56,"series":2,"stats":{"get_bytes":409357,"get_count":91,"put_bytes":414731,"put_count":89}} -{"i":57,"series":2,"stats":{"get_bytes":383876,"get_count":87,"put_bytes":389247,"put_count":85}} -{"i":58,"series":2,"stats":{"get_bytes":382651,"get_count":84,"put_bytes":388027,"put_count":82}} -{"i":59,"series":2,"stats":{"get_bytes":390398,"get_count":85,"put_bytes":395780,"put_count":83}} -{"i":60,"series":2,"stats":{"get_bytes":391327,"get_count":86,"put_bytes":396703,"put_count":84}} -{"i":61,"series":2,"stats":{"get_bytes":403559,"get_count":87,"put_bytes":408938,"put_count":85}} -{"i":62,"series":2,"stats":{"get_bytes":398461,"get_count":86,"put_bytes":403841,"put_count":84}} -{"i":63,"series":2,"stats":{"get_bytes":413241,"get_count":89,"put_bytes":418611,"put_count":87}} -{"i":64,"series":2,"stats":{"get_bytes":414009,"get_count":89,"put_bytes":419390,"put_count":87}} -{"i":65,"series":2,"stats":{"get_bytes":411165,"get_count":86,"put_bytes":416534,"put_count":84}} -{"i":66,"series":2,"stats":{"get_bytes":434970,"get_count":91,"put_bytes":440353,"put_count":89}} -{"i":67,"series":2,"stats":{"get_bytes":445201,"get_count":92,"put_bytes":450574,"put_count":90}} -{"i":68,"series":2,"stats":{"get_bytes":430937,"get_count":90,"put_bytes":436314,"put_count":88}} -{"i":69,"series":2,"stats":{"get_bytes":429730,"get_count":89,"put_bytes":435104,"put_count":87}} -{"i":70,"series":2,"stats":{"get_bytes":433862,"get_count":89,"put_bytes":439244,"put_count":87}} -{"i":71,"series":2,"stats":{"get_bytes":410758,"get_count":86,"put_bytes":416140,"put_count":84}} -{"i":72,"series":2,"stats":{"get_bytes":448697,"get_count":92,"put_bytes":454070,"put_count":90}} -{"i":73,"series":2,"stats":{"get_bytes":411359,"get_count":84,"put_bytes":416880,"put_count":84}} -{"i":74,"series":2,"stats":{"get_bytes":428625,"get_count":87,"put_bytes":434006,"put_count":85}} -{"i":75,"series":2,"stats":{"get_bytes":410158,"get_count":83,"put_bytes":415529,"put_count":81}} -{"i":76,"series":2,"stats":{"get_bytes":459400,"get_count":92,"put_bytes":464775,"put_count":90}} -{"i":77,"series":2,"stats":{"get_bytes":468934,"get_count":94,"put_bytes":474370,"put_count":93}} -{"i":78,"series":2,"stats":{"get_bytes":454825,"get_count":90,"put_bytes":460202,"put_count":88}} -{"i":79,"series":2,"stats":{"get_bytes":459668,"get_count":90,"put_bytes":465047,"put_count":88}} -{"i":80,"series":2,"stats":{"get_bytes":442113,"get_count":87,"put_bytes":447557,"put_count":86}} -{"i":81,"series":2,"stats":{"get_bytes":443786,"get_count":86,"put_bytes":449157,"put_count":84}} -{"i":82,"series":2,"stats":{"get_bytes":465035,"get_count":88,"put_bytes":470400,"put_count":86}} -{"i":83,"series":2,"stats":{"get_bytes":445039,"get_count":85,"put_bytes":450418,"put_count":83}} -{"i":84,"series":2,"stats":{"get_bytes":465888,"get_count":89,"put_bytes":471412,"put_count":89}} -{"i":85,"series":2,"stats":{"get_bytes":483185,"get_count":91,"put_bytes":488566,"put_count":89}} -{"i":86,"series":2,"stats":{"get_bytes":508765,"get_count":95,"put_bytes":514221,"put_count":94}} -{"i":87,"series":2,"stats":{"get_bytes":481159,"get_count":90,"put_bytes":486596,"put_count":89}} -{"i":88,"series":2,"stats":{"get_bytes":465605,"get_count":86,"put_bytes":470971,"put_count":84}} -{"i":89,"series":2,"stats":{"get_bytes":456384,"get_count":84,"put_bytes":461844,"put_count":83}} -{"i":90,"series":2,"stats":{"get_bytes":474727,"get_count":87,"put_bytes":480100,"put_count":85}} -{"i":91,"series":2,"stats":{"get_bytes":488491,"get_count":91,"put_bytes":493871,"put_count":89}} -{"i":92,"series":2,"stats":{"get_bytes":453904,"get_count":84,"put_bytes":459278,"put_count":82}} -{"i":93,"series":2,"stats":{"get_bytes":484358,"get_count":90,"put_bytes":489888,"put_count":90}} -{"i":94,"series":2,"stats":{"get_bytes":512672,"get_count":91,"put_bytes":518107,"put_count":90}} -{"i":95,"series":2,"stats":{"get_bytes":457983,"get_count":84,"put_bytes":463361,"put_count":82}} -{"i":96,"series":2,"stats":{"get_bytes":484698,"get_count":88,"put_bytes":490068,"put_count":86}} -{"i":97,"series":2,"stats":{"get_bytes":511434,"get_count":91,"put_bytes":516878,"put_count":90}} -{"i":98,"series":2,"stats":{"get_bytes":464298,"get_count":83,"put_bytes":469728,"put_count":82}} -{"i":99,"series":2,"stats":{"get_bytes":473466,"get_count":84,"put_bytes":478926,"put_count":83}} +{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2187,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":7495,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":9591,"get_count":4,"put_bytes":15002,"put_count":4}} +{"i":3,"series":1,"stats":{"get_bytes":16767,"get_count":5,"put_bytes":22583,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":22349,"get_count":6,"put_bytes":28125,"put_count":11}} +{"i":5,"series":1,"stats":{"get_bytes":29237,"get_count":10,"put_bytes":35300,"put_count":19}} +{"i":6,"series":1,"stats":{"get_bytes":34721,"get_count":16,"put_bytes":41820,"put_count":38}} +{"i":7,"series":1,"stats":{"get_bytes":36901,"get_count":20,"put_bytes":43688,"put_count":38}} +{"i":8,"series":1,"stats":{"get_bytes":42387,"get_count":35,"put_bytes":49555,"put_count":58}} +{"i":9,"series":1,"stats":{"get_bytes":42059,"get_count":36,"put_bytes":48657,"put_count":52}} +{"i":10,"series":1,"stats":{"get_bytes":45610,"get_count":43,"put_bytes":52210,"put_count":58}} +{"i":11,"series":1,"stats":{"get_bytes":46120,"get_count":45,"put_bytes":52741,"put_count":61}} +{"i":12,"series":1,"stats":{"get_bytes":48134,"get_count":53,"put_bytes":54914,"put_count":71}} +{"i":13,"series":1,"stats":{"get_bytes":45957,"get_count":52,"put_bytes":52797,"put_count":70}} +{"i":14,"series":1,"stats":{"get_bytes":52094,"get_count":71,"put_bytes":58492,"put_count":83}} +{"i":15,"series":1,"stats":{"get_bytes":55712,"get_count":76,"put_bytes":61486,"put_count":80}} +{"i":16,"series":1,"stats":{"get_bytes":54016,"get_count":72,"put_bytes":60325,"put_count":81}} +{"i":17,"series":1,"stats":{"get_bytes":54577,"get_count":75,"put_bytes":60611,"put_count":81}} +{"i":18,"series":1,"stats":{"get_bytes":57976,"get_count":78,"put_bytes":63688,"put_count":80}} +{"i":19,"series":1,"stats":{"get_bytes":64453,"get_count":84,"put_bytes":70095,"put_count":85}} +{"i":20,"series":1,"stats":{"get_bytes":59424,"get_count":76,"put_bytes":65193,"put_count":78}} +{"i":21,"series":1,"stats":{"get_bytes":70177,"get_count":87,"put_bytes":75866,"put_count":88}} +{"i":22,"series":1,"stats":{"get_bytes":65735,"get_count":81,"put_bytes":71320,"put_count":81}} +{"i":23,"series":1,"stats":{"get_bytes":71979,"get_count":85,"put_bytes":77488,"put_count":84}} +{"i":24,"series":1,"stats":{"get_bytes":70111,"get_count":83,"put_bytes":75768,"put_count":84}} +{"i":25,"series":1,"stats":{"get_bytes":69677,"get_count":76,"put_bytes":75357,"put_count":77}} +{"i":26,"series":1,"stats":{"get_bytes":77949,"get_count":86,"put_bytes":83539,"put_count":86}} +{"i":27,"series":1,"stats":{"get_bytes":78939,"get_count":88,"put_bytes":84388,"put_count":86}} +{"i":28,"series":1,"stats":{"get_bytes":86211,"get_count":92,"put_bytes":91636,"put_count":90}} +{"i":29,"series":1,"stats":{"get_bytes":83995,"get_count":85,"put_bytes":89426,"put_count":83}} +{"i":30,"series":1,"stats":{"get_bytes":90629,"get_count":91,"put_bytes":96123,"put_count":90}} +{"i":31,"series":1,"stats":{"get_bytes":91042,"get_count":88,"put_bytes":96442,"put_count":86}} +{"i":32,"series":1,"stats":{"get_bytes":89693,"get_count":89,"put_bytes":95167,"put_count":88}} +{"i":33,"series":1,"stats":{"get_bytes":91842,"get_count":85,"put_bytes":97242,"put_count":83}} +{"i":34,"series":1,"stats":{"get_bytes":91964,"get_count":81,"put_bytes":97365,"put_count":79}} +{"i":35,"series":1,"stats":{"get_bytes":99487,"get_count":90,"put_bytes":104890,"put_count":88}} +{"i":36,"series":1,"stats":{"get_bytes":95516,"get_count":83,"put_bytes":100917,"put_count":81}} +{"i":37,"series":1,"stats":{"get_bytes":107807,"get_count":88,"put_bytes":113209,"put_count":86}} +{"i":38,"series":1,"stats":{"get_bytes":106204,"get_count":88,"put_bytes":111611,"put_count":86}} +{"i":39,"series":1,"stats":{"get_bytes":104287,"get_count":84,"put_bytes":109682,"put_count":82}} +{"i":40,"series":1,"stats":{"get_bytes":109035,"get_count":86,"put_bytes":114422,"put_count":84}} +{"i":41,"series":1,"stats":{"get_bytes":105866,"get_count":82,"put_bytes":111279,"put_count":80}} +{"i":42,"series":1,"stats":{"get_bytes":108993,"get_count":85,"put_bytes":114412,"put_count":83}} +{"i":43,"series":1,"stats":{"get_bytes":114428,"get_count":86,"put_bytes":119837,"put_count":84}} +{"i":44,"series":1,"stats":{"get_bytes":121968,"get_count":87,"put_bytes":127368,"put_count":85}} +{"i":45,"series":1,"stats":{"get_bytes":125868,"get_count":86,"put_bytes":131292,"put_count":84}} +{"i":46,"series":1,"stats":{"get_bytes":117724,"get_count":80,"put_bytes":123127,"put_count":78}} +{"i":47,"series":1,"stats":{"get_bytes":125331,"get_count":85,"put_bytes":130744,"put_count":83}} +{"i":48,"series":1,"stats":{"get_bytes":126396,"get_count":84,"put_bytes":131796,"put_count":82}} +{"i":49,"series":1,"stats":{"get_bytes":134373,"get_count":88,"put_bytes":139779,"put_count":86}} +{"i":50,"series":1,"stats":{"get_bytes":141146,"get_count":91,"put_bytes":146543,"put_count":89}} +{"i":51,"series":1,"stats":{"get_bytes":137745,"get_count":86,"put_bytes":143145,"put_count":84}} +{"i":52,"series":1,"stats":{"get_bytes":136346,"get_count":85,"put_bytes":141755,"put_count":83}} +{"i":53,"series":1,"stats":{"get_bytes":144907,"get_count":90,"put_bytes":150320,"put_count":88}} +{"i":54,"series":1,"stats":{"get_bytes":140413,"get_count":86,"put_bytes":145802,"put_count":84}} +{"i":55,"series":1,"stats":{"get_bytes":142974,"get_count":88,"put_bytes":148367,"put_count":86}} +{"i":56,"series":1,"stats":{"get_bytes":146420,"get_count":87,"put_bytes":151822,"put_count":85}} +{"i":57,"series":1,"stats":{"get_bytes":164398,"get_count":91,"put_bytes":169804,"put_count":89}} +{"i":58,"series":1,"stats":{"get_bytes":149658,"get_count":85,"put_bytes":155058,"put_count":83}} +{"i":59,"series":1,"stats":{"get_bytes":162593,"get_count":91,"put_bytes":167989,"put_count":89}} +{"i":60,"series":1,"stats":{"get_bytes":156644,"get_count":86,"put_bytes":162051,"put_count":84}} +{"i":61,"series":1,"stats":{"get_bytes":158420,"get_count":84,"put_bytes":163828,"put_count":82}} +{"i":62,"series":1,"stats":{"get_bytes":173640,"get_count":94,"put_bytes":179036,"put_count":92}} +{"i":63,"series":1,"stats":{"get_bytes":168878,"get_count":88,"put_bytes":174276,"put_count":86}} +{"i":64,"series":1,"stats":{"get_bytes":169961,"get_count":87,"put_bytes":175367,"put_count":85}} +{"i":65,"series":1,"stats":{"get_bytes":175246,"get_count":86,"put_bytes":180635,"put_count":84}} +{"i":66,"series":1,"stats":{"get_bytes":182460,"get_count":90,"put_bytes":187870,"put_count":88}} +{"i":67,"series":1,"stats":{"get_bytes":184120,"get_count":89,"put_bytes":189520,"put_count":87}} +{"i":68,"series":1,"stats":{"get_bytes":175684,"get_count":89,"put_bytes":181089,"put_count":87}} +{"i":69,"series":1,"stats":{"get_bytes":150361,"get_count":74,"put_bytes":155766,"put_count":72}} +{"i":70,"series":1,"stats":{"get_bytes":169350,"get_count":81,"put_bytes":174836,"put_count":80}} +{"i":71,"series":1,"stats":{"get_bytes":176817,"get_count":85,"put_bytes":182221,"put_count":83}} +{"i":72,"series":1,"stats":{"get_bytes":179783,"get_count":83,"put_bytes":185180,"put_count":81}} +{"i":73,"series":1,"stats":{"get_bytes":204852,"get_count":93,"put_bytes":210252,"put_count":91}} +{"i":74,"series":1,"stats":{"get_bytes":180953,"get_count":86,"put_bytes":186339,"put_count":84}} +{"i":75,"series":1,"stats":{"get_bytes":182834,"get_count":82,"put_bytes":188226,"put_count":80}} +{"i":76,"series":1,"stats":{"get_bytes":190180,"get_count":86,"put_bytes":195586,"put_count":84}} +{"i":77,"series":1,"stats":{"get_bytes":197390,"get_count":84,"put_bytes":202782,"put_count":82}} +{"i":78,"series":1,"stats":{"get_bytes":201795,"get_count":87,"put_bytes":207184,"put_count":85}} +{"i":79,"series":1,"stats":{"get_bytes":207207,"get_count":88,"put_bytes":212673,"put_count":87}} +{"i":80,"series":1,"stats":{"get_bytes":207796,"get_count":89,"put_bytes":213192,"put_count":87}} +{"i":81,"series":1,"stats":{"get_bytes":217722,"get_count":91,"put_bytes":223117,"put_count":89}} +{"i":82,"series":1,"stats":{"get_bytes":206079,"get_count":84,"put_bytes":211467,"put_count":82}} +{"i":83,"series":1,"stats":{"get_bytes":231544,"get_count":94,"put_bytes":236935,"put_count":92}} +{"i":84,"series":1,"stats":{"get_bytes":212692,"get_count":83,"put_bytes":218084,"put_count":81}} +{"i":85,"series":1,"stats":{"get_bytes":224798,"get_count":88,"put_bytes":230180,"put_count":86}} +{"i":86,"series":1,"stats":{"get_bytes":224061,"get_count":88,"put_bytes":229446,"put_count":86}} +{"i":87,"series":1,"stats":{"get_bytes":210315,"get_count":83,"put_bytes":215704,"put_count":81}} +{"i":88,"series":1,"stats":{"get_bytes":225384,"get_count":83,"put_bytes":230773,"put_count":81}} +{"i":89,"series":1,"stats":{"get_bytes":215947,"get_count":84,"put_bytes":221329,"put_count":82}} +{"i":90,"series":1,"stats":{"get_bytes":223359,"get_count":86,"put_bytes":228749,"put_count":84}} +{"i":91,"series":1,"stats":{"get_bytes":233107,"get_count":87,"put_bytes":238497,"put_count":85}} +{"i":92,"series":1,"stats":{"get_bytes":231915,"get_count":85,"put_bytes":237301,"put_count":83}} +{"i":93,"series":1,"stats":{"get_bytes":250890,"get_count":91,"put_bytes":256278,"put_count":89}} +{"i":94,"series":1,"stats":{"get_bytes":244012,"get_count":89,"put_bytes":249393,"put_count":87}} +{"i":95,"series":1,"stats":{"get_bytes":241104,"get_count":88,"put_bytes":246486,"put_count":86}} +{"i":96,"series":1,"stats":{"get_bytes":259404,"get_count":92,"put_bytes":264790,"put_count":90}} +{"i":97,"series":1,"stats":{"get_bytes":254252,"get_count":88,"put_bytes":259639,"put_count":86}} +{"i":98,"series":1,"stats":{"get_bytes":245367,"get_count":83,"put_bytes":250748,"put_count":81}} +{"i":99,"series":1,"stats":{"get_bytes":265672,"get_count":91,"put_bytes":271054,"put_count":89}} +{"i":0,"series":2,"stats":{"get_bytes":262561,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":256306,"get_count":86,"put_bytes":261595,"put_count":84}} +{"i":2,"series":2,"stats":{"get_bytes":259551,"get_count":88,"put_bytes":264833,"put_count":86}} +{"i":3,"series":2,"stats":{"get_bytes":258455,"get_count":87,"put_bytes":263746,"put_count":85}} +{"i":4,"series":2,"stats":{"get_bytes":262087,"get_count":86,"put_bytes":267371,"put_count":84}} +{"i":5,"series":2,"stats":{"get_bytes":264602,"get_count":88,"put_bytes":269893,"put_count":86}} +{"i":6,"series":2,"stats":{"get_bytes":289346,"get_count":94,"put_bytes":294621,"put_count":92}} +{"i":7,"series":2,"stats":{"get_bytes":257472,"get_count":85,"put_bytes":262763,"put_count":83}} +{"i":8,"series":2,"stats":{"get_bytes":283636,"get_count":91,"put_bytes":288926,"put_count":89}} +{"i":9,"series":2,"stats":{"get_bytes":272721,"get_count":87,"put_bytes":278005,"put_count":85}} +{"i":10,"series":2,"stats":{"get_bytes":277845,"get_count":87,"put_bytes":283133,"put_count":85}} +{"i":11,"series":2,"stats":{"get_bytes":286402,"get_count":89,"put_bytes":291692,"put_count":87}} +{"i":12,"series":2,"stats":{"get_bytes":289967,"get_count":88,"put_bytes":295246,"put_count":86}} +{"i":13,"series":2,"stats":{"get_bytes":291493,"get_count":89,"put_bytes":296780,"put_count":87}} +{"i":14,"series":2,"stats":{"get_bytes":287484,"get_count":87,"put_bytes":292768,"put_count":85}} +{"i":15,"series":2,"stats":{"get_bytes":290067,"get_count":86,"put_bytes":295350,"put_count":84}} +{"i":16,"series":2,"stats":{"get_bytes":286314,"get_count":85,"put_bytes":291692,"put_count":83}} +{"i":17,"series":2,"stats":{"get_bytes":302283,"get_count":90,"put_bytes":307664,"put_count":88}} +{"i":18,"series":2,"stats":{"get_bytes":294305,"get_count":86,"put_bytes":299697,"put_count":84}} +{"i":19,"series":2,"stats":{"get_bytes":296318,"get_count":86,"put_bytes":301713,"put_count":84}} +{"i":20,"series":2,"stats":{"get_bytes":325916,"get_count":94,"put_bytes":331292,"put_count":92}} +{"i":21,"series":2,"stats":{"get_bytes":312438,"get_count":90,"put_bytes":317824,"put_count":88}} +{"i":22,"series":2,"stats":{"get_bytes":315110,"get_count":88,"put_bytes":320571,"put_count":87}} +{"i":23,"series":2,"stats":{"get_bytes":287993,"get_count":82,"put_bytes":293378,"put_count":80}} +{"i":24,"series":2,"stats":{"get_bytes":287426,"get_count":82,"put_bytes":292806,"put_count":80}} +{"i":25,"series":2,"stats":{"get_bytes":326190,"get_count":91,"put_bytes":331571,"put_count":89}} +{"i":26,"series":2,"stats":{"get_bytes":323941,"get_count":88,"put_bytes":329315,"put_count":86}} +{"i":27,"series":2,"stats":{"get_bytes":295327,"get_count":81,"put_bytes":300706,"put_count":79}} +{"i":28,"series":2,"stats":{"get_bytes":330731,"get_count":90,"put_bytes":336115,"put_count":88}} +{"i":29,"series":2,"stats":{"get_bytes":286895,"get_count":79,"put_bytes":292280,"put_count":77}} +{"i":30,"series":2,"stats":{"get_bytes":338414,"get_count":91,"put_bytes":343782,"put_count":89}} +{"i":31,"series":2,"stats":{"get_bytes":332856,"get_count":87,"put_bytes":338231,"put_count":85}} +{"i":32,"series":2,"stats":{"get_bytes":324107,"get_count":85,"put_bytes":329487,"put_count":83}} +{"i":33,"series":2,"stats":{"get_bytes":333120,"get_count":86,"put_bytes":338492,"put_count":84}} +{"i":34,"series":2,"stats":{"get_bytes":345413,"get_count":89,"put_bytes":350785,"put_count":87}} +{"i":35,"series":2,"stats":{"get_bytes":335648,"get_count":87,"put_bytes":341034,"put_count":85}} +{"i":36,"series":2,"stats":{"get_bytes":342222,"get_count":87,"put_bytes":347601,"put_count":85}} +{"i":37,"series":2,"stats":{"get_bytes":335890,"get_count":85,"put_bytes":341272,"put_count":83}} +{"i":38,"series":2,"stats":{"get_bytes":354872,"get_count":90,"put_bytes":360254,"put_count":88}} +{"i":39,"series":2,"stats":{"get_bytes":362564,"get_count":89,"put_bytes":367944,"put_count":87}} +{"i":40,"series":2,"stats":{"get_bytes":361175,"get_count":89,"put_bytes":366621,"put_count":88}} +{"i":41,"series":2,"stats":{"get_bytes":378649,"get_count":93,"put_bytes":384029,"put_count":91}} +{"i":42,"series":2,"stats":{"get_bytes":331758,"get_count":83,"put_bytes":337128,"put_count":81}} +{"i":43,"series":2,"stats":{"get_bytes":349361,"get_count":87,"put_bytes":354737,"put_count":85}} +{"i":44,"series":2,"stats":{"get_bytes":372796,"get_count":90,"put_bytes":378179,"put_count":88}} +{"i":45,"series":2,"stats":{"get_bytes":339897,"get_count":80,"put_bytes":345270,"put_count":78}} +{"i":46,"series":2,"stats":{"get_bytes":349795,"get_count":84,"put_bytes":355173,"put_count":82}} +{"i":47,"series":2,"stats":{"get_bytes":391128,"get_count":93,"put_bytes":396504,"put_count":91}} +{"i":48,"series":2,"stats":{"get_bytes":393749,"get_count":93,"put_bytes":399126,"put_count":91}} +{"i":49,"series":2,"stats":{"get_bytes":359499,"get_count":85,"put_bytes":364881,"put_count":83}} +{"i":50,"series":2,"stats":{"get_bytes":335356,"get_count":78,"put_bytes":340738,"put_count":76}} +{"i":51,"series":2,"stats":{"get_bytes":377430,"get_count":88,"put_bytes":382808,"put_count":86}} +{"i":52,"series":2,"stats":{"get_bytes":397379,"get_count":91,"put_bytes":402758,"put_count":89}} +{"i":53,"series":2,"stats":{"get_bytes":377650,"get_count":86,"put_bytes":383024,"put_count":84}} +{"i":54,"series":2,"stats":{"get_bytes":378146,"get_count":85,"put_bytes":383522,"put_count":83}} +{"i":55,"series":2,"stats":{"get_bytes":402299,"get_count":92,"put_bytes":407667,"put_count":90}} +{"i":56,"series":2,"stats":{"get_bytes":409359,"get_count":91,"put_bytes":414732,"put_count":89}} +{"i":57,"series":2,"stats":{"get_bytes":383878,"get_count":87,"put_bytes":389248,"put_count":85}} +{"i":58,"series":2,"stats":{"get_bytes":382653,"get_count":84,"put_bytes":388028,"put_count":82}} +{"i":59,"series":2,"stats":{"get_bytes":390400,"get_count":85,"put_bytes":395781,"put_count":83}} +{"i":60,"series":2,"stats":{"get_bytes":391329,"get_count":86,"put_bytes":396704,"put_count":84}} +{"i":61,"series":2,"stats":{"get_bytes":403561,"get_count":87,"put_bytes":408939,"put_count":85}} +{"i":62,"series":2,"stats":{"get_bytes":398463,"get_count":86,"put_bytes":403842,"put_count":84}} +{"i":63,"series":2,"stats":{"get_bytes":413243,"get_count":89,"put_bytes":418612,"put_count":87}} +{"i":64,"series":2,"stats":{"get_bytes":414011,"get_count":89,"put_bytes":419391,"put_count":87}} +{"i":65,"series":2,"stats":{"get_bytes":411167,"get_count":86,"put_bytes":416535,"put_count":84}} +{"i":66,"series":2,"stats":{"get_bytes":434972,"get_count":91,"put_bytes":440354,"put_count":89}} +{"i":67,"series":2,"stats":{"get_bytes":445203,"get_count":92,"put_bytes":450575,"put_count":90}} +{"i":68,"series":2,"stats":{"get_bytes":430939,"get_count":90,"put_bytes":436315,"put_count":88}} +{"i":69,"series":2,"stats":{"get_bytes":429732,"get_count":89,"put_bytes":435105,"put_count":87}} +{"i":70,"series":2,"stats":{"get_bytes":433864,"get_count":89,"put_bytes":439245,"put_count":87}} +{"i":71,"series":2,"stats":{"get_bytes":410760,"get_count":86,"put_bytes":416141,"put_count":84}} +{"i":72,"series":2,"stats":{"get_bytes":448699,"get_count":92,"put_bytes":454071,"put_count":90}} +{"i":73,"series":2,"stats":{"get_bytes":411361,"get_count":84,"put_bytes":416881,"put_count":84}} +{"i":74,"series":2,"stats":{"get_bytes":428627,"get_count":87,"put_bytes":434007,"put_count":85}} +{"i":75,"series":2,"stats":{"get_bytes":410160,"get_count":83,"put_bytes":415530,"put_count":81}} +{"i":76,"series":2,"stats":{"get_bytes":459402,"get_count":92,"put_bytes":464776,"put_count":90}} +{"i":77,"series":2,"stats":{"get_bytes":468936,"get_count":94,"put_bytes":474371,"put_count":93}} +{"i":78,"series":2,"stats":{"get_bytes":454827,"get_count":90,"put_bytes":460203,"put_count":88}} +{"i":79,"series":2,"stats":{"get_bytes":459670,"get_count":90,"put_bytes":465048,"put_count":88}} +{"i":80,"series":2,"stats":{"get_bytes":442115,"get_count":87,"put_bytes":447558,"put_count":86}} +{"i":81,"series":2,"stats":{"get_bytes":443788,"get_count":86,"put_bytes":449158,"put_count":84}} +{"i":82,"series":2,"stats":{"get_bytes":465037,"get_count":88,"put_bytes":470401,"put_count":86}} +{"i":83,"series":2,"stats":{"get_bytes":445041,"get_count":85,"put_bytes":450419,"put_count":83}} +{"i":84,"series":2,"stats":{"get_bytes":465890,"get_count":89,"put_bytes":471413,"put_count":89}} +{"i":85,"series":2,"stats":{"get_bytes":483187,"get_count":91,"put_bytes":488567,"put_count":89}} +{"i":86,"series":2,"stats":{"get_bytes":508767,"get_count":95,"put_bytes":514222,"put_count":94}} +{"i":87,"series":2,"stats":{"get_bytes":481161,"get_count":90,"put_bytes":486597,"put_count":89}} +{"i":88,"series":2,"stats":{"get_bytes":465607,"get_count":86,"put_bytes":470972,"put_count":84}} +{"i":89,"series":2,"stats":{"get_bytes":456386,"get_count":84,"put_bytes":461845,"put_count":83}} +{"i":90,"series":2,"stats":{"get_bytes":474729,"get_count":87,"put_bytes":480101,"put_count":85}} +{"i":91,"series":2,"stats":{"get_bytes":488493,"get_count":91,"put_bytes":493872,"put_count":89}} +{"i":92,"series":2,"stats":{"get_bytes":453906,"get_count":84,"put_bytes":459279,"put_count":82}} +{"i":93,"series":2,"stats":{"get_bytes":484360,"get_count":90,"put_bytes":489889,"put_count":90}} +{"i":94,"series":2,"stats":{"get_bytes":512674,"get_count":91,"put_bytes":518108,"put_count":90}} +{"i":95,"series":2,"stats":{"get_bytes":457985,"get_count":84,"put_bytes":463362,"put_count":82}} +{"i":96,"series":2,"stats":{"get_bytes":484700,"get_count":88,"put_bytes":490069,"put_count":86}} +{"i":97,"series":2,"stats":{"get_bytes":511436,"get_count":91,"put_bytes":516879,"put_count":90}} +{"i":98,"series":2,"stats":{"get_bytes":464300,"get_count":83,"put_bytes":469729,"put_count":82}} +{"i":99,"series":2,"stats":{"get_bytes":473468,"get_count":84,"put_bytes":478927,"put_count":83}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index 8ef30b107..82338efe9 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":27514,"put_count":10}} -{"i":1,"series":1,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":27865,"put_count":11}} -{"i":2,"series":1,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":26841,"put_count":8}} -{"i":3,"series":1,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":27915,"put_count":9}} -{"i":4,"series":1,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":29060,"put_count":12}} -{"i":5,"series":1,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":26553,"put_count":8}} -{"i":6,"series":1,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":29124,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":27335,"put_count":9}} -{"i":8,"series":1,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":28412,"put_count":11}} -{"i":9,"series":1,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":28564,"put_count":11}} -{"i":10,"series":1,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":27544,"put_count":10}} -{"i":11,"series":1,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":28292,"put_count":10}} -{"i":12,"series":1,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":27301,"put_count":9}} -{"i":13,"series":1,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":28397,"put_count":11}} -{"i":14,"series":1,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":28852,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":28469,"put_count":10}} -{"i":16,"series":1,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":27613,"put_count":9}} -{"i":17,"series":1,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":27573,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":26700,"put_count":9}} -{"i":19,"series":1,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":27910,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":28543,"put_count":12}} -{"i":21,"series":1,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":27484,"put_count":10}} -{"i":22,"series":1,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":28044,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":27154,"put_count":9}} -{"i":24,"series":1,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":27729,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":28262,"put_count":11}} -{"i":26,"series":1,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":28864,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":28281,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":27039,"put_count":8}} -{"i":29,"series":1,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":28217,"put_count":9}} -{"i":30,"series":1,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":27386,"put_count":9}} -{"i":31,"series":1,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":26667,"put_count":7}} -{"i":32,"series":1,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":26689,"put_count":8}} -{"i":33,"series":1,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":29135,"put_count":11}} -{"i":34,"series":1,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":27730,"put_count":10}} -{"i":35,"series":1,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":27593,"put_count":9}} -{"i":36,"series":1,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":26305,"put_count":8}} -{"i":37,"series":1,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":26381,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":26904,"put_count":8}} -{"i":39,"series":1,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":27586,"put_count":8}} -{"i":40,"series":1,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":28116,"put_count":11}} -{"i":41,"series":1,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":28315,"put_count":10}} -{"i":42,"series":1,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":27942,"put_count":10}} -{"i":43,"series":1,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":28307,"put_count":10}} -{"i":44,"series":1,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":26451,"put_count":8}} -{"i":45,"series":1,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":25968,"put_count":6}} -{"i":46,"series":1,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":26768,"put_count":8}} -{"i":47,"series":1,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":27113,"put_count":8}} -{"i":48,"series":1,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":26766,"put_count":8}} -{"i":49,"series":1,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":27768,"put_count":9}} -{"i":50,"series":1,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":25589,"put_count":6}} -{"i":51,"series":1,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":27258,"put_count":9}} -{"i":52,"series":1,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":26919,"put_count":8}} -{"i":53,"series":1,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":27953,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":25734,"put_count":6}} -{"i":55,"series":1,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":27941,"put_count":10}} -{"i":56,"series":1,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":27976,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":27712,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":28023,"put_count":10}} -{"i":59,"series":1,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":28255,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":28496,"put_count":11}} -{"i":61,"series":1,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":27367,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":27394,"put_count":9}} -{"i":63,"series":1,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":27501,"put_count":10}} -{"i":64,"series":1,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":27789,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":28339,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":27257,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":27560,"put_count":10}} -{"i":68,"series":1,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":28259,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":27113,"put_count":9}} -{"i":70,"series":1,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":27770,"put_count":9}} -{"i":71,"series":1,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":26373,"put_count":7}} -{"i":72,"series":1,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":29067,"put_count":11}} -{"i":73,"series":1,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":27529,"put_count":9}} -{"i":74,"series":1,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":27947,"put_count":10}} -{"i":75,"series":1,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":27792,"put_count":10}} -{"i":76,"series":1,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":27897,"put_count":9}} -{"i":77,"series":1,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":27703,"put_count":9}} -{"i":78,"series":1,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":27045,"put_count":7}} -{"i":79,"series":1,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":27620,"put_count":9}} -{"i":80,"series":1,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":28670,"put_count":12}} -{"i":81,"series":1,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":28145,"put_count":10}} -{"i":82,"series":1,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":26903,"put_count":8}} -{"i":83,"series":1,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":27754,"put_count":9}} -{"i":84,"series":1,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":25882,"put_count":6}} -{"i":85,"series":1,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":26450,"put_count":8}} -{"i":86,"series":1,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":28702,"put_count":11}} -{"i":87,"series":1,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":28210,"put_count":11}} -{"i":88,"series":1,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":27851,"put_count":10}} -{"i":89,"series":1,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":27493,"put_count":10}} -{"i":90,"series":1,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":28326,"put_count":11}} -{"i":91,"series":1,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":27496,"put_count":10}} -{"i":92,"series":1,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":27010,"put_count":9}} -{"i":93,"series":1,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":27242,"put_count":9}} -{"i":94,"series":1,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":26799,"put_count":7}} -{"i":95,"series":1,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":27441,"put_count":9}} -{"i":96,"series":1,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":26195,"put_count":6}} -{"i":97,"series":1,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":26333,"put_count":7}} -{"i":98,"series":1,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":27600,"put_count":9}} -{"i":99,"series":1,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":27547,"put_count":9}} -{"i":0,"series":2,"stats":{"get_bytes":29609,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":29960,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":2,"series":2,"stats":{"get_bytes":28936,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":3,"series":2,"stats":{"get_bytes":30010,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":4,"series":2,"stats":{"get_bytes":31155,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":5,"series":2,"stats":{"get_bytes":28648,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":6,"series":2,"stats":{"get_bytes":31219,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":7,"series":2,"stats":{"get_bytes":29430,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":8,"series":2,"stats":{"get_bytes":30507,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":9,"series":2,"stats":{"get_bytes":30659,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":10,"series":2,"stats":{"get_bytes":29639,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":11,"series":2,"stats":{"get_bytes":30387,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":12,"series":2,"stats":{"get_bytes":29396,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":13,"series":2,"stats":{"get_bytes":30492,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":14,"series":2,"stats":{"get_bytes":30947,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":15,"series":2,"stats":{"get_bytes":30564,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":16,"series":2,"stats":{"get_bytes":29708,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":17,"series":2,"stats":{"get_bytes":29668,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":18,"series":2,"stats":{"get_bytes":28795,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":19,"series":2,"stats":{"get_bytes":30005,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":20,"series":2,"stats":{"get_bytes":30638,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":21,"series":2,"stats":{"get_bytes":29579,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":22,"series":2,"stats":{"get_bytes":30139,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":23,"series":2,"stats":{"get_bytes":29249,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":24,"series":2,"stats":{"get_bytes":29824,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":25,"series":2,"stats":{"get_bytes":30357,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":26,"series":2,"stats":{"get_bytes":30959,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":27,"series":2,"stats":{"get_bytes":30376,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":28,"series":2,"stats":{"get_bytes":29134,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":29,"series":2,"stats":{"get_bytes":30312,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":30,"series":2,"stats":{"get_bytes":29481,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":31,"series":2,"stats":{"get_bytes":28762,"get_count":9,"put_bytes":87,"put_count":1}} -{"i":32,"series":2,"stats":{"get_bytes":28784,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":33,"series":2,"stats":{"get_bytes":31230,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":34,"series":2,"stats":{"get_bytes":29825,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":35,"series":2,"stats":{"get_bytes":29688,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":36,"series":2,"stats":{"get_bytes":28400,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":37,"series":2,"stats":{"get_bytes":28476,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":38,"series":2,"stats":{"get_bytes":28999,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":39,"series":2,"stats":{"get_bytes":29681,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":40,"series":2,"stats":{"get_bytes":30211,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":41,"series":2,"stats":{"get_bytes":30410,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":42,"series":2,"stats":{"get_bytes":30037,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":43,"series":2,"stats":{"get_bytes":30402,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":44,"series":2,"stats":{"get_bytes":28546,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":45,"series":2,"stats":{"get_bytes":28063,"get_count":8,"put_bytes":87,"put_count":1}} -{"i":46,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":47,"series":2,"stats":{"get_bytes":29208,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":48,"series":2,"stats":{"get_bytes":28861,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":49,"series":2,"stats":{"get_bytes":29863,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":50,"series":2,"stats":{"get_bytes":27684,"get_count":8,"put_bytes":87,"put_count":1}} -{"i":51,"series":2,"stats":{"get_bytes":29353,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":52,"series":2,"stats":{"get_bytes":29014,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":53,"series":2,"stats":{"get_bytes":30048,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":54,"series":2,"stats":{"get_bytes":27829,"get_count":8,"put_bytes":87,"put_count":1}} -{"i":55,"series":2,"stats":{"get_bytes":30036,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":56,"series":2,"stats":{"get_bytes":30071,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":57,"series":2,"stats":{"get_bytes":29807,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":58,"series":2,"stats":{"get_bytes":30118,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":59,"series":2,"stats":{"get_bytes":30350,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":60,"series":2,"stats":{"get_bytes":30591,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":61,"series":2,"stats":{"get_bytes":29462,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":62,"series":2,"stats":{"get_bytes":29489,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":63,"series":2,"stats":{"get_bytes":29596,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":64,"series":2,"stats":{"get_bytes":29884,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":65,"series":2,"stats":{"get_bytes":30434,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":66,"series":2,"stats":{"get_bytes":29352,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":67,"series":2,"stats":{"get_bytes":29655,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":68,"series":2,"stats":{"get_bytes":30354,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":69,"series":2,"stats":{"get_bytes":29208,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":70,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":71,"series":2,"stats":{"get_bytes":28468,"get_count":9,"put_bytes":87,"put_count":1}} -{"i":72,"series":2,"stats":{"get_bytes":31162,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":73,"series":2,"stats":{"get_bytes":29624,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":74,"series":2,"stats":{"get_bytes":30042,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":75,"series":2,"stats":{"get_bytes":29887,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":76,"series":2,"stats":{"get_bytes":29992,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":77,"series":2,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":78,"series":2,"stats":{"get_bytes":29140,"get_count":9,"put_bytes":87,"put_count":1}} -{"i":79,"series":2,"stats":{"get_bytes":29715,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":80,"series":2,"stats":{"get_bytes":30765,"get_count":14,"put_bytes":87,"put_count":1}} -{"i":81,"series":2,"stats":{"get_bytes":30240,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":82,"series":2,"stats":{"get_bytes":28998,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":83,"series":2,"stats":{"get_bytes":29849,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":84,"series":2,"stats":{"get_bytes":27977,"get_count":8,"put_bytes":87,"put_count":1}} -{"i":85,"series":2,"stats":{"get_bytes":28545,"get_count":10,"put_bytes":87,"put_count":1}} -{"i":86,"series":2,"stats":{"get_bytes":30797,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":87,"series":2,"stats":{"get_bytes":30305,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":88,"series":2,"stats":{"get_bytes":29946,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":89,"series":2,"stats":{"get_bytes":29588,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":90,"series":2,"stats":{"get_bytes":30421,"get_count":13,"put_bytes":87,"put_count":1}} -{"i":91,"series":2,"stats":{"get_bytes":29591,"get_count":12,"put_bytes":87,"put_count":1}} -{"i":92,"series":2,"stats":{"get_bytes":29105,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":93,"series":2,"stats":{"get_bytes":29337,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":94,"series":2,"stats":{"get_bytes":28894,"get_count":9,"put_bytes":87,"put_count":1}} -{"i":95,"series":2,"stats":{"get_bytes":29536,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":96,"series":2,"stats":{"get_bytes":28290,"get_count":8,"put_bytes":87,"put_count":1}} -{"i":97,"series":2,"stats":{"get_bytes":28428,"get_count":9,"put_bytes":87,"put_count":1}} -{"i":98,"series":2,"stats":{"get_bytes":29695,"get_count":11,"put_bytes":87,"put_count":1}} -{"i":99,"series":2,"stats":{"get_bytes":29642,"get_count":11,"put_bytes":87,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":29611,"get_count":12,"put_bytes":27515,"put_count":10}} +{"i":1,"series":1,"stats":{"get_bytes":29962,"get_count":13,"put_bytes":27866,"put_count":11}} +{"i":2,"series":1,"stats":{"get_bytes":28938,"get_count":10,"put_bytes":26842,"put_count":8}} +{"i":3,"series":1,"stats":{"get_bytes":30012,"get_count":11,"put_bytes":27916,"put_count":9}} +{"i":4,"series":1,"stats":{"get_bytes":31157,"get_count":14,"put_bytes":29061,"put_count":12}} +{"i":5,"series":1,"stats":{"get_bytes":28650,"get_count":10,"put_bytes":26554,"put_count":8}} +{"i":6,"series":1,"stats":{"get_bytes":31221,"get_count":13,"put_bytes":29125,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":29432,"get_count":11,"put_bytes":27336,"put_count":9}} +{"i":8,"series":1,"stats":{"get_bytes":30509,"get_count":13,"put_bytes":28413,"put_count":11}} +{"i":9,"series":1,"stats":{"get_bytes":30661,"get_count":13,"put_bytes":28565,"put_count":11}} +{"i":10,"series":1,"stats":{"get_bytes":29641,"get_count":12,"put_bytes":27545,"put_count":10}} +{"i":11,"series":1,"stats":{"get_bytes":30389,"get_count":12,"put_bytes":28293,"put_count":10}} +{"i":12,"series":1,"stats":{"get_bytes":29398,"get_count":11,"put_bytes":27302,"put_count":9}} +{"i":13,"series":1,"stats":{"get_bytes":30494,"get_count":13,"put_bytes":28398,"put_count":11}} +{"i":14,"series":1,"stats":{"get_bytes":30949,"get_count":13,"put_bytes":28853,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":30566,"get_count":12,"put_bytes":28470,"put_count":10}} +{"i":16,"series":1,"stats":{"get_bytes":29710,"get_count":11,"put_bytes":27614,"put_count":9}} +{"i":17,"series":1,"stats":{"get_bytes":29670,"get_count":12,"put_bytes":27574,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":28797,"get_count":11,"put_bytes":26701,"put_count":9}} +{"i":19,"series":1,"stats":{"get_bytes":30007,"get_count":13,"put_bytes":27911,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":30640,"get_count":14,"put_bytes":28544,"put_count":12}} +{"i":21,"series":1,"stats":{"get_bytes":29581,"get_count":12,"put_bytes":27485,"put_count":10}} +{"i":22,"series":1,"stats":{"get_bytes":30141,"get_count":13,"put_bytes":28045,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":29251,"get_count":11,"put_bytes":27155,"put_count":9}} +{"i":24,"series":1,"stats":{"get_bytes":29826,"get_count":12,"put_bytes":27730,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":30359,"get_count":13,"put_bytes":28263,"put_count":11}} +{"i":26,"series":1,"stats":{"get_bytes":30961,"get_count":14,"put_bytes":28865,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":30378,"get_count":13,"put_bytes":28282,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":29136,"get_count":10,"put_bytes":27040,"put_count":8}} +{"i":29,"series":1,"stats":{"get_bytes":30314,"get_count":11,"put_bytes":28218,"put_count":9}} +{"i":30,"series":1,"stats":{"get_bytes":29483,"get_count":11,"put_bytes":27387,"put_count":9}} +{"i":31,"series":1,"stats":{"get_bytes":28764,"get_count":9,"put_bytes":26668,"put_count":7}} +{"i":32,"series":1,"stats":{"get_bytes":28786,"get_count":10,"put_bytes":26690,"put_count":8}} +{"i":33,"series":1,"stats":{"get_bytes":31232,"get_count":13,"put_bytes":29136,"put_count":11}} +{"i":34,"series":1,"stats":{"get_bytes":29827,"get_count":12,"put_bytes":27731,"put_count":10}} +{"i":35,"series":1,"stats":{"get_bytes":29690,"get_count":11,"put_bytes":27594,"put_count":9}} +{"i":36,"series":1,"stats":{"get_bytes":28402,"get_count":10,"put_bytes":26306,"put_count":8}} +{"i":37,"series":1,"stats":{"get_bytes":28478,"get_count":10,"put_bytes":26382,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":29001,"get_count":10,"put_bytes":26905,"put_count":8}} +{"i":39,"series":1,"stats":{"get_bytes":29683,"get_count":10,"put_bytes":27587,"put_count":8}} +{"i":40,"series":1,"stats":{"get_bytes":30213,"get_count":13,"put_bytes":28117,"put_count":11}} +{"i":41,"series":1,"stats":{"get_bytes":30412,"get_count":12,"put_bytes":28316,"put_count":10}} +{"i":42,"series":1,"stats":{"get_bytes":30039,"get_count":12,"put_bytes":27943,"put_count":10}} +{"i":43,"series":1,"stats":{"get_bytes":30404,"get_count":12,"put_bytes":28308,"put_count":10}} +{"i":44,"series":1,"stats":{"get_bytes":28548,"get_count":10,"put_bytes":26452,"put_count":8}} +{"i":45,"series":1,"stats":{"get_bytes":28065,"get_count":8,"put_bytes":25969,"put_count":6}} +{"i":46,"series":1,"stats":{"get_bytes":28865,"get_count":10,"put_bytes":26769,"put_count":8}} +{"i":47,"series":1,"stats":{"get_bytes":29210,"get_count":10,"put_bytes":27114,"put_count":8}} +{"i":48,"series":1,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":26767,"put_count":8}} +{"i":49,"series":1,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":27769,"put_count":9}} +{"i":50,"series":1,"stats":{"get_bytes":27686,"get_count":8,"put_bytes":25590,"put_count":6}} +{"i":51,"series":1,"stats":{"get_bytes":29355,"get_count":11,"put_bytes":27259,"put_count":9}} +{"i":52,"series":1,"stats":{"get_bytes":29016,"get_count":10,"put_bytes":26920,"put_count":8}} +{"i":53,"series":1,"stats":{"get_bytes":30050,"get_count":12,"put_bytes":27954,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":27831,"get_count":8,"put_bytes":25735,"put_count":6}} +{"i":55,"series":1,"stats":{"get_bytes":30038,"get_count":12,"put_bytes":27942,"put_count":10}} +{"i":56,"series":1,"stats":{"get_bytes":30073,"get_count":11,"put_bytes":27977,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":29809,"get_count":12,"put_bytes":27713,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":30120,"get_count":12,"put_bytes":28024,"put_count":10}} +{"i":59,"series":1,"stats":{"get_bytes":30352,"get_count":13,"put_bytes":28256,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":30593,"get_count":13,"put_bytes":28497,"put_count":11}} +{"i":61,"series":1,"stats":{"get_bytes":29464,"get_count":11,"put_bytes":27368,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":29491,"get_count":11,"put_bytes":27395,"put_count":9}} +{"i":63,"series":1,"stats":{"get_bytes":29598,"get_count":12,"put_bytes":27502,"put_count":10}} +{"i":64,"series":1,"stats":{"get_bytes":29886,"get_count":12,"put_bytes":27790,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":30436,"get_count":14,"put_bytes":28340,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":29354,"get_count":11,"put_bytes":27258,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":29657,"get_count":12,"put_bytes":27561,"put_count":10}} +{"i":68,"series":1,"stats":{"get_bytes":30356,"get_count":13,"put_bytes":28260,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":29210,"get_count":11,"put_bytes":27114,"put_count":9}} +{"i":70,"series":1,"stats":{"get_bytes":29867,"get_count":11,"put_bytes":27771,"put_count":9}} +{"i":71,"series":1,"stats":{"get_bytes":28470,"get_count":9,"put_bytes":26374,"put_count":7}} +{"i":72,"series":1,"stats":{"get_bytes":31164,"get_count":13,"put_bytes":29068,"put_count":11}} +{"i":73,"series":1,"stats":{"get_bytes":29626,"get_count":11,"put_bytes":27530,"put_count":9}} +{"i":74,"series":1,"stats":{"get_bytes":30044,"get_count":12,"put_bytes":27948,"put_count":10}} +{"i":75,"series":1,"stats":{"get_bytes":29889,"get_count":12,"put_bytes":27793,"put_count":10}} +{"i":76,"series":1,"stats":{"get_bytes":29994,"get_count":11,"put_bytes":27898,"put_count":9}} +{"i":77,"series":1,"stats":{"get_bytes":29800,"get_count":11,"put_bytes":27704,"put_count":9}} +{"i":78,"series":1,"stats":{"get_bytes":29142,"get_count":9,"put_bytes":27046,"put_count":7}} +{"i":79,"series":1,"stats":{"get_bytes":29717,"get_count":11,"put_bytes":27621,"put_count":9}} +{"i":80,"series":1,"stats":{"get_bytes":30767,"get_count":14,"put_bytes":28671,"put_count":12}} +{"i":81,"series":1,"stats":{"get_bytes":30242,"get_count":12,"put_bytes":28146,"put_count":10}} +{"i":82,"series":1,"stats":{"get_bytes":29000,"get_count":10,"put_bytes":26904,"put_count":8}} +{"i":83,"series":1,"stats":{"get_bytes":29851,"get_count":11,"put_bytes":27755,"put_count":9}} +{"i":84,"series":1,"stats":{"get_bytes":27979,"get_count":8,"put_bytes":25883,"put_count":6}} +{"i":85,"series":1,"stats":{"get_bytes":28547,"get_count":10,"put_bytes":26451,"put_count":8}} +{"i":86,"series":1,"stats":{"get_bytes":30799,"get_count":13,"put_bytes":28703,"put_count":11}} +{"i":87,"series":1,"stats":{"get_bytes":30307,"get_count":13,"put_bytes":28211,"put_count":11}} +{"i":88,"series":1,"stats":{"get_bytes":29948,"get_count":12,"put_bytes":27852,"put_count":10}} +{"i":89,"series":1,"stats":{"get_bytes":29590,"get_count":12,"put_bytes":27494,"put_count":10}} +{"i":90,"series":1,"stats":{"get_bytes":30423,"get_count":13,"put_bytes":28327,"put_count":11}} +{"i":91,"series":1,"stats":{"get_bytes":29593,"get_count":12,"put_bytes":27497,"put_count":10}} +{"i":92,"series":1,"stats":{"get_bytes":29107,"get_count":11,"put_bytes":27011,"put_count":9}} +{"i":93,"series":1,"stats":{"get_bytes":29339,"get_count":11,"put_bytes":27243,"put_count":9}} +{"i":94,"series":1,"stats":{"get_bytes":28896,"get_count":9,"put_bytes":26800,"put_count":7}} +{"i":95,"series":1,"stats":{"get_bytes":29538,"get_count":11,"put_bytes":27442,"put_count":9}} +{"i":96,"series":1,"stats":{"get_bytes":28292,"get_count":8,"put_bytes":26196,"put_count":6}} +{"i":97,"series":1,"stats":{"get_bytes":28430,"get_count":9,"put_bytes":26334,"put_count":7}} +{"i":98,"series":1,"stats":{"get_bytes":29697,"get_count":11,"put_bytes":27601,"put_count":9}} +{"i":99,"series":1,"stats":{"get_bytes":29644,"get_count":11,"put_bytes":27548,"put_count":9}} +{"i":0,"series":2,"stats":{"get_bytes":29611,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":29962,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":2,"series":2,"stats":{"get_bytes":28938,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":3,"series":2,"stats":{"get_bytes":30012,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":4,"series":2,"stats":{"get_bytes":31157,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":5,"series":2,"stats":{"get_bytes":28650,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":6,"series":2,"stats":{"get_bytes":31221,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":7,"series":2,"stats":{"get_bytes":29432,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":8,"series":2,"stats":{"get_bytes":30509,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":9,"series":2,"stats":{"get_bytes":30661,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":10,"series":2,"stats":{"get_bytes":29641,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":11,"series":2,"stats":{"get_bytes":30389,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":12,"series":2,"stats":{"get_bytes":29398,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":13,"series":2,"stats":{"get_bytes":30494,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":14,"series":2,"stats":{"get_bytes":30949,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":15,"series":2,"stats":{"get_bytes":30566,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":16,"series":2,"stats":{"get_bytes":29710,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":17,"series":2,"stats":{"get_bytes":29670,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":18,"series":2,"stats":{"get_bytes":28797,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":19,"series":2,"stats":{"get_bytes":30007,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":20,"series":2,"stats":{"get_bytes":30640,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":21,"series":2,"stats":{"get_bytes":29581,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":22,"series":2,"stats":{"get_bytes":30141,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":23,"series":2,"stats":{"get_bytes":29251,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":24,"series":2,"stats":{"get_bytes":29826,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":25,"series":2,"stats":{"get_bytes":30359,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":26,"series":2,"stats":{"get_bytes":30961,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":27,"series":2,"stats":{"get_bytes":30378,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":28,"series":2,"stats":{"get_bytes":29136,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":29,"series":2,"stats":{"get_bytes":30314,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":30,"series":2,"stats":{"get_bytes":29483,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":31,"series":2,"stats":{"get_bytes":28764,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":32,"series":2,"stats":{"get_bytes":28786,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":33,"series":2,"stats":{"get_bytes":31232,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":34,"series":2,"stats":{"get_bytes":29827,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":35,"series":2,"stats":{"get_bytes":29690,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":36,"series":2,"stats":{"get_bytes":28402,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":37,"series":2,"stats":{"get_bytes":28478,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":38,"series":2,"stats":{"get_bytes":29001,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":39,"series":2,"stats":{"get_bytes":29683,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":40,"series":2,"stats":{"get_bytes":30213,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":41,"series":2,"stats":{"get_bytes":30412,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":42,"series":2,"stats":{"get_bytes":30039,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":43,"series":2,"stats":{"get_bytes":30404,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":44,"series":2,"stats":{"get_bytes":28548,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":45,"series":2,"stats":{"get_bytes":28065,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":46,"series":2,"stats":{"get_bytes":28865,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":47,"series":2,"stats":{"get_bytes":29210,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":48,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":49,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":50,"series":2,"stats":{"get_bytes":27686,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":51,"series":2,"stats":{"get_bytes":29355,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":52,"series":2,"stats":{"get_bytes":29016,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":53,"series":2,"stats":{"get_bytes":30050,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":54,"series":2,"stats":{"get_bytes":27831,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":55,"series":2,"stats":{"get_bytes":30038,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":56,"series":2,"stats":{"get_bytes":30073,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":57,"series":2,"stats":{"get_bytes":29809,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":58,"series":2,"stats":{"get_bytes":30120,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":59,"series":2,"stats":{"get_bytes":30352,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":60,"series":2,"stats":{"get_bytes":30593,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":61,"series":2,"stats":{"get_bytes":29464,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":62,"series":2,"stats":{"get_bytes":29491,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":63,"series":2,"stats":{"get_bytes":29598,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":64,"series":2,"stats":{"get_bytes":29886,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":65,"series":2,"stats":{"get_bytes":30436,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":66,"series":2,"stats":{"get_bytes":29354,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":67,"series":2,"stats":{"get_bytes":29657,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":68,"series":2,"stats":{"get_bytes":30356,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":69,"series":2,"stats":{"get_bytes":29210,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":70,"series":2,"stats":{"get_bytes":29867,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":71,"series":2,"stats":{"get_bytes":28470,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":72,"series":2,"stats":{"get_bytes":31164,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":73,"series":2,"stats":{"get_bytes":29626,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":74,"series":2,"stats":{"get_bytes":30044,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":75,"series":2,"stats":{"get_bytes":29889,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":76,"series":2,"stats":{"get_bytes":29994,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":77,"series":2,"stats":{"get_bytes":29800,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":78,"series":2,"stats":{"get_bytes":29142,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":79,"series":2,"stats":{"get_bytes":29717,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":80,"series":2,"stats":{"get_bytes":30767,"get_count":14,"put_bytes":88,"put_count":1}} +{"i":81,"series":2,"stats":{"get_bytes":30242,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":82,"series":2,"stats":{"get_bytes":29000,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":83,"series":2,"stats":{"get_bytes":29851,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":84,"series":2,"stats":{"get_bytes":27979,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":85,"series":2,"stats":{"get_bytes":28547,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":86,"series":2,"stats":{"get_bytes":30799,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":87,"series":2,"stats":{"get_bytes":30307,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":88,"series":2,"stats":{"get_bytes":29948,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":89,"series":2,"stats":{"get_bytes":29590,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":90,"series":2,"stats":{"get_bytes":30423,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":91,"series":2,"stats":{"get_bytes":29593,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":92,"series":2,"stats":{"get_bytes":29107,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":93,"series":2,"stats":{"get_bytes":29339,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":94,"series":2,"stats":{"get_bytes":28896,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":95,"series":2,"stats":{"get_bytes":29538,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":96,"series":2,"stats":{"get_bytes":28292,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":97,"series":2,"stats":{"get_bytes":28430,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":98,"series":2,"stats":{"get_bytes":29697,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":99,"series":2,"stats":{"get_bytes":29644,"get_count":11,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index dc913e2fd..ffcb99458 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":16957,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":16889,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":15923,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":15695,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":15629,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":15551,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":16513,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":15921,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":16072,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":16439,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":16294,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":16363,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":15924,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":15999,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":15991,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":17322,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":16296,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":17175,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":15994,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":17037,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":16736,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":17106,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":16367,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":16069,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":16740,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":16298,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":15483,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":16070,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":16665,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":16221,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":15626,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":15925,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":16142,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":16000,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":16808,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":16143,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":16436,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":17619,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":15849,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":15778,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":17178,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":15114,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":17033,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":16366,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":15702,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":16290,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":16071,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":15997,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":15918,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":15779,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":16657,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":16440,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":16735,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":16293,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":16884,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":16660,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":15482,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":15554,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":16588,"get_count":5,"put_bytes":87,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":15848,"get_count":5,"put_bytes":87,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":16738,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":16959,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":16891,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":15925,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":15781,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":15781,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":15116,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":15697,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":16739,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":15631,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":15920,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":15553,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":16515,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":15923,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":16368,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":16074,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":17108,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":17108,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":16441,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":16296,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":16365,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":15926,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":16001,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":15993,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":17324,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":16298,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":17177,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":16369,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":15996,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":17039,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":16738,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":16666,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":17108,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":16444,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":16369,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":16071,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":16742,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":16444,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":16368,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":17035,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":16592,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":16300,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":16592,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":16664,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":15558,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":15485,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":16223,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":15998,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":16072,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":15850,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":16667,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":16368,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":16223,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":15628,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":15927,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":16666,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":16144,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":16146,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":16146,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":16002,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":16810,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":16145,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":16438,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":17621,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":15851,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":16146,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":15780,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":17180,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":15998,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":15116,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":16147,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":17035,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":16368,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":15486,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":15704,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":16292,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":16073,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":15999,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":16592,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":15920,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":15781,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":16659,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":15782,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":16442,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":16737,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":16295,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":16886,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":16662,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":15484,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":15556,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":16590,"get_count":5,"put_bytes":88,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":15850,"get_count":5,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 0521b6cf7..0c337641a 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":266491,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":257836,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":246528,"get_count":81,"put_bytes":87,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":258731,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":269892,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":277895,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":253688,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":244118,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":261238,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":267250,"get_count":90,"put_bytes":87,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":247536,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":260116,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":251442,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":249516,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":266163,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":262195,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":272231,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":265168,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":255856,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":269006,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":250187,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":282751,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":252070,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":260660,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":260160,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":239583,"get_count":79,"put_bytes":87,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":265024,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":266671,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":274442,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":256022,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":277883,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":266982,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":262070,"get_count":90,"put_bytes":87,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":256448,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":249022,"get_count":81,"put_bytes":87,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":266876,"get_count":90,"put_bytes":87,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":245415,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":269056,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":264272,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":253375,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":258091,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":243447,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":249141,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":251888,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":264174,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":258121,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":241572,"get_count":80,"put_bytes":87,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":254362,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":246996,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":261938,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":269981,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":258752,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":259423,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":263537,"get_count":90,"put_bytes":87,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":257615,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":260749,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":255598,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":278597,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":253717,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":269693,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":258100,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":251759,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":278232,"get_count":94,"put_bytes":87,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":260531,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":261868,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":262696,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":272423,"get_count":90,"put_bytes":87,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":270334,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":261406,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":220833,"get_count":75,"put_bytes":87,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":237960,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":251797,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":247447,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":278770,"get_count":93,"put_bytes":87,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":246382,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":246300,"get_count":82,"put_bytes":87,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":253088,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":252908,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":259910,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":267248,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":259625,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":272858,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":255094,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":281548,"get_count":94,"put_bytes":87,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":255832,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":268789,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":264134,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":245157,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":257535,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":246060,"get_count":84,"put_bytes":87,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":251084,"get_count":86,"put_bytes":87,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":259741,"get_count":87,"put_bytes":87,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":256740,"get_count":85,"put_bytes":87,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":272969,"get_count":91,"put_bytes":87,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":263014,"get_count":89,"put_bytes":87,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":257747,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":273134,"get_count":92,"put_bytes":87,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":266752,"get_count":88,"put_bytes":87,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":254375,"get_count":83,"put_bytes":87,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":271999,"get_count":91,"put_bytes":87,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":266493,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":1,"series":1,"stats":{"get_bytes":257838,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":2,"series":1,"stats":{"get_bytes":246530,"get_count":81,"put_bytes":88,"put_count":1}} +{"i":3,"series":1,"stats":{"get_bytes":258733,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":4,"series":1,"stats":{"get_bytes":269894,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":5,"series":1,"stats":{"get_bytes":277897,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":6,"series":1,"stats":{"get_bytes":253690,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":7,"series":1,"stats":{"get_bytes":244120,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":8,"series":1,"stats":{"get_bytes":261240,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":9,"series":1,"stats":{"get_bytes":267252,"get_count":90,"put_bytes":88,"put_count":1}} +{"i":10,"series":1,"stats":{"get_bytes":247538,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":11,"series":1,"stats":{"get_bytes":260118,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":12,"series":1,"stats":{"get_bytes":251444,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":13,"series":1,"stats":{"get_bytes":249518,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":14,"series":1,"stats":{"get_bytes":266165,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":15,"series":1,"stats":{"get_bytes":262197,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":16,"series":1,"stats":{"get_bytes":272233,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":17,"series":1,"stats":{"get_bytes":265170,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":18,"series":1,"stats":{"get_bytes":255858,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":19,"series":1,"stats":{"get_bytes":269008,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":20,"series":1,"stats":{"get_bytes":250189,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":21,"series":1,"stats":{"get_bytes":282753,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":22,"series":1,"stats":{"get_bytes":252072,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":23,"series":1,"stats":{"get_bytes":260662,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":24,"series":1,"stats":{"get_bytes":260162,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":25,"series":1,"stats":{"get_bytes":239585,"get_count":79,"put_bytes":88,"put_count":1}} +{"i":26,"series":1,"stats":{"get_bytes":265026,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":27,"series":1,"stats":{"get_bytes":266673,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":28,"series":1,"stats":{"get_bytes":274444,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":29,"series":1,"stats":{"get_bytes":256024,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":30,"series":1,"stats":{"get_bytes":277885,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":31,"series":1,"stats":{"get_bytes":266984,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":32,"series":1,"stats":{"get_bytes":262072,"get_count":90,"put_bytes":88,"put_count":1}} +{"i":33,"series":1,"stats":{"get_bytes":256450,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":34,"series":1,"stats":{"get_bytes":249024,"get_count":81,"put_bytes":88,"put_count":1}} +{"i":35,"series":1,"stats":{"get_bytes":266878,"get_count":90,"put_bytes":88,"put_count":1}} +{"i":36,"series":1,"stats":{"get_bytes":245417,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":37,"series":1,"stats":{"get_bytes":269058,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":38,"series":1,"stats":{"get_bytes":264274,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":39,"series":1,"stats":{"get_bytes":253377,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":40,"series":1,"stats":{"get_bytes":258093,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":41,"series":1,"stats":{"get_bytes":243449,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":42,"series":1,"stats":{"get_bytes":249143,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":43,"series":1,"stats":{"get_bytes":251890,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":44,"series":1,"stats":{"get_bytes":264176,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":45,"series":1,"stats":{"get_bytes":258123,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":46,"series":1,"stats":{"get_bytes":241574,"get_count":80,"put_bytes":88,"put_count":1}} +{"i":47,"series":1,"stats":{"get_bytes":254364,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":48,"series":1,"stats":{"get_bytes":246998,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":49,"series":1,"stats":{"get_bytes":261940,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":50,"series":1,"stats":{"get_bytes":269983,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":51,"series":1,"stats":{"get_bytes":258754,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":52,"series":1,"stats":{"get_bytes":259425,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":53,"series":1,"stats":{"get_bytes":263539,"get_count":90,"put_bytes":88,"put_count":1}} +{"i":54,"series":1,"stats":{"get_bytes":257617,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":55,"series":1,"stats":{"get_bytes":260751,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":56,"series":1,"stats":{"get_bytes":255600,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":57,"series":1,"stats":{"get_bytes":278599,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":58,"series":1,"stats":{"get_bytes":253719,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":59,"series":1,"stats":{"get_bytes":269695,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":60,"series":1,"stats":{"get_bytes":258102,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":61,"series":1,"stats":{"get_bytes":251761,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":62,"series":1,"stats":{"get_bytes":278234,"get_count":94,"put_bytes":88,"put_count":1}} +{"i":63,"series":1,"stats":{"get_bytes":260533,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":64,"series":1,"stats":{"get_bytes":261870,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":65,"series":1,"stats":{"get_bytes":262698,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":66,"series":1,"stats":{"get_bytes":272425,"get_count":90,"put_bytes":88,"put_count":1}} +{"i":67,"series":1,"stats":{"get_bytes":270336,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":68,"series":1,"stats":{"get_bytes":261408,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":69,"series":1,"stats":{"get_bytes":220835,"get_count":75,"put_bytes":88,"put_count":1}} +{"i":70,"series":1,"stats":{"get_bytes":237962,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":71,"series":1,"stats":{"get_bytes":251799,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":72,"series":1,"stats":{"get_bytes":247449,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":73,"series":1,"stats":{"get_bytes":278772,"get_count":93,"put_bytes":88,"put_count":1}} +{"i":74,"series":1,"stats":{"get_bytes":246384,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":75,"series":1,"stats":{"get_bytes":246302,"get_count":82,"put_bytes":88,"put_count":1}} +{"i":76,"series":1,"stats":{"get_bytes":253090,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":77,"series":1,"stats":{"get_bytes":252910,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":78,"series":1,"stats":{"get_bytes":259912,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":79,"series":1,"stats":{"get_bytes":267250,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":80,"series":1,"stats":{"get_bytes":259627,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":81,"series":1,"stats":{"get_bytes":272860,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":82,"series":1,"stats":{"get_bytes":255096,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":83,"series":1,"stats":{"get_bytes":281550,"get_count":94,"put_bytes":88,"put_count":1}} +{"i":84,"series":1,"stats":{"get_bytes":255834,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":85,"series":1,"stats":{"get_bytes":268791,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":86,"series":1,"stats":{"get_bytes":264136,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":87,"series":1,"stats":{"get_bytes":245159,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":88,"series":1,"stats":{"get_bytes":257537,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":89,"series":1,"stats":{"get_bytes":246062,"get_count":84,"put_bytes":88,"put_count":1}} +{"i":90,"series":1,"stats":{"get_bytes":251086,"get_count":86,"put_bytes":88,"put_count":1}} +{"i":91,"series":1,"stats":{"get_bytes":259743,"get_count":87,"put_bytes":88,"put_count":1}} +{"i":92,"series":1,"stats":{"get_bytes":256742,"get_count":85,"put_bytes":88,"put_count":1}} +{"i":93,"series":1,"stats":{"get_bytes":272971,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":94,"series":1,"stats":{"get_bytes":263016,"get_count":89,"put_bytes":88,"put_count":1}} +{"i":95,"series":1,"stats":{"get_bytes":257749,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":96,"series":1,"stats":{"get_bytes":273136,"get_count":92,"put_bytes":88,"put_count":1}} +{"i":97,"series":1,"stats":{"get_bytes":266754,"get_count":88,"put_bytes":88,"put_count":1}} +{"i":98,"series":1,"stats":{"get_bytes":254377,"get_count":83,"put_bytes":88,"put_count":1}} +{"i":99,"series":1,"stats":{"get_bytes":272001,"get_count":91,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 713fbb278..9674784a8 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,6 +1,8 @@ +use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::{runtime::builtins::Type, test_utils::*, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; #[allow(dead_code)] pub fn construct_and_verify(initcode: Vec) -> MockRuntime { @@ -13,11 +15,22 @@ pub fn init_construct_and_verify( ) -> MockRuntime { let mut rt = MockRuntime::default(); - // invoke constructor - rt.expect_validate_caller_any(); + // construct EVM actor + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + rt.expect_validate_caller_type(vec![Type::Init]); initrt(&mut rt); - let params = evm::ConstructorParams { bytecode: initcode.into() }; + // first actor created is 0 + rt.add_delegated_address( + Address::new_id(0), + Address::new_delegated(10, &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000")) + .unwrap(), + ); + + let params = evm::ConstructorParams { + creator: EthAddress::from_id(fil_actors_runtime::EAM_ACTOR_ADDR.id().unwrap()), + initcode: initcode.into(), + }; let result = rt .call::( diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 14b75a602..1149a18f5 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -5,8 +5,9 @@ use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR}; 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::clock::ChainEpoch; +use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::crypto::signature::{ Signature, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE, }; @@ -141,6 +142,26 @@ 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_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, @@ -174,6 +195,10 @@ where fvm::actor::resolve_address(address) } + fn lookup_address(&self, id: ActorID) -> Option
{ + fvm::actor::lookup_address(id) + } + fn get_actor_code_cid(&self, id: &ActorID) -> Option { fvm::actor::get_actor_code_cid(&Address::new_id(*id)) } @@ -453,10 +478,16 @@ where fvm::actor::install_actor(code_id).map_err(|_| Error::msg("failed to install actor")) } - fn hash(&self, hasher: fvm_shared::crypto::hash::SupportedHashes, data: &[u8]) -> Vec { + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { fvm::crypto::hash_owned(hasher, data) } + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + let mut buf = [0u8; 64]; + let len = fvm::crypto::hash_into(hasher, data, &mut buf); + (buf, len) + } + fn recover_secp_public_key( &self, hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 7eb36a7fc..a9ba31cd7 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -59,6 +59,14 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { 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; @@ -74,6 +82,10 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// If the argument is an ID address it is returned directly. fn resolve_address(&self, address: &Address) -> Option; + /// Looks-up the "predictable" address of an actor by ID, if any. Returns None if either the + /// target actor doesn't exist, or if the target actord doesn't have a predictable address. + fn lookup_address(&self, id: ActorID) -> Option
; + /// Look up the code ID at an actor address. fn get_actor_code_cid(&self, id: &ActorID) -> Option; @@ -218,6 +230,9 @@ pub trait Primitives { /// Hashes input data using a supported hash function. fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec; + /// Hashes input into a 64 byte buffer + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize); + /// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes. fn compute_unsealed_sector_cid( &self, diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 5245a4e0c..a54862eef 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -12,8 +12,7 @@ use cid::Cid; use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore}; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; -use fvm_shared::address::Payload; -use fvm_shared::address::{Address, Protocol}; +use fvm_shared::address::{Address, Payload, Protocol}; use fvm_shared::clock::ChainEpoch; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; use fvm_shared::consensus::ConsensusFault; @@ -117,6 +116,7 @@ pub struct MockRuntime { pub miner: Address, pub base_fee: TokenAmount, pub id_addresses: HashMap, + pub delegated_addresses: HashMap, pub actor_code_cids: HashMap, pub new_actor_addr: Option
, pub receiver: Address, @@ -164,6 +164,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 +195,11 @@ impl Expectations { "expected ValidateCallerAddr {:?}, not received", self.expect_validate_caller_addr ); + assert!( + self.expect_validate_caller_f4_namespace.is_none(), + "expected ValidateNamespace {:?}, not received", + self.expect_validate_caller_f4_namespace + ); assert!( self.expect_validate_caller_type.is_none(), "expected ValidateCallerType {:?}, not received", @@ -290,6 +296,7 @@ impl MockRuntime { miner: Address::new_id(0), base_fee: Default::default(), id_addresses: Default::default(), + delegated_addresses: Default::default(), actor_code_cids: Default::default(), new_actor_addr: Default::default(), receiver: Address::new_id(0), @@ -488,6 +495,16 @@ impl MockRuntime { self.id_addresses.insert(source, target); } + pub fn add_delegated_address(&mut self, source: Address, target: Address) { + assert_eq!( + target.protocol(), + Protocol::Delegated, + "target must use Delegated address protocol" + ); + assert_eq!(source.protocol(), Protocol::ID, "source must use ID address protocol"); + self.delegated_addresses.insert(source, target); + } + pub fn call( &mut self, method_num: MethodNum, @@ -570,6 +587,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); @@ -792,6 +815,51 @@ 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_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_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, @@ -851,6 +919,11 @@ impl Runtime> for MockRuntime { } } + fn lookup_address(&self, id: ActorID) -> Option
{ + self.require_in_call(); + self.delegated_addresses.get(&Address::new_id(id)).copied() + } + fn get_actor_code_cid(&self, id: &ActorID) -> Option { self.require_in_call(); self.actor_code_cids.get(&Address::new_id(*id)).cloned() @@ -1206,6 +1279,10 @@ impl Primitives for MockRuntime { ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { (*self.recover_pubkey_fn)(hash, signature).map_err(|_| anyhow!("failed to recover pubkey.")) } + + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + (*self.hash_func)(hasher, data) + } } impl Verifier for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index b95699081..ef8067138 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -757,6 +757,43 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat } } + 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_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, @@ -819,6 +856,10 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat } } + fn lookup_address(&self, id: ActorID) -> Option
{ + self.v.get_actor(Address::new_id(id)).and_then(|act| act.predictable_address) + } + fn send( &self, to: &Address, @@ -1000,6 +1041,12 @@ impl Primitives for VM<'_> { hasher.digest(data).to_bytes() } + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + let hasher = Code::try_from(hasher as u64).unwrap(); + let (len, buf, ..) = hasher.digest(data).into_inner(); + (buf, len as usize) + } + fn recover_secp_public_key( &self, hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], @@ -1040,6 +1087,10 @@ impl Primitives for InvocationCtx<'_, '_> { self.v.hash(hasher, data) } + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + self.v.hash_64(hasher, data) + } + fn recover_secp_public_key( &self, hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], From 55da3dfd5822903c11c4fd0bb1661ea62a21eca7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 14 Oct 2022 17:54:43 -0700 Subject: [PATCH 077/339] fix: register the EAM (#756) - Add a new type. - Add it to the bundle. - Add it to the test VM. --- Cargo.lock | 1 + build.rs | 1 + runtime/src/runtime/builtins.rs | 2 ++ runtime/src/test_utils.rs | 5 ++++- state/src/check.rs | 1 + test_vm/Cargo.toml | 1 + test_vm/src/lib.rs | 14 +++++++++++--- test_vm/tests/init_test.rs | 4 ++-- 8 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da4fd8e58..19260283f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3592,6 +3592,7 @@ dependencies = [ "cid", "fil_actor_account", "fil_actor_cron", + "fil_actor_eam", "fil_actor_evm", "fil_actor_init", "fil_actor_market", diff --git a/build.rs b/build.rs index 09d28893c..97550d214 100644 --- a/build.rs +++ b/build.rs @@ -27,6 +27,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("verifreg", "verifiedregistry"), ("embryo", "embryo"), ("evm", "evm"), + ("eam", "eam"), ]; /// Default Cargo features to activate during the build. diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index adc93f046..ea429eca3 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -21,6 +21,7 @@ pub enum Type { VerifiedRegistry = 11, Embryo = 12, EVM = 13, + EAM = 14, } impl Type { @@ -39,6 +40,7 @@ impl Type { Type::VerifiedRegistry => "verifiedregistry", Type::Embryo => "embryo", Type::EVM => "evm", + Type::EAM => "eam", } } } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index a54862eef..9b48d8da3 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -58,6 +58,7 @@ lazy_static::lazy_static! { pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry"); pub static ref EMBRYO_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/embryo"); pub static ref EVM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/evm"); + pub static ref EAM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/eam"); pub static ref ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System); @@ -71,8 +72,9 @@ lazy_static::lazy_static! { map.insert(*MULTISIG_ACTOR_CODE_ID, Type::Multisig); map.insert(*REWARD_ACTOR_CODE_ID, Type::Reward); map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); - map.insert(*EMBRYO_ACTOR_CODE_ID, Type::EVM); + map.insert(*EMBRYO_ACTOR_CODE_ID, Type::Embryo); map.insert(*EVM_ACTOR_CODE_ID, Type::EVM); + map.insert(*EAM_ACTOR_CODE_ID, Type::EAM); map }; pub static ref ACTOR_CODES: BTreeMap = [ @@ -89,6 +91,7 @@ lazy_static::lazy_static! { (Type::VerifiedRegistry, *VERIFREG_ACTOR_CODE_ID), (Type::Embryo, *EMBRYO_ACTOR_CODE_ID), (Type::EVM, *EVM_ACTOR_CODE_ID), + (Type::EAM, *EAM_ACTOR_CODE_ID), ] .into_iter() .collect(); diff --git a/state/src/check.rs b/state/src/check.rs index daf3cc6a3..5cf218858 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -210,6 +210,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( } Some(Type::Embryo) => {} Some(Type::EVM) => {} + Some(Type::EAM) => {} None => { bail!("unexpected actor code CID {} for address {}", actor.code, key); } diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 089742604..b66c74c75 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -25,6 +25,7 @@ fil_actor_market = { version = "10.0.0-alpha.1", path = "../actors/market" } 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_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } +fil_actor_eam = { version = "10.0.0-alpha.1", path = "../actors/eam" } lazy_static = "1.4.0" fvm_shared = { version = "3.0.0-alpha.5", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index ef8067138..e87538dc5 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -4,6 +4,7 @@ use cid::multihash::Code; 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_eam::EamActor; 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}; @@ -23,9 +24,9 @@ use fil_actors_runtime::runtime::{ use fil_actors_runtime::test_utils::*; use fil_actors_runtime::MessageAccumulator; 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_builtin_actors_state::check::check_state_invariants; use fil_builtin_actors_state::check::Tree; @@ -237,6 +238,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), + ); + // burnt funds let burnt_funds_head = v.put_store(&AccountState { address: BURNT_FUNDS_ACTOR_ADDR }); v.set_actor( @@ -686,6 +693,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { Err(ActorError::unhandled_message("embryo actors only handle method 0".into())) } Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, ¶ms), + Type::EAM => EamActor::invoke_method(self, self.msg.method, ¶ms), }; if res.is_err() { self.v.rollback(prior_root) diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs index 3775b4be7..1fc5e9603 100644 --- a/test_vm/tests/init_test.rs +++ b/test_vm/tests/init_test.rs @@ -2,7 +2,7 @@ use fil_actor_init::Exec4Return; use fil_actors_runtime::{ cbor::serialize, runtime::EMPTY_ARR_CID, - test_utils::{ACCOUNT_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID}, + test_utils::{EAM_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID}, EAM_ACTOR_ADDR, EAM_ACTOR_ID, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::MemoryBlockstore; @@ -26,7 +26,7 @@ fn embryo_deploy() { // Create a "fake" eam. v.set_actor( EAM_ACTOR_ADDR, - actor(*ACCOUNT_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), + actor(*EAM_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), ); // Create an embryo. From 49a91d5a72f1dc67d03a2028fd089244f1d938ea Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 17 Oct 2022 13:02:28 -0700 Subject: [PATCH 078/339] feat: allow anyone to call EAM::create2 (#757) Part of #973. --- actors/eam/src/lib.rs | 44 ++++++++++++++++++++++---------------- actors/eam/tests/create.rs | 5 ++--- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 8601a9605..2a69d38e2 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -140,21 +140,6 @@ where Ok(Return::from_exec4(ret, new_addr)) } -/// lookup caller's raw ETH address -fn get_caller_address(rt: &RT) -> Result -where - BS: Blockstore + Clone, - RT: Runtime, -{ - let caller_id = rt.message().caller().id().unwrap(); - - let addr = rt.lookup_address(caller_id).unwrap(); - match addr.payload() { - Payload::Delegated(eth) => Ok(EthAddress(eth.subaddress().try_into().unwrap())), - _ => unreachable!(), - } -} - pub struct EamActor; impl EamActor { pub fn constructor(rt: &mut RT) -> Result<(), ActorError> @@ -171,6 +156,9 @@ impl EamActor { rt.validate_immediate_caller_type(std::iter::once(&Type::Init)) } + /// Create a new contract per the EVM's CREATE rules. + /// + /// Permissions: May only be called by EVM contracts. pub fn create(rt: &mut RT, params: CreateParams) -> Result where BS: Blockstore + Clone, @@ -178,7 +166,12 @@ impl EamActor { { rt.validate_immediate_caller_type(iter::once(&Type::EVM))?; - let caller_addr = get_caller_address(rt)?; + let caller_id = rt.message().caller().id().unwrap(); + let caller_addr = match rt.lookup_address(caller_id).unwrap().payload() { + Payload::Delegated(addr) => EthAddress(addr.subaddress().try_into().unwrap()), + _ => unreachable!(), + }; + // CREATE logic let rlp = RlpCreateAddress { address: caller_addr, nonce: params.nonce }; let eth_addr = EthAddress(hash_20(rt, &rlp.rlp_bytes())); @@ -187,17 +180,32 @@ impl EamActor { create_actor(rt, caller_addr, eth_addr, params.initcode) } + /// Create a new contract per the EVM's CREATE2 rules. + /// + /// Permissions: May be called by any actor. pub fn create2(rt: &mut RT, params: Create2Params) -> Result where BS: Blockstore + Clone, RT: Runtime, { - rt.validate_immediate_caller_type(iter::once(&Type::EVM))?; + rt.validate_immediate_caller_accept_any()?; // CREATE2 logic let inithash = rt.hash(SupportedHashes::Keccak256, ¶ms.initcode); - let caller_addr = get_caller_address(rt)?; + // Try to lookup the caller's EVM address, but otherwise derive one from the ID address. + let caller_id = rt.message().caller().id().unwrap(); + let caller_addr = match rt.lookup_address(caller_id).map(|a| *a.payload()) { + Some(Payload::Delegated(addr)) if addr.namespace() == EAM_ACTOR_ID => { + EthAddress(addr.subaddress().try_into().expect("eth address has an invalid size")) + } + _ => { + let mut bytes = [0u8; 20]; + bytes[0] = 0xff; + bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); + EthAddress(bytes) + } + }; let eth_addr = EthAddress(hash_20( rt, diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 98460745b..9c07f8cac 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -8,7 +8,7 @@ use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, }; -use fil_actors_runtime::{EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fil_actors_runtime::INIT_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; @@ -87,8 +87,7 @@ fn call_create2() { rt.add_delegated_address(id_addr, f4_eth_addr); rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); - rt.expect_validate_caller_type(vec![Type::EVM]); - rt.expect_validate_caller_namespace(vec![EAM_ACTOR_ID]); + rt.expect_validate_caller_any(); let initcode = vec![0xff]; From 40aad0490d687ad4db6f2b0e6b69f85a7ad626fb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 18 Oct 2022 22:14:26 -0700 Subject: [PATCH 079/339] fix: encode/decode EVM parameters as CBOR (#759) * fix: encode/decode EVM parameters as CBOR The previous code was passing parameters as raw bytes, but telling the system that these bytes were valid CBOR. Unfortunately: - This is incorrect and will break once we actually start enforcing this. - This makes it _very_ difficult to test the EVM because we have quite a bit of testing code that assumes CBOR. The long-term solution is to correctly handle IPLD (see https://github.com/filecoin-project/builtin-actors/issues/758). But the short-term solution is to just encode/decode parameters as CBOR. * fix: only use RawBytes for CBOR "RawBytes" is for storing serialized CBOR (per the docs). Any other uses are bugs. --- .../evm/src/interpreter/instructions/call.rs | 50 ++++++++++--------- actors/evm/src/lib.rs | 15 +++--- actors/evm/tests/basic.rs | 8 +-- actors/evm/tests/calc.rs | 10 ++-- actors/evm/tests/call.rs | 13 +++-- actors/evm/tests/create.rs | 10 ++-- actors/evm/tests/env.rs | 19 +++---- actors/evm/tests/ext_opcodes.rs | 8 +-- actors/evm/tests/misc.rs | 19 ++++--- actors/evm/tests/precompile.rs | 4 +- actors/evm/tests/selfdestruct.rs | 5 +- actors/evm/tests/util.rs | 20 +++++--- 12 files changed, 91 insertions(+), 90 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 3fa45c787..ad1b46d8b 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,3 +1,5 @@ +use fvm_ipld_encoding::{BytesDe, BytesSer}; + use { super::memory::{copy_to_memory, get_memory_region}, crate::interpreter::address::EthAddress, @@ -140,7 +142,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( let input_region = get_memory_region(memory, input_offset, input_size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; - let result = { + state.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()] @@ -149,19 +151,19 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( }; if precompiles::Precompiles::::is_precompile(&dst) { - let result = precompiles::Precompiles::call_precompile(rt, dst, input_data) - .map_err(|_| StatusCode::PrecompileFailure)?; - Ok(RawBytes::from(result)) + precompiles::Precompiles::call_precompile(rt, dst, input_data) + .map_err(|_| StatusCode::PrecompileFailure)? } else { let dst_addr = EthAddress::try_from(dst)? .as_id_address() .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; - match kind { + let call_result = match kind { CallKind::Call => rt.send( &dst_addr, Method::InvokeContract as u64, - RawBytes::from(input_data.to_vec()), + // TODO: support IPLD codecs #758 + RawBytes::serialize(BytesSer(input_data))?, TokenAmount::from(&value), ), CallKind::DelegateCall => { @@ -173,28 +175,30 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( CallKind::CallCode => { todo!() } + }; + match call_result { + Ok(result) => { + // TODO: support IPLD codecs #758 + let BytesDe(result) = result.deserialize()?; + result + } + Err(ae) => { + return if ae.exit_code() == EVM_CONTRACT_REVERTED { + // reverted -- we don't have return data yet + // push failure + stack.push(U256::zero()); + Ok(()) + } else { + Err(StatusCode::from(ae)) + }; + } } } - }; - - if let Err(ae) = result { - return if ae.exit_code() == EVM_CONTRACT_REVERTED { - // reverted -- we don't have return data yet - // push failure - stack.push(U256::zero()); - Ok(()) - } else { - Err(StatusCode::from(ae)) - }; } - - let result = result.unwrap().to_vec(); - - // save return_data - state.return_data = result.clone().into(); + .into(); // copy return data to output region if it is non-zero - copy_to_memory(memory, output_offset, output_size, U256::zero(), result.as_slice())?; + copy_to_memory(memory, output_offset, output_size, U256::zero(), &state.return_data)?; stack.push(U256::from(1)); Ok(()) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index b4dede83b..793718af7 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,7 @@ use std::iter; use fil_actors_runtime::{runtime::builtins::Type, EAM_ACTOR_ID}; +use fvm_ipld_encoding::{BytesDe, BytesSer}; use fvm_shared::address::{Address, Payload}; use interpreter::address::EthAddress; @@ -166,8 +167,8 @@ impl EvmContractActor { pub fn invoke_contract( rt: &mut RT, method: u64, - input_data: &RawBytes, - ) -> Result + input_data: &[u8], + ) -> Result, ActorError> where BS: Blockstore + Clone, RT: Runtime, @@ -244,8 +245,7 @@ impl EvmContractActor { rt.delete_actor(&addr)? } - let output = RawBytes::from(exec_status.output_data.to_vec()); - Ok(output) + Ok(exec_status.output_data.to_vec()) } pub fn bytecode(rt: &mut RT) -> Result @@ -307,7 +307,9 @@ impl ActorCode for EvmContractActor { Ok(RawBytes::default()) } Some(Method::InvokeContract) => { - Self::invoke_contract(rt, Method::InvokeContract as u64, params) + let BytesDe(params) = params.deserialize()?; + let value = Self::invoke_contract(rt, Method::InvokeContract as u64, ¶ms)?; + Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::GetBytecode) => { let cid = Self::bytecode(rt)?; @@ -317,7 +319,8 @@ impl ActorCode for EvmContractActor { let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(value)?) } - None => Self::invoke_contract(rt, method, params), + // Otherwise, we take the bytes as CBOR. + None => Self::invoke_contract(rt, method, params).map(RawBytes::new), } } } diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index eea26366b..f09bf159b 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -31,9 +31,7 @@ fn basic_contract_construction_and_invocation() { let mut arg0 = vec![0u8; 32]; solidity_params.append(&mut arg0); - let input_data = RawBytes::from(solidity_params); - - let result = util::invoke_contract(&mut rt, input_data); + let result = util::invoke_contract(&mut rt, &solidity_params); assert_eq!(U256::from_big_endian(&result), U256::from(0)); // invoke contract -- getBalance @@ -46,9 +44,7 @@ fn basic_contract_construction_and_invocation() { arg0[31] = 100; // the owner address solidity_params.append(&mut arg0); - let input_data = RawBytes::from(solidity_params); - - let result = util::invoke_contract(&mut rt, input_data); + let result = util::invoke_contract(&mut rt, &solidity_params); assert_eq!(U256::from_big_endian(&result), U256::from(10000)); } diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index a509b3a72..3c6039521 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -2,7 +2,6 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; -use fvm_ipld_encoding::RawBytes; mod util; @@ -75,26 +74,23 @@ fn test_magic_calc() { // invoke contract -- get_magic let contract_params = vec![0u8; 32]; - let input_data = RawBytes::from(contract_params); - let result = util::invoke_contract(&mut rt, input_data); + 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 input_data = RawBytes::from(contract_params); - let result = util::invoke_contract(&mut rt, input_data); + 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 input_data = RawBytes::from(contract_params); - let result = util::invoke_contract(&mut rt, input_data); + 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 index 49c707e1c..008ce5dde 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -4,7 +4,7 @@ use evm::interpreter::address::EthAddress; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; -use fvm_ipld_encoding::RawBytes; +use fvm_ipld_encoding::{BytesSer, RawBytes}; use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; @@ -76,10 +76,10 @@ fn test_call() { // dest + method 0 with no data let mut contract_params = vec![0u8; 36]; evm_target_word.to_big_endian(&mut contract_params[..32]); - let input_data = RawBytes::from(contract_params); let proxy_call_contract_params = vec![0u8; 4]; - let proxy_call_input_data = RawBytes::from(proxy_call_contract_params); + let proxy_call_input_data = RawBytes::serialize(BytesSer(&proxy_call_contract_params)) + .expect("failed to serialize input data"); // expected return data let mut return_data = vec![0u8; 32]; @@ -90,11 +90,11 @@ fn test_call() { evm::Method::InvokeContract as u64, proxy_call_input_data, TokenAmount::zero(), - RawBytes::from(return_data), + RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, input_data); + let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } @@ -193,7 +193,6 @@ fn test_callactor() { let mut contract_params = vec![0u8; 64]; evm_target_word.to_big_endian(&mut contract_params[..32]); contract_params[63] = 0x42; - let input_data = RawBytes::from(contract_params); let proxy_call_input_data = RawBytes::default(); @@ -209,7 +208,7 @@ fn test_callactor() { RawBytes::from(return_data), ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, input_data); + let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index 44c5f9311..f1f1ff0f2 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -100,7 +100,7 @@ fn test_create() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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 @@ -122,7 +122,7 @@ fn test_create() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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 @@ -144,7 +144,7 @@ fn test_create() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + 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 @@ -162,7 +162,7 @@ fn test_create() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(&result[..], &[0; 32]); } @@ -181,7 +181,7 @@ fn test_create() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::from(contract_params.to_vec())); + let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(&result[..], &[0; 32]); } } diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs index 664d40f94..40d4d5a37 100644 --- a/actors/evm/tests/env.rs +++ b/actors/evm/tests/env.rs @@ -7,11 +7,11 @@ use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; use fil_actors_runtime::{ runtime::builtins::Type, - test_utils::{expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID}, + test_utils::{MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID}, }; use fvm_ipld_blockstore::tracking::{BSStats, TrackingBlockstore}; use fvm_ipld_blockstore::MemoryBlockstore; -use fvm_ipld_encoding::RawBytes; +use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; use fvm_shared::address::Address; /// Alias for a call we will never send to the blockchain. @@ -62,15 +62,14 @@ impl TestEnv { .unwrap(), ); - let result = self + assert!(self .runtime .call::( evm::Method::Constructor as u64, &RawBytes::serialize(params).unwrap(), ) - .unwrap(); - - expect_empty(result); + .unwrap() + .is_empty()); self.runtime.verify(); } @@ -80,14 +79,16 @@ impl TestEnv { /// 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 = RawBytes::from(input.to_vec()); + let input = RawBytes::serialize(BytesSer(&input)).expect("failed to serialize input data"); self.runtime.expect_validate_caller_any(); - let result = self + let BytesDe(result) = self .runtime .call::(evm::Method::InvokeContract as u64, &input) + .unwrap() + .deserialize() .unwrap(); - decode_function_data(&call.function, result.bytes(), false).unwrap() + decode_function_data(&call.function, result, false).unwrap() } } diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 2fed20254..03a015407 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -53,7 +53,7 @@ fn test_extcodesize() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(0x04)); } @@ -96,7 +96,7 @@ fn test_extcodehash() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(&bytecode_cid.hash().digest()[..32])); } @@ -139,6 +139,6 @@ fn test_extcodecopy() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, RawBytes::default()); - assert_eq!(other_bytecode.as_slice(), result.bytes()); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(other_bytecode.as_slice(), result); } diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index 2d5721b72..721f960c4 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -4,7 +4,6 @@ mod util; use cid::Cid; use evm::interpreter::U256; use fil_actor_evm as evm; -use fvm_ipld_encoding::RawBytes; use fvm_shared::econ::TokenAmount; #[test] @@ -25,7 +24,7 @@ return let mut rt = util::construct_and_verify(contract); rt.tipset_timestamp = 123; - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(123)); } @@ -51,7 +50,7 @@ return let test_cid = Cid::try_from("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); rt.tipset_cids = vec![test_cid]; - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(result.to_vec(), test_cid.hash().digest()); } @@ -72,7 +71,7 @@ return .unwrap(); let mut rt = util::construct_and_verify(contract); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(31415926)); } @@ -93,7 +92,7 @@ return .unwrap(); let mut rt = util::construct_and_verify(contract); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(10_000_000_000u64)); } @@ -116,7 +115,7 @@ return 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, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(123 + 345)); } @@ -147,7 +146,7 @@ return let mut input_data = vec![0u8; 32]; input_data[12] = 0xff; input_data[31] = 0x64; - let result = util::invoke_contract(&mut rt, RawBytes::from(input_data)); + let result = util::invoke_contract(&mut rt, &input_data); assert_eq!(U256::from_big_endian(&result), U256::from(123)); } @@ -176,7 +175,7 @@ return 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, RawBytes::from(input_data)); + let result = util::invoke_contract(&mut rt, &input_data); assert_eq!(U256::from_big_endian(&result), U256::from(0)); } @@ -198,7 +197,7 @@ return .unwrap(); let mut rt = util::construct_and_verify(contract); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(0)); } @@ -220,6 +219,6 @@ return let mut rt = util::construct_and_verify(contract); rt.expect_gas_available(123); - let result = util::invoke_contract(&mut rt, RawBytes::default()); + let result = util::invoke_contract(&mut rt, &[]); assert_eq!(U256::from_big_endian(&result), U256::from(123)); } diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 7bd5ec5d6..2227001e3 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -2,7 +2,6 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; -use fvm_ipld_encoding::RawBytes; mod util; @@ -44,9 +43,8 @@ fn test_precompile_hash() { // invoke contract let contract_params = vec![0u8; 32]; - let input_data = RawBytes::from(contract_params); - let result = util::invoke_contract(&mut rt, input_data); + let result = util::invoke_contract(&mut rt, &contract_params); let expected = hex_literal::hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); assert_eq!( diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index 4db707383..a4b077ff0 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -1,5 +1,4 @@ use fil_actors_runtime::test_utils::*; -use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; mod util; @@ -17,11 +16,9 @@ fn test_selfdestruct() { }); let solidity_params = hex::decode("35f46994").unwrap(); - let input_data = RawBytes::from(solidity_params); rt.expect_validate_caller_any(); rt.expect_delete_actor(beneficiary); - let result = util::invoke_contract(&mut rt, input_data); - expect_empty(result); + assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); rt.verify(); } diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 9674784a8..5dddb722b 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,7 +1,7 @@ use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; use fil_actors_runtime::{runtime::builtins::Type, test_utils::*, INIT_ACTOR_ADDR}; -use fvm_ipld_encoding::RawBytes; +use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; use fvm_shared::address::Address; #[allow(dead_code)] @@ -32,20 +32,28 @@ pub fn init_construct_and_verify( initcode: initcode.into(), }; - let result = rt + assert!(rt .call::( evm::Method::Constructor as u64, &RawBytes::serialize(params).unwrap(), ) - .unwrap(); - expect_empty(result); + .unwrap() + .is_empty()); rt.verify(); rt } #[allow(dead_code)] -pub fn invoke_contract(rt: &mut MockRuntime, input_data: RawBytes) -> RawBytes { +pub fn invoke_contract(rt: &mut MockRuntime, input_data: &[u8]) -> Vec { rt.expect_validate_caller_any(); - rt.call::(evm::Method::InvokeContract as u64, &input_data).unwrap() + let BytesDe(res) = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(BytesSer(input_data)).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + res } From 17f314e5d5f2402e284109aaf477063f2614a0c9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 19 Oct 2022 00:06:35 -0700 Subject: [PATCH 080/339] EVM: save/restore state before/after send (#754) * feat: refactor the EVM to manage state in a consistent location - Deduplicate code between the constructor and call. - Add logic for flushing/reloading state. - Add a helper that flushes, sends, and reloads. This also fixes the whole reborrow mess. Through mostly trial and error, I found that a few opcodes were causing the problem. Fixing those opcodes made everything happy. * fix: save/restore EVM contract state before/after sending messages * fix: remove needless lifetimes Apparently, the real issue was that these lifetimes were over constrained. * chore: remove unused type * chore: add some docs * fix: use the correct nonce The EVM _used_ to start the nonce at zero, but contracts now start the nonce at one. --- actors/evm/src/interpreter/bytecode.rs | 25 +-- actors/evm/src/interpreter/execution.rs | 34 ++-- .../evm/src/interpreter/instructions/call.rs | 13 +- .../src/interpreter/instructions/lifecycle.rs | 36 +--- .../src/interpreter/instructions/storage.rs | 8 +- actors/evm/src/interpreter/system.rs | 189 ++++++++++++++++-- actors/evm/src/lib.rs | 90 ++------- actors/evm/src/state.rs | 18 +- actors/evm/tests/create.rs | 2 +- runtime/src/runtime/fvm.rs | 23 +-- runtime/src/runtime/mod.rs | 32 ++- runtime/src/test_utils.rs | 11 +- test_vm/src/lib.rs | 25 +-- 13 files changed, 290 insertions(+), 216 deletions(-) diff --git a/actors/evm/src/interpreter/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs index 8ca77e0f7..c5f749842 100644 --- a/actors/evm/src/interpreter/bytecode.rs +++ b/actors/evm/src/interpreter/bytecode.rs @@ -1,14 +1,15 @@ #![allow(dead_code)] -use {super::opcode::OpCode, crate::interpreter::output::StatusCode, std::ops::Deref}; +use {super::opcode::OpCode, std::ops::Deref}; -pub struct Bytecode<'c> { - code: &'c [u8], +#[derive(Clone, Debug)] +pub struct Bytecode { + code: Vec, jumpdest: Vec, } -impl<'c> Bytecode<'c> { - pub fn new(bytecode: &'c [u8]) -> Result { +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. // todo: create the jumpdest table only once during initial contract deployment @@ -25,7 +26,7 @@ impl<'c> Bytecode<'c> { } } - Ok(Self { code: bytecode, jumpdest }) + Self { code: bytecode, jumpdest } } /// Checks if the EVM is allowed to jump to this location. @@ -37,16 +38,16 @@ impl<'c> Bytecode<'c> { } } -impl<'c> Deref for Bytecode<'c> { +impl Deref for Bytecode { type Target = [u8]; - fn deref(&self) -> &'c Self::Target { - self.code + fn deref(&self) -> &Self::Target { + &self.code } } -impl<'c> AsRef<[u8]> for Bytecode<'c> { - fn as_ref(&self) -> &'c [u8] { - 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 index ee73251ed..0fada1765 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -51,10 +51,10 @@ impl ExecutionState { } } -struct Machine<'r, BS: Blockstore, RT: Runtime> { - system: &'r mut System<'r, BS, RT>, +pub struct Machine<'r, 'a, BS: Blockstore, RT: Runtime> { + system: &'r mut System<'a, BS, RT>, runtime: &'r mut ExecutionState, - bytecode: &'r Bytecode<'r>, + bytecode: &'r Bytecode, pc: usize, reverted: bool, } @@ -69,9 +69,9 @@ type Instruction = fn(*mut M) -> Result; macro_rules! def_jmptable { ($($op:ident)*) => { - const fn jmptable() -> [Instruction>; 256] { - let mut table: [Instruction>; 256] = [Machine::<'r, BS, RT>::UNDEFINED; 256]; - $(table[OpCode::$op as usize] = Machine::<'r, BS, RT>::$op;)* + const fn jmptable() -> [Instruction>; 256] { + let mut table: [Instruction>; 256] = [Machine::<'r, 'a, BS, RT>::UNDEFINED; 256]; + $(table[OpCode::$op as usize] = Machine::<'r, 'a, BS, RT>::$op;)* table } } @@ -102,9 +102,9 @@ macro_rules! def_ins { } } -impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { +impl<'r, 'a, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, 'a, BS, RT> { pub fn new( - system: &'r mut System<'r, BS, RT>, + system: &'r mut System<'a, BS, RT>, runtime: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { @@ -133,12 +133,7 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { fn step(&mut self) -> Result { let op = OpCode::try_from(self.bytecode[self.pc])?; - Machine::<'r, BS, RT>::dispatch(op)(self) - } - - // Beware, dragons! - fn dispatch(op: OpCode) -> Instruction> { - Self::JMPTABLE[op as usize] + Self::JMPTABLE[op as usize](self) } def_ins! { @@ -872,13 +867,14 @@ impl<'r, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, BS, RT> { } } - const JMPTABLE: [Instruction>; 256] = Machine::<'r, BS, RT>::jmptable(); + const JMPTABLE: [Instruction>; 256] = + Machine::<'r, 'a, BS, RT>::jmptable(); } -pub fn execute<'r, BS: Blockstore, RT: Runtime>( - bytecode: &'r Bytecode, - runtime: &'r mut ExecutionState, - system: &'r mut System<'r, BS, RT>, +pub fn execute>( + bytecode: &Bytecode, + runtime: &mut ExecutionState, + system: &mut System, ) -> Result { let mut m = Machine::new(system, runtime, bytecode); m.execute()?; diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index ad1b46d8b..aa95369fb 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -107,13 +107,12 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod Ok(()) } -pub fn call<'r, BS: Blockstore, RT: Runtime>( +pub fn call>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + platform: &mut System, kind: CallKind, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; - let rt = &*platform.rt; // as immutable reference // NOTE gas is currently ignored as FVM's send doesn't allow the caller to specify a gas // limit (external invocation gas limit applies). This may changed in the future. @@ -151,7 +150,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( }; if precompiles::Precompiles::::is_precompile(&dst) { - precompiles::Precompiles::call_precompile(rt, dst, input_data) + precompiles::Precompiles::call_precompile(platform.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? } else { let dst_addr = EthAddress::try_from(dst)? @@ -159,7 +158,7 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; let call_result = match kind { - CallKind::Call => rt.send( + CallKind::Call => platform.send( &dst_addr, Method::InvokeContract as u64, // TODO: support IPLD codecs #758 @@ -204,9 +203,9 @@ pub fn call<'r, BS: Blockstore, RT: Runtime>( Ok(()) } -pub fn callactor<'r, BS: Blockstore, RT: Runtime>( +pub fn callactor>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + platform: &System, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; let rt = &*platform.rt; // as immutable reference diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 136a31f07..d9e97d037 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -7,7 +7,6 @@ use serde_tuple::{Deserialize_tuple, Serialize_tuple}; use crate::interpreter::stack::Stack; use crate::interpreter::{address::EthAddress, U256}; -use crate::state::State; use super::memory::{get_memory_region, MemoryRegion}; use { @@ -41,9 +40,9 @@ pub struct EamReturn { } #[inline] -pub fn create<'r, BS: Blockstore, RT: Runtime>( +pub fn create>( state: &mut ExecutionState, - platform: &'r mut System<'r, BS, RT>, + platform: &mut System, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; @@ -62,22 +61,14 @@ pub fn create<'r, BS: Blockstore, RT: Runtime>( ))); }; - // bump nonce and flush state before send - let nonce = platform.rt.transaction(|state: &mut State, _rt| { - let nonce = state.nonce; - // this may be redundant if we are compiling with checked integer math - state.nonce = state.nonce.checked_add(1).unwrap(); - Ok(nonce) - })?; - + let nonce = platform.increment_nonce(); let params = CreateParams { code: input_data.to_vec(), nonce }; - create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) } -pub fn create2<'r, BS: Blockstore, RT: Runtime>( +pub fn create2>( state: &mut ExecutionState, - platform: &'r mut System<'r, BS, RT>, + platform: &mut System, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; @@ -103,25 +94,20 @@ pub fn create2<'r, BS: Blockstore, RT: Runtime>( }; let params = Create2Params { code: input_data.to_vec(), salt }; - platform.rt.transaction(|state: &mut State, _rt| { - // this may be redundant if we are compiling with checked integer math - state.nonce = state.nonce.checked_add(1).unwrap(); - Ok(()) - })?; - + platform.increment_nonce(); create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) } /// call into Ethereum Address Manager to make the new account -fn create_init<'r, BS: Blockstore, RT: Runtime>( +fn create_init>( stack: &mut Stack, - platform: &'r mut System<'r, BS, RT>, + platform: &mut System, params: RawBytes, method: MethodNum, value: TokenAmount, ) -> Result<(), StatusCode> { // send bytecode & params to EAM to generate the address and contract - let ret = platform.rt.send(&EAM_ACTOR_ADDR, method, params, value); + let ret = platform.send(&EAM_ACTOR_ADDR, method, params, value); // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 // Normally EVM will do some checks here to ensure that a contract has the capability @@ -155,9 +141,9 @@ fn create_init<'r, BS: Blockstore, RT: Runtime>( } #[inline] -pub fn selfdestruct<'r, BS: Blockstore, RT: Runtime>( +pub fn selfdestruct>( state: &mut ExecutionState, - _system: &'r mut System<'r, BS, RT>, + _system: &mut System, ) -> Result<(), StatusCode> { let beneficiary_addr = EthAddress::try_from(state.stack.pop())?; let id_addr = beneficiary_addr.as_id_address().expect("no support for non-ID addresses yet"); diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 2d1dfcc44..0ea3e1b61 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -5,9 +5,9 @@ use { }; #[inline] -pub fn sload<'r, BS: Blockstore, RT: Runtime>( +pub fn sload>( state: &mut ExecutionState, - platform: &'r mut System<'r, BS, RT>, + platform: &mut System, ) -> Result<(), StatusCode> { // where? let location = state.stack.pop(); @@ -22,9 +22,9 @@ pub fn sload<'r, BS: Blockstore, RT: Runtime>( } #[inline] -pub fn sstore<'r, BS: Blockstore, RT: Runtime>( +pub fn sstore>( state: &mut ExecutionState, - platform: &'r mut System<'r, BS, RT>, + platform: &mut System, ) -> Result<(), StatusCode> { let location = state.stack.pop(); let value = state.stack.pop(); diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 0eaea7052..9cac14539 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -1,9 +1,19 @@ #![allow(dead_code)] -use fil_actors_runtime::EAM_ACTOR_ID; -use fvm_shared::address::{Address, Payload}; +use fil_actors_runtime::{actor_error, runtime::EMPTY_ARR_CID, AsActorError, EAM_ACTOR_ID}; +use fvm_ipld_blockstore::Block; +use fvm_ipld_encoding::{CborStore, RawBytes}; +use fvm_shared::{ + address::{Address, Payload}, + econ::TokenAmount, + error::ExitCode, + MethodNum, IPLD_RAW, +}; +use multihash::Code; + +use crate::state::State; -use super::address::EthAddress; +use super::{address::EthAddress, Bytecode}; use { crate::interpreter::{StatusCode, U256}, @@ -31,22 +41,167 @@ pub enum StorageStatus { /// that bridges the FVM world to EVM world pub struct System<'r, BS: Blockstore, RT: Runtime> { pub rt: &'r mut RT, - state: &'r mut Hamt, + + /// The current bytecode. This is usually only "none" when the actor is first constructed. + bytecode: Option, + /// The contract's EVM storage slots. + slots: Hamt, + /// The contracts "nonce" (incremented when creating new actors). + nonce: u64, + /// The last saved state root. None if the current state hasn't been saved yet. + saved_state_root: Option, } impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { - pub fn new(rt: &'r mut RT, state: &'r mut Hamt) -> anyhow::Result { - Ok(Self { rt, state }) + /// Create the actor. + pub fn create(rt: &'r mut RT) -> Result + where + BS: Clone, + { + 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")); + } + let store = rt.store().clone(); + Ok(Self { + rt, + slots: Hamt::<_, U256, U256>::new(store), + nonce: 1, + saved_state_root: None, + bytecode: None, + }) + } + + /// Load the actor from state. + pub fn load(rt: &'r mut RT) -> Result + where + BS: 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")?; + + Ok(Self { + rt, + slots: Hamt::<_, U256, U256>::load(&state.contract_state, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?, + nonce: state.nonce, + saved_state_root: Some(state_root), + bytecode: Some(state.bytecode), + }) + } + + 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 + } + + /// Send a message, saving and reloading state as necessary. + pub fn send( + &mut self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + ) -> Result { + self.flush()?; + let result = self.rt.send(to, method, params, value)?; + self.reload()?; + Ok(result) } - /// Reborrow the system with a shorter lifetime. - #[allow(clippy::needless_lifetimes)] - pub fn reborrow<'a>(&'a mut self) -> System<'a, BS, RT> { - System { rt: &mut *self.rt, state: &mut *self.state } + /// Flush the actor state (bytecode, nonce, and slots). + pub fn flush(&mut self) -> Result<(), ActorError> { + if self.saved_state_root.is_some() { + return Ok(()); + } + let bytecode_cid = match self.bytecode { + Some(cid) => cid, + None => self.set_bytecode(&[])?, + }; + let new_root = self + .rt + .store() + .put_cbor( + &State { + bytecode: bytecode_cid, + contract_state: self.slots.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush contract state", + )?, + nonce: self.nonce, + }, + 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> { + 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(state.bytecode); + Ok(()) } - pub fn flush_state(&mut self) -> Result { - self.state.flush().map_err(|e| ActorError::illegal_state(e.to_string())) + /// Load the bytecode. + pub fn load_bytecode(&self) -> Result, ActorError> { + match &self.bytecode { + Some(cid) => { + let bytecode = self + .rt + .store() + .get(cid) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to read state")? + .expect("bytecode not in state tree"); + if bytecode.is_empty() { + return Ok(None); + } + Ok(Some(Bytecode::new(bytecode))) + } + None => Ok(None), + } + } + + /// Set the bytecode. + pub fn set_bytecode(&mut self, bytecode: &[u8]) -> Result { + self.saved_state_root = None; + // Reject code starting with 0xEF, EIP-3541 + if bytecode.first() == Some(&0xEF) { + return Err(ActorError::illegal_argument( + "EIP-3541: Contract code starting with the 0xEF byte is disallowed.".into(), + )); + } + let k = self + .rt + .store() + .put(Code::Blake2b256, &Block::new(IPLD_RAW, bytecode)) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to write bytecode")?; + self.bytecode = Some(k); + Ok(k) } /// Get value of a storage key. @@ -54,7 +209,7 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { let mut key_bytes = [0u8; 32]; key.to_big_endian(&mut key_bytes); - Ok(self.state.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned()) + Ok(self.slots.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned()) } /// Set value of a storage key. @@ -63,20 +218,22 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { key: U256, value: Option, ) -> Result { + self.saved_state_root = None; // dirty. + let mut key_bytes = [0u8; 32]; key.to_big_endian(&mut key_bytes); let prev_value = - self.state.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned(); + self.slots.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned(); let mut storage_status = if prev_value == value { StorageStatus::Unchanged } else { StorageStatus::Modified }; if value.is_none() { - self.state.delete(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?; + self.slots.delete(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?; storage_status = StorageStatus::Deleted; } else { - self.state + self.slots .set(key, value.unwrap()) .map_err(|e| StatusCode::InternalError(e.to_string()))?; } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 793718af7..91bbb2d97 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -16,12 +16,11 @@ use { fil_actors_runtime::{ cbor, runtime::{ActorCode, Runtime}, - ActorDowncast, ActorError, + ActorError, }, fvm_ipld_blockstore::Blockstore, fvm_ipld_encoding::tuple::*, fvm_ipld_encoding::RawBytes, - fvm_ipld_hamt::Hamt, fvm_shared::error::*, fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}, num_derive::FromPrimitive, @@ -97,13 +96,7 @@ impl EvmContractActor { return Err(ActorError::illegal_argument("no initcode provided".into())); } - // create an empty storage HAMT to pass it down for execution. - let mut hamt = Hamt::<_, U256, U256>::new(rt.store().clone()); - - // create an instance of the platform abstraction layer -- note: do we even need this? - let mut system = System::new(rt, &mut hamt).map_err(|e| { - ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) - })?; + let mut system = System::create(rt)?; // create a new execution context let mut exec_state = ExecutionState::new( @@ -114,12 +107,11 @@ impl EvmContractActor { ); // identify bytecode valid jump destinations - let initcode = Bytecode::new(¶ms.initcode) - .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; + let initcode = Bytecode::new(params.initcode.into()); // invoke the contract constructor let exec_status = - execute(&initcode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { + execute(&initcode, &mut exec_state, &mut system).map_err(|e| match e { StatusCode::ActorError(e) => e, _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; @@ -137,26 +129,8 @@ impl EvmContractActor { // the resulting bytecode. let contract_bytecode = exec_status.output_data; - // Reject code starting with 0xEF, EIP-3541 - if !contract_bytecode.is_empty() && contract_bytecode[0] == 0xEF { - return Err(ActorError::illegal_argument( - "EIP-3541: Contract code starting with the 0xEF byte is disallowed.".into(), - )); - } - - let contract_state_cid = system.flush_state()?; - - let state = State::new( - rt.store(), - RawBytes::new(contract_bytecode.to_vec()), - contract_state_cid, - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") - })?; - rt.create(&state)?; - - Ok(()) + system.set_bytecode(&contract_bytecode)?; + system.flush() } else if let StatusCode::ActorError(e) = exec_status.status_code { Err(e) } else { @@ -175,28 +149,15 @@ impl EvmContractActor { { rt.validate_immediate_caller_accept_any()?; - let state: State = rt.state()?; - let bytecode: Vec = rt - .store() - .get(&state.bytecode) - .map_err(|e| ActorError::unspecified(format!("failed to load bytecode: {e:?}")))? - .ok_or_else(|| ActorError::unspecified("missing bytecode".to_string()))?; - - let bytecode = Bytecode::new(&bytecode) - .map_err(|e| ActorError::unspecified(format!("failed to parse bytecode: {e:?}")))?; - - // clone the blockstore here to pass to the System, this is bound to the HAMT. - let blockstore = rt.store().clone(); - - // load the storage HAMT - let mut hamt = Hamt::load(&state.contract_state, blockstore).map_err(|e| { - ActorError::illegal_state(format!("failed to load storage HAMT on invoke: {e:?}, e")) - })?; - - let mut system = System::new(rt, &mut hamt).map_err(|e| { + let mut system = System::load(rt).map_err(|e| { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; + let bytecode = match system.load_bytecode()? { + Some(bytecode) => bytecode, + None => return Ok(Vec::new()), // an EVM contract with no code returns immediately + }; + // Resolve the caller's ethereum address. If the caller doesn't have one, the caller's ID is used instead. let caller_fil_addr = system.rt.message().caller(); let caller_eth_addr = system.resolve_ethereum_address(&caller_fil_addr).unwrap(); @@ -213,7 +174,7 @@ impl EvmContractActor { ); let exec_status = - execute(&bytecode, &mut exec_state, &mut system.reborrow()).map_err(|e| match e { + execute(&bytecode, &mut exec_state, &mut system).map_err(|e| match e { StatusCode::ActorError(e) => e, _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; @@ -225,13 +186,7 @@ impl EvmContractActor { "contract reverted".to_string(), )); } else if exec_status.status_code == StatusCode::Success { - // this needs to be outside the transaction or else rustc has a fit about - // mutably borrowing the runtime twice.... sigh. - let contract_state = system.flush_state()?; - rt.transaction(|state: &mut State, _rt| { - state.contract_state = contract_state; - Ok(()) - })?; + system.flush()?; } else if let StatusCode::ActorError(e) = exec_status.status_code { return Err(e); } else { @@ -269,22 +224,7 @@ impl EvmContractActor { // access arbitrary storage keys from a contract. rt.validate_immediate_caller_is([&Address::new_id(0)])?; - let state: State = rt.state()?; - let blockstore = rt.store().clone(); - - // load the storage HAMT - let mut hamt = - Hamt::<_, _, U256>::load(&state.contract_state, blockstore).map_err(|e| { - ActorError::illegal_state(format!( - "failed to load storage HAMT on invoke: {e:?}, e" - )) - })?; - - let mut system = System::new(rt, &mut hamt).map_err(|e| { - ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) - })?; - - system + System::load(rt)? .get_storage(params.storage_key) .map_err(|st| ActorError::unspecified(format!("failed to get storage key: {}", &st)))? .ok_or_else(|| ActorError::not_found(String::from("storage key not found"))) diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 10c2ab131..05adad380 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -1,15 +1,10 @@ use { cid::Cid, - fvm_ipld_blockstore::Block, - fvm_ipld_blockstore::Blockstore, fvm_ipld_encoding::tuple::*, - fvm_ipld_encoding::{Cbor, RawBytes}, - multihash::Code, + fvm_ipld_encoding::Cbor, serde_tuple::{Deserialize_tuple, Serialize_tuple}, }; -pub const RAW: u64 = 0x55; - /// Data stored by an EVM contract. /// This runs on the fvm-evm-runtime actor code cid. #[derive(Debug, Serialize_tuple, Deserialize_tuple)] @@ -29,14 +24,3 @@ pub struct State { } impl Cbor for State {} - -impl State { - pub fn new( - store: &BS, - bytecode: RawBytes, - contract_state: Cid, - ) -> anyhow::Result { - let bytecode_cid = store.put(Code::Blake2b256, &Block::new(RAW, bytecode.to_vec()))?; - Ok(Self { bytecode: bytecode_cid, contract_state, nonce: 0 }) - } -} diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index f1f1ff0f2..5fd8135b7 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -81,7 +81,7 @@ fn test_create() { let mut create_params = CreateParams { code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), - nonce: 0, + nonce: 1, }; // byte 3 is method num diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 1149a18f5..fe8fef8bc 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -33,8 +33,6 @@ use crate::runtime::{ }; use crate::{actor_error, ActorError, Runtime}; -use super::EMPTY_ARR_CID; - /// A runtime that bridges to the FVM environment through the FVM SDK. pub struct FvmRuntime { blockstore: B, @@ -268,25 +266,12 @@ where }) } - fn create(&mut self, obj: &C) -> Result<(), ActorError> { - let root = fvm::sself::root()?; - if root != EMPTY_ARR_CID { - return Err( - actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root), - ); - } - let new_root = ActorBlockstore.put_cbor(obj, Code::Blake2b256) - .map_err(|e| actor_error!(illegal_argument; "failed to write actor state during creation: {}", e.to_string()))?; - fvm::sself::set_root(&new_root)?; - Ok(()) + fn get_state_root(&self) -> Result { + Ok(fvm::sself::root()?) } - fn state(&self) -> Result { - let root = fvm::sself::root()?; - Ok(ActorBlockstore - .get_cbor(&root) - .map_err(|_| actor_error!(illegal_argument; "failed to get actor for Readonly state"))? - .expect("State does not exist for actor state root")) + fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> { + Ok(fvm::sself::set_root(root)?) } fn transaction(&mut self, f: F) -> Result diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index a9ba31cd7..42c7d41fc 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -3,7 +3,7 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::{Cbor, RawBytes}; +use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; @@ -20,12 +20,13 @@ use fvm_shared::sector::{ }; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; +use multihash::Code; pub use self::actor_code::*; pub use self::policy::*; pub use self::randomness::DomainSeparationTag; use crate::runtime::builtins::Type; -use crate::ActorError; +use crate::{actor_error, ActorError}; mod actor_code; pub mod builtins; @@ -112,10 +113,33 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Initializes the state object. /// This is only valid when the state has not yet been initialized. /// NOTE: we should also limit this to being invoked during the constructor method - fn create(&mut self, obj: &C) -> Result<(), ActorError>; + fn create(&mut self, obj: &C) -> Result<(), ActorError> { + let root = self.get_state_root()?; + if root != EMPTY_ARR_CID { + return Err( + actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root), + ); + } + let new_root = self.store().put_cbor(obj, Code::Blake2b256) + .map_err(|e| actor_error!(illegal_argument; "failed to write actor state during creation: {}", e.to_string()))?; + self.set_state_root(&new_root)?; + Ok(()) + } /// Loads a readonly copy of the state of the receiver into the argument. - fn state(&self) -> Result; + fn state(&self) -> Result { + Ok(self + .store() + .get_cbor(&self.get_state_root()?) + .map_err(|_| actor_error!(illegal_argument; "failed to get actor for Readonly state"))? + .expect("State does not exist for actor state root")) + } + + /// Gets the state-root. + fn get_state_root(&self) -> Result; + + /// Sets the state-root. + fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError>; /// Loads a mutable copy of the state of the receiver, passes it to `f`, /// and after `f` completes puts the state object back to the store and sets it as diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 9b48d8da3..8ebc6a93a 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -39,7 +39,7 @@ use rand::prelude::*; use crate::runtime::builtins::Type; use crate::runtime::{ ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy, - Verifier, + Verifier, EMPTY_ARR_CID, }; use crate::{actor_error, ActorError}; use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; @@ -1010,6 +1010,15 @@ impl Runtime> for MockRuntime { Ok(self.store_get(self.state.as_ref().unwrap())) } + fn get_state_root(&self) -> Result { + Ok(self.state.unwrap_or(EMPTY_ARR_CID)) + } + + fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> { + self.state = Some(*root); + Ok(()) + } + fn transaction(&mut self, f: F) -> Result where C: Cbor, diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index e87538dc5..dbdd62c1b 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -920,32 +920,25 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().into())) } - fn create(&mut self, obj: &C) -> Result<(), ActorError> { + fn get_state_root(&self) -> Result { + Ok(self.v.get_actor(self.to()).unwrap().head) + } + + fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> { let maybe_act = self.v.get_actor(self.to()); match maybe_act { None => Err(ActorError::unchecked( ExitCode::SYS_ASSERTION_FAILED, - "failed to create state".to_string(), + "actor does not exist".to_string(), )), Some(mut act) => { - if act.head != EMPTY_ARR_CID { - Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, - "failed to construct state: already initialized".to_string(), - )) - } else { - act.head = self.v.store.put_cbor(obj, Code::Blake2b256).unwrap(); - self.v.set_actor(self.to(), act); - Ok(()) - } + act.head = *root; + self.v.set_actor(self.to(), act); + Ok(()) } } } - fn state(&self) -> Result { - Ok(self.v.get_state::(self.to()).unwrap()) - } - fn transaction(&mut self, f: F) -> Result where C: Cbor, From e4d503c7074e96b59d5f4c40fa3c1897b787a83a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 19 Oct 2022 12:44:46 -0700 Subject: [PATCH 081/339] EVM: add an evm recursive call test (#767) * fix: implement Cbor for Create2/Create params/return * test: add an EVM call test --- Cargo.lock | 2 + actors/eam/src/lib.rs | 7 ++ actors/evm/tests/contracts/Recursive.abi | 1 + actors/evm/tests/contracts/Recursive.bin | 1 + actors/evm/tests/contracts/Recursive.hex | 1 + .../evm/tests/contracts/Recursive.signatures | 3 + actors/evm/tests/contracts/Recursive.sol | 38 ++++++++ .../tests/contracts/Recursive_storage.json | 1 + actors/evm/tests/contracts/SimpleCoin.bin | 2 +- actors/evm/tests/contracts/simplecoin.hex | 2 +- actors/evm/tests/contracts/simplecoin.sol | 2 +- test_vm/Cargo.toml | 2 + test_vm/src/lib.rs | 25 +++--- test_vm/tests/evm_test.rs | 88 +++++++++++++++++++ 14 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 actors/evm/tests/contracts/Recursive.abi create mode 100644 actors/evm/tests/contracts/Recursive.bin create mode 100644 actors/evm/tests/contracts/Recursive.hex create mode 100644 actors/evm/tests/contracts/Recursive.signatures create mode 100644 actors/evm/tests/contracts/Recursive.sol create mode 100644 actors/evm/tests/contracts/Recursive_storage.json create mode 100644 test_vm/tests/evm_test.rs diff --git a/Cargo.lock b/Cargo.lock index 19260283f..ca157b66e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3590,6 +3590,7 @@ dependencies = [ "bimap", "blake2b_simd", "cid", + "ethers", "fil_actor_account", "fil_actor_cron", "fil_actor_eam", @@ -3610,6 +3611,7 @@ dependencies = [ "fvm_ipld_encoding", "fvm_ipld_hamt", "fvm_shared", + "hex", "indexmap", "integer-encoding", "lazy_static", diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 2a69d38e2..5051a3b15 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -1,6 +1,7 @@ use std::iter; use ext::init::{Exec4Params, Exec4Return}; +use fvm_ipld_encoding::Cbor; use rlp::Encodable; pub mod ext; @@ -67,11 +68,14 @@ pub struct Create2Params { pub salt: [u8; 32], } +impl Cbor for Create2Params {} + #[derive(Serialize_tuple, Deserialize_tuple)] pub struct InitAccountParams { #[serde(with = "strict_bytes")] pub pubkey: [u8; SECP_PUB_LEN], } +impl Cbor for InitAccountParams {} #[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq)] pub struct Return { @@ -79,6 +83,9 @@ pub struct Return { pub robust_address: Address, pub eth_address: EthAddress, } + +impl Cbor for Return {} + pub type CreateReturn = Return; pub type Create2Return = Return; 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/SimpleCoin.bin b/actors/evm/tests/contracts/SimpleCoin.bin index d541d643d..55b83cb12 100644 --- a/actors/evm/tests/contracts/SimpleCoin.bin +++ b/actors/evm/tests/contracts/SimpleCoin.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220bae09d124baa1fa30544534027cf2c2de125781c15a22bba297d7013f7c662b464736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/simplecoin.hex b/actors/evm/tests/contracts/simplecoin.hex index d541d643d..55b83cb12 100644 --- a/actors/evm/tests/contracts/simplecoin.hex +++ b/actors/evm/tests/contracts/simplecoin.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea2646970667358221220bae09d124baa1fa30544534027cf2c2de125781c15a22bba297d7013f7c662b464736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/simplecoin.sol b/actors/evm/tests/contracts/simplecoin.sol index dc4564075..5318b0cb8 100644 --- a/actors/evm/tests/contracts/simplecoin.sol +++ b/actors/evm/tests/contracts/simplecoin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.4.25 <=0.8.15; +pragma solidity >=0.4.2; contract SimpleCoin { mapping(address => uint256) balances; diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index b66c74c75..676271035 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -50,6 +50,8 @@ regex = "1" [dev-dependencies] cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } +ethers = { version = "0.17.0", features = ["abigen"] } +hex = "0.4.3" [features] m2-native = [] diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index dbdd62c1b..c626faae4 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -53,7 +53,7 @@ use fvm_shared::sector::{ }; use fvm_shared::smooth::FilterEstimate; use fvm_shared::version::NetworkVersion; -use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, IPLD_RAW, METHOD_CONSTRUCTOR, METHOD_SEND}; use multihash::MultihashDigest; use regex::Regex; use serde::ser; @@ -436,11 +436,15 @@ impl<'bs> VM<'bs> { match res { Err(ae) => { self.rollback(prior_root); - Ok(MessageResult { code: ae.exit_code(), ret: RawBytes::default() }) + Ok(MessageResult { + code: ae.exit_code(), + ret: RawBytes::default(), + error_message: Some(ae.msg().into()), + }) } Ok(ret) => { self.checkpoint(); - Ok(MessageResult { code: ExitCode::OK, ret }) + Ok(MessageResult { code: ExitCode::OK, ret, error_message: None }) } } } @@ -540,10 +544,10 @@ impl MessageInfo for InvocationCtx<'_, '_> { self.msg.value.clone() } fn gas_limit(&self) -> u64 { - todo!() + u32::MAX.into() } fn gas_premium(&self) -> TokenAmount { - todo!() + TokenAmount::zero() } } @@ -986,20 +990,20 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat TokenAmount::zero() } - fn actor_balance(&self, _id: ActorID) -> Option { - todo!() + fn actor_balance(&self, id: ActorID) -> Option { + self.v.get_actor(Address::new_id(id)).map(|act| act.balance) } fn gas_available(&self) -> u64 { - todo!() + u32::MAX.into() } fn tipset_timestamp(&self) -> u64 { - todo!() + 0 } fn tipset_cid(&self, _epoch: i64) -> Option { - todo!() + Some(Cid::new_v1(IPLD_RAW, Multihash::wrap(0, b"faketipset").unwrap())) } } @@ -1151,6 +1155,7 @@ impl RuntimePolicy for InvocationCtx<'_, '_> { pub struct MessageResult { pub code: ExitCode, pub ret: RawBytes, + pub error_message: Option, } #[derive(Serialize_tuple, Deserialize_tuple, Clone, PartialEq, Eq, Debug)] diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs new file mode 100644 index 000000000..0e937ddc7 --- /dev/null +++ b/test_vm/tests/evm_test.rs @@ -0,0 +1,88 @@ +use std::sync::Arc; + +use ethers::core::types::Address as EthAddress; +use ethers::prelude::abigen; +use ethers::providers::Provider; +use fil_actors_runtime::EAM_ACTOR_ADDR; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor}; +use fvm_shared::econ::TokenAmount; +use fvm_shared::ActorID; +use num_traits::Zero; +use serde::{Deserialize, Serialize}; +use test_vm::{util::create_accounts, VM}; + +// Generate a statically typed interface for the contract. +abigen!(Recursive, "../actors/evm/tests/contracts/Recursive.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) +} + +// TODO: we should move this somewhere else, or just find a way to avoid this. Unfortunately, "BytesSer" doesn't implemenet `Cbor`. +// Really, we should consider getting rid of `Cbor` entirely. + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +struct ContractParams(#[serde(with = "strict_bytes")] pub Vec); + +impl Cbor for ContractParams {} + +#[test] +fn test_evm_lifecycle() { + 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::Create2 as u64, + fil_actor_eam::Create2Params { initcode: bytecode, salt: [0u8; 32] }, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.error_message.unwrap() + ); + + let create_return: fil_actor_eam::Create2Return = + create_result.ret.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, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + ContractParams(contract_params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.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"); +} From 21c1291c50eeaed062d36d57b04f25b8926ecef0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 19 Oct 2022 14:01:06 -0700 Subject: [PATCH 082/339] chore: update storage measurement plots (#769) (for recent changes) --- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 16330 -> 15926 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 21008 -> 22390 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 16527 -> 14782 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 19889 -> 18131 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 16258 -> 16842 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 15108 -> 15291 bytes .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 16110 -> 16168 bytes .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 21305 -> 23236 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 23406 -> 22925 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 19046 -> 17700 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 20467 -> 19391 bytes 22 files changed, 1620 insertions(+), 1620 deletions(-) diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 99d4334c9..ba364a1bc 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2288,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2285,"get_count":4,"put_bytes":197,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2293,"get_count":4,"put_bytes":205,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2301,"get_count":4,"put_bytes":214,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2310,"get_count":4,"put_bytes":222,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2318,"get_count":4,"put_bytes":230,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2326,"get_count":4,"put_bytes":238,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2334,"get_count":4,"put_bytes":246,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2342,"get_count":4,"put_bytes":334,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":342,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2438,"get_count":4,"put_bytes":350,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2446,"get_count":4,"put_bytes":359,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2455,"get_count":4,"put_bytes":367,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":375,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2471,"get_count":4,"put_bytes":383,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":2479,"get_count":4,"put_bytes":392,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":466,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":2562,"get_count":4,"put_bytes":474,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2570,"get_count":4,"put_bytes":482,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2578,"get_count":4,"put_bytes":491,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":2587,"get_count":4,"put_bytes":499,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":507,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2603,"get_count":4,"put_bytes":515,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2611,"get_count":4,"put_bytes":523,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":2619,"get_count":4,"put_bytes":597,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":2693,"get_count":4,"put_bytes":605,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":2701,"get_count":4,"put_bytes":613,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":2709,"get_count":4,"put_bytes":622,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":2718,"get_count":4,"put_bytes":630,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":2726,"get_count":4,"put_bytes":638,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":2734,"get_count":4,"put_bytes":646,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":2742,"get_count":4,"put_bytes":654,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":2750,"get_count":4,"put_bytes":727,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":2823,"get_count":4,"put_bytes":735,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":2831,"get_count":4,"put_bytes":743,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":2839,"get_count":4,"put_bytes":752,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":2848,"get_count":4,"put_bytes":760,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":2856,"get_count":4,"put_bytes":768,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":2864,"get_count":4,"put_bytes":776,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":2872,"get_count":4,"put_bytes":784,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":2880,"get_count":4,"put_bytes":858,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":2954,"get_count":4,"put_bytes":866,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":2962,"get_count":4,"put_bytes":874,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":2970,"get_count":4,"put_bytes":883,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":2979,"get_count":4,"put_bytes":891,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":2987,"get_count":4,"put_bytes":899,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":2995,"get_count":4,"put_bytes":907,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":3003,"get_count":4,"put_bytes":915,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":3011,"get_count":4,"put_bytes":990,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3086,"get_count":4,"put_bytes":998,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":3094,"get_count":4,"put_bytes":1006,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3102,"get_count":4,"put_bytes":1015,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":3111,"get_count":4,"put_bytes":1023,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3119,"get_count":4,"put_bytes":1031,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":3127,"get_count":4,"put_bytes":1039,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":3135,"get_count":4,"put_bytes":1047,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":3143,"get_count":4,"put_bytes":1121,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3217,"get_count":4,"put_bytes":1129,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3225,"get_count":4,"put_bytes":1137,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":3233,"get_count":4,"put_bytes":1146,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":3242,"get_count":4,"put_bytes":1154,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":3250,"get_count":4,"put_bytes":1162,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":3258,"get_count":4,"put_bytes":1170,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3266,"get_count":4,"put_bytes":1178,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":3274,"get_count":4,"put_bytes":1252,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":3348,"get_count":4,"put_bytes":1260,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":3356,"get_count":4,"put_bytes":1268,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":3364,"get_count":4,"put_bytes":1277,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":3373,"get_count":4,"put_bytes":1285,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":3381,"get_count":4,"put_bytes":1293,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":3389,"get_count":4,"put_bytes":1301,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":3397,"get_count":4,"put_bytes":1309,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":3405,"get_count":4,"put_bytes":1383,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":3479,"get_count":4,"put_bytes":1391,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":3487,"get_count":4,"put_bytes":1399,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":3495,"get_count":4,"put_bytes":1408,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":3504,"get_count":4,"put_bytes":1416,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":3512,"get_count":4,"put_bytes":1424,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":3520,"get_count":4,"put_bytes":1432,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":3528,"get_count":4,"put_bytes":1440,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":3536,"get_count":4,"put_bytes":1514,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":3610,"get_count":4,"put_bytes":1522,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":3618,"get_count":4,"put_bytes":1530,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":3626,"get_count":4,"put_bytes":1539,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":3635,"get_count":4,"put_bytes":1547,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":3643,"get_count":4,"put_bytes":1555,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":3651,"get_count":4,"put_bytes":1563,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":3659,"get_count":4,"put_bytes":1571,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":3667,"get_count":4,"put_bytes":1645,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":3741,"get_count":4,"put_bytes":1653,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1661,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":3757,"get_count":4,"put_bytes":1670,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":3766,"get_count":4,"put_bytes":1678,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":3774,"get_count":4,"put_bytes":1686,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":3782,"get_count":4,"put_bytes":1694,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":3790,"get_count":4,"put_bytes":1702,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1776,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":3872,"get_count":4,"put_bytes":1784,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":3880,"get_count":4,"put_bytes":1792,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":3888,"get_count":4,"put_bytes":1801,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":3897,"get_count":4,"put_bytes":1885,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":3981,"get_count":4,"put_bytes":1893,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3989,"get_count":4,"put_bytes":1901,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3997,"get_count":4,"put_bytes":1910,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":1918,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":4014,"get_count":4,"put_bytes":1926,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":4022,"get_count":4,"put_bytes":1934,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":4030,"get_count":4,"put_bytes":1942,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":4038,"get_count":4,"put_bytes":2016,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":4112,"get_count":4,"put_bytes":2024,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":4120,"get_count":4,"put_bytes":2032,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":4128,"get_count":4,"put_bytes":2041,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":4137,"get_count":4,"put_bytes":2049,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":4145,"get_count":4,"put_bytes":2057,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":4153,"get_count":4,"put_bytes":2065,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":4161,"get_count":4,"put_bytes":2074,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":4170,"get_count":4,"put_bytes":2147,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":4243,"get_count":4,"put_bytes":2155,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":4251,"get_count":4,"put_bytes":2163,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2172,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":4268,"get_count":4,"put_bytes":2180,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2188,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2196,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":4292,"get_count":4,"put_bytes":2204,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2278,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":4374,"get_count":4,"put_bytes":2286,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":4382,"get_count":4,"put_bytes":2294,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":4390,"get_count":4,"put_bytes":2303,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2311,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":4407,"get_count":4,"put_bytes":2319,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2327,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2335,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":4431,"get_count":4,"put_bytes":2409,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":4505,"get_count":4,"put_bytes":2417,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":4513,"get_count":4,"put_bytes":2425,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":4521,"get_count":4,"put_bytes":2434,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2442,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":4538,"get_count":4,"put_bytes":2450,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":4546,"get_count":4,"put_bytes":2458,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":4554,"get_count":4,"put_bytes":2466,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":4562,"get_count":4,"put_bytes":2540,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":4636,"get_count":4,"put_bytes":2548,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":4644,"get_count":4,"put_bytes":2556,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2565,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":4661,"get_count":4,"put_bytes":2573,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":4669,"get_count":4,"put_bytes":2581,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":4677,"get_count":4,"put_bytes":2589,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":4685,"get_count":4,"put_bytes":2597,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2673,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":4769,"get_count":4,"put_bytes":2681,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":4777,"get_count":4,"put_bytes":2689,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":4785,"get_count":4,"put_bytes":2698,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":4794,"get_count":4,"put_bytes":2706,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":4802,"get_count":4,"put_bytes":2714,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":4810,"get_count":4,"put_bytes":2722,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":4818,"get_count":4,"put_bytes":2730,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":4826,"get_count":4,"put_bytes":2804,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":4900,"get_count":4,"put_bytes":2812,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":4908,"get_count":4,"put_bytes":2820,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":4916,"get_count":4,"put_bytes":2829,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":4925,"get_count":4,"put_bytes":2837,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":4933,"get_count":4,"put_bytes":2845,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":4941,"get_count":4,"put_bytes":2853,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":4949,"get_count":4,"put_bytes":2861,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":4957,"get_count":4,"put_bytes":2935,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":5031,"get_count":4,"put_bytes":2943,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":5039,"get_count":4,"put_bytes":2951,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":5047,"get_count":4,"put_bytes":2960,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":5056,"get_count":4,"put_bytes":2968,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":5064,"get_count":4,"put_bytes":2976,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":5072,"get_count":4,"put_bytes":2984,"put_count":2}} -{"i":71,"series":2,"stats":{"get_bytes":5080,"get_count":4,"put_bytes":2992,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":5088,"get_count":4,"put_bytes":3066,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":5162,"get_count":4,"put_bytes":3074,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":5170,"get_count":4,"put_bytes":3082,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":5178,"get_count":4,"put_bytes":3091,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":5187,"get_count":4,"put_bytes":3099,"put_count":2}} -{"i":77,"series":2,"stats":{"get_bytes":5195,"get_count":4,"put_bytes":3107,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":5203,"get_count":4,"put_bytes":3115,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":5211,"get_count":4,"put_bytes":3123,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":5219,"get_count":4,"put_bytes":3198,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":5294,"get_count":4,"put_bytes":3206,"put_count":2}} -{"i":82,"series":2,"stats":{"get_bytes":5302,"get_count":4,"put_bytes":3214,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":5310,"get_count":4,"put_bytes":3223,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":5319,"get_count":4,"put_bytes":3231,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":5327,"get_count":4,"put_bytes":3239,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":5335,"get_count":4,"put_bytes":3247,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":5343,"get_count":4,"put_bytes":3255,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":5351,"get_count":4,"put_bytes":3328,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":5424,"get_count":4,"put_bytes":3336,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":5432,"get_count":4,"put_bytes":3344,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":5440,"get_count":4,"put_bytes":3353,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":5449,"get_count":4,"put_bytes":3361,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":5457,"get_count":4,"put_bytes":3369,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":5465,"get_count":4,"put_bytes":3377,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":5473,"get_count":4,"put_bytes":3385,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":5481,"get_count":4,"put_bytes":3459,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":5555,"get_count":4,"put_bytes":3467,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":5563,"get_count":4,"put_bytes":3475,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":5571,"get_count":4,"put_bytes":3484,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2288,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2197,"get_count":3,"put_bytes":197,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2205,"get_count":3,"put_bytes":205,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2213,"get_count":3,"put_bytes":214,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2222,"get_count":3,"put_bytes":222,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2230,"get_count":3,"put_bytes":230,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":238,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":246,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2254,"get_count":3,"put_bytes":334,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2342,"get_count":3,"put_bytes":342,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2350,"get_count":3,"put_bytes":350,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2358,"get_count":3,"put_bytes":359,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2367,"get_count":3,"put_bytes":367,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2375,"get_count":3,"put_bytes":375,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2383,"get_count":3,"put_bytes":383,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":2391,"get_count":3,"put_bytes":392,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":2400,"get_count":3,"put_bytes":466,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":2474,"get_count":3,"put_bytes":474,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2482,"get_count":3,"put_bytes":482,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2490,"get_count":3,"put_bytes":491,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":2499,"get_count":3,"put_bytes":499,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2507,"get_count":3,"put_bytes":507,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2515,"get_count":3,"put_bytes":515,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2523,"get_count":3,"put_bytes":523,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":2531,"get_count":3,"put_bytes":597,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":2605,"get_count":3,"put_bytes":605,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":2613,"get_count":3,"put_bytes":613,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":2621,"get_count":3,"put_bytes":622,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":2630,"get_count":3,"put_bytes":630,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2638,"get_count":3,"put_bytes":638,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2646,"get_count":3,"put_bytes":646,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":2654,"get_count":3,"put_bytes":654,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":2662,"get_count":3,"put_bytes":727,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":2735,"get_count":3,"put_bytes":735,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":2743,"get_count":3,"put_bytes":743,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":2751,"get_count":3,"put_bytes":752,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":2760,"get_count":3,"put_bytes":760,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":2768,"get_count":3,"put_bytes":768,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":2776,"get_count":3,"put_bytes":776,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":2784,"get_count":3,"put_bytes":784,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":2792,"get_count":3,"put_bytes":858,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":2866,"get_count":3,"put_bytes":866,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":2874,"get_count":3,"put_bytes":874,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":2882,"get_count":3,"put_bytes":883,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":2891,"get_count":3,"put_bytes":891,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":2899,"get_count":3,"put_bytes":899,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":2907,"get_count":3,"put_bytes":907,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":2915,"get_count":3,"put_bytes":915,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":2923,"get_count":3,"put_bytes":990,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":2998,"get_count":3,"put_bytes":998,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":3006,"get_count":3,"put_bytes":1006,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3014,"get_count":3,"put_bytes":1015,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":3023,"get_count":3,"put_bytes":1023,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3031,"get_count":3,"put_bytes":1031,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1039,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":3047,"get_count":3,"put_bytes":1047,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":3055,"get_count":3,"put_bytes":1121,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3129,"get_count":3,"put_bytes":1129,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3137,"get_count":3,"put_bytes":1137,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":3145,"get_count":3,"put_bytes":1146,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":3154,"get_count":3,"put_bytes":1154,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3162,"get_count":3,"put_bytes":1162,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":3170,"get_count":3,"put_bytes":1170,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3178,"get_count":3,"put_bytes":1178,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":3186,"get_count":3,"put_bytes":1252,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1260,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":3268,"get_count":3,"put_bytes":1268,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":3276,"get_count":3,"put_bytes":1277,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":3285,"get_count":3,"put_bytes":1285,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":3293,"get_count":3,"put_bytes":1293,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":3301,"get_count":3,"put_bytes":1301,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":3309,"get_count":3,"put_bytes":1309,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":3317,"get_count":3,"put_bytes":1383,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":3391,"get_count":3,"put_bytes":1391,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":3399,"get_count":3,"put_bytes":1399,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":3407,"get_count":3,"put_bytes":1408,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":3416,"get_count":3,"put_bytes":1416,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":3424,"get_count":3,"put_bytes":1424,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":3432,"get_count":3,"put_bytes":1432,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":3440,"get_count":3,"put_bytes":1440,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":3448,"get_count":3,"put_bytes":1514,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":3522,"get_count":3,"put_bytes":1522,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1530,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":3538,"get_count":3,"put_bytes":1539,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":3547,"get_count":3,"put_bytes":1547,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":3555,"get_count":3,"put_bytes":1555,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":3563,"get_count":3,"put_bytes":1563,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":3571,"get_count":3,"put_bytes":1571,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":3579,"get_count":3,"put_bytes":1645,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":3653,"get_count":3,"put_bytes":1653,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":3661,"get_count":3,"put_bytes":1661,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":3669,"get_count":3,"put_bytes":1670,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":3678,"get_count":3,"put_bytes":1678,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":3686,"get_count":3,"put_bytes":1686,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":3694,"get_count":3,"put_bytes":1694,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":3702,"get_count":3,"put_bytes":1702,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":3710,"get_count":3,"put_bytes":1776,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":3784,"get_count":3,"put_bytes":1784,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":3792,"get_count":3,"put_bytes":1792,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":3800,"get_count":3,"put_bytes":1801,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":3809,"get_count":3,"put_bytes":1885,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3893,"get_count":3,"put_bytes":1893,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3901,"get_count":3,"put_bytes":1901,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3909,"get_count":3,"put_bytes":1910,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3918,"get_count":3,"put_bytes":1918,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3926,"get_count":3,"put_bytes":1926,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3934,"get_count":3,"put_bytes":1934,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":3942,"get_count":3,"put_bytes":1942,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":3950,"get_count":3,"put_bytes":2016,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":4024,"get_count":3,"put_bytes":2024,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":4032,"get_count":3,"put_bytes":2032,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":4040,"get_count":3,"put_bytes":2041,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":4049,"get_count":3,"put_bytes":2049,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":4057,"get_count":3,"put_bytes":2057,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":4065,"get_count":3,"put_bytes":2065,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":4073,"get_count":3,"put_bytes":2074,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":4082,"get_count":3,"put_bytes":2147,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":4155,"get_count":3,"put_bytes":2155,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":4163,"get_count":3,"put_bytes":2163,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":4171,"get_count":3,"put_bytes":2172,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":4180,"get_count":3,"put_bytes":2180,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":4188,"get_count":3,"put_bytes":2188,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":4196,"get_count":3,"put_bytes":2196,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":4204,"get_count":3,"put_bytes":2204,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":4212,"get_count":3,"put_bytes":2278,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":4286,"get_count":3,"put_bytes":2286,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":4294,"get_count":3,"put_bytes":2294,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":4302,"get_count":3,"put_bytes":2303,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":4311,"get_count":3,"put_bytes":2311,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":4319,"get_count":3,"put_bytes":2319,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":4327,"get_count":3,"put_bytes":2327,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":4335,"get_count":3,"put_bytes":2335,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":4343,"get_count":3,"put_bytes":2409,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":4417,"get_count":3,"put_bytes":2417,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":4425,"get_count":3,"put_bytes":2425,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":4433,"get_count":3,"put_bytes":2434,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":4442,"get_count":3,"put_bytes":2442,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":4450,"get_count":3,"put_bytes":2450,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":4458,"get_count":3,"put_bytes":2458,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":4466,"get_count":3,"put_bytes":2466,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":4474,"get_count":3,"put_bytes":2540,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":4548,"get_count":3,"put_bytes":2548,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":4556,"get_count":3,"put_bytes":2556,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":4564,"get_count":3,"put_bytes":2565,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":4573,"get_count":3,"put_bytes":2573,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":4581,"get_count":3,"put_bytes":2581,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":4589,"get_count":3,"put_bytes":2589,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":4597,"get_count":3,"put_bytes":2597,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":4605,"get_count":3,"put_bytes":2673,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":4681,"get_count":3,"put_bytes":2681,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":4689,"get_count":3,"put_bytes":2689,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":4697,"get_count":3,"put_bytes":2698,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":4706,"get_count":3,"put_bytes":2706,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":4714,"get_count":3,"put_bytes":2714,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":4722,"get_count":3,"put_bytes":2722,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":4730,"get_count":3,"put_bytes":2730,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":4738,"get_count":3,"put_bytes":2804,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":4812,"get_count":3,"put_bytes":2812,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":4820,"get_count":3,"put_bytes":2820,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":4828,"get_count":3,"put_bytes":2829,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":4837,"get_count":3,"put_bytes":2837,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":4845,"get_count":3,"put_bytes":2845,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":4853,"get_count":3,"put_bytes":2853,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":4861,"get_count":3,"put_bytes":2861,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":4869,"get_count":3,"put_bytes":2935,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":4943,"get_count":3,"put_bytes":2943,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":4951,"get_count":3,"put_bytes":2951,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":4959,"get_count":3,"put_bytes":2960,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":4968,"get_count":3,"put_bytes":2968,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":4976,"get_count":3,"put_bytes":2976,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":4984,"get_count":3,"put_bytes":2984,"put_count":2}} +{"i":71,"series":2,"stats":{"get_bytes":4992,"get_count":3,"put_bytes":2992,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":5000,"get_count":3,"put_bytes":3066,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":5074,"get_count":3,"put_bytes":3074,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":5082,"get_count":3,"put_bytes":3082,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":5090,"get_count":3,"put_bytes":3091,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":5099,"get_count":3,"put_bytes":3099,"put_count":2}} +{"i":77,"series":2,"stats":{"get_bytes":5107,"get_count":3,"put_bytes":3107,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":5115,"get_count":3,"put_bytes":3115,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":5123,"get_count":3,"put_bytes":3123,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":5131,"get_count":3,"put_bytes":3198,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":5206,"get_count":3,"put_bytes":3206,"put_count":2}} +{"i":82,"series":2,"stats":{"get_bytes":5214,"get_count":3,"put_bytes":3214,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":5222,"get_count":3,"put_bytes":3223,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":5231,"get_count":3,"put_bytes":3231,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":5239,"get_count":3,"put_bytes":3239,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":5247,"get_count":3,"put_bytes":3247,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":5255,"get_count":3,"put_bytes":3255,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":5263,"get_count":3,"put_bytes":3328,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":5336,"get_count":3,"put_bytes":3336,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":5344,"get_count":3,"put_bytes":3344,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":5352,"get_count":3,"put_bytes":3353,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":5361,"get_count":3,"put_bytes":3361,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":5369,"get_count":3,"put_bytes":3369,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":5377,"get_count":3,"put_bytes":3377,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":5385,"get_count":3,"put_bytes":3385,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":5393,"get_count":3,"put_bytes":3459,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":5467,"get_count":3,"put_bytes":3467,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":5475,"get_count":3,"put_bytes":3475,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":5483,"get_count":3,"put_bytes":3484,"put_count":2}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index 58b583e024c79b0b59eb1193f3acf0cfd49dfc55..4effdef85d5d7d63f55cf0b921c4f5fbdb4e8705 100644 GIT binary patch literal 15926 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 Pu_B#&hIeb!p^^U|Rz6Ek literal 16330 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 diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 49c4792cf..7bca6192f 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":3912,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":3909,"get_count":4,"put_bytes":3429,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":5525,"get_count":4,"put_bytes":5114,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":7210,"get_count":4,"put_bytes":6724,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":8820,"get_count":4,"put_bytes":8406,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":10502,"get_count":4,"put_bytes":10020,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":12116,"get_count":4,"put_bytes":11698,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":13794,"get_count":4,"put_bytes":13310,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":15406,"get_count":4,"put_bytes":14988,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":17084,"get_count":4,"put_bytes":16743,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":17729,"get_count":4,"put_bytes":17309,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":19405,"get_count":4,"put_bytes":18918,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":21014,"get_count":4,"put_bytes":20595,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":22691,"get_count":4,"put_bytes":22266,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":23817,"get_count":4,"put_bytes":23397,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":25493,"get_count":4,"put_bytes":25005,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":27101,"get_count":4,"put_bytes":26761,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":28299,"get_count":4,"put_bytes":28040,"put_count":5}} -{"i":18,"series":1,"stats":{"get_bytes":28457,"get_count":4,"put_bytes":28036,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":30132,"get_count":4,"put_bytes":29721,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":31818,"get_count":5,"put_bytes":31397,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":32798,"get_count":4,"put_bytes":32374,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":33920,"get_count":4,"put_bytes":33499,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":36153,"get_count":5,"put_bytes":35661,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":37068,"get_count":4,"put_bytes":36718,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":38261,"get_count":4,"put_bytes":37840,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":39385,"get_count":4,"put_bytes":38961,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":41607,"get_count":5,"put_bytes":41283,"put_count":5}} -{"i":28,"series":1,"stats":{"get_bytes":41568,"get_count":4,"put_bytes":41218,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":43874,"get_count":6,"put_bytes":43606,"put_count":7}} -{"i":30,"series":1,"stats":{"get_bytes":43206,"get_count":5,"put_bytes":43018,"put_count":6}} -{"i":31,"series":1,"stats":{"get_bytes":43306,"get_count":5,"put_bytes":43186,"put_count":8}} -{"i":32,"series":1,"stats":{"get_bytes":42514,"get_count":5,"put_bytes":42162,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":44127,"get_count":6,"put_bytes":43764,"put_count":6}} -{"i":34,"series":1,"stats":{"get_bytes":44511,"get_count":6,"put_bytes":44174,"put_count":5}} -{"i":35,"series":1,"stats":{"get_bytes":45441,"get_count":6,"put_bytes":45109,"put_count":6}} -{"i":36,"series":1,"stats":{"get_bytes":45811,"get_count":6,"put_bytes":45463,"put_count":5}} -{"i":37,"series":1,"stats":{"get_bytes":47561,"get_count":7,"put_bytes":47306,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":47730,"get_count":7,"put_bytes":47443,"put_count":7}} -{"i":39,"series":1,"stats":{"get_bytes":46397,"get_count":5,"put_bytes":46072,"put_count":5}} -{"i":40,"series":1,"stats":{"get_bytes":48986,"get_count":8,"put_bytes":48642,"put_count":7}} -{"i":41,"series":1,"stats":{"get_bytes":48126,"get_count":6,"put_bytes":47868,"put_count":7}} -{"i":42,"series":1,"stats":{"get_bytes":48300,"get_count":6,"put_bytes":48102,"put_count":7}} -{"i":43,"series":1,"stats":{"get_bytes":48811,"get_count":7,"put_bytes":48521,"put_count":8}} -{"i":44,"series":1,"stats":{"get_bytes":49380,"get_count":7,"put_bytes":49331,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":49911,"get_count":10,"put_bytes":49424,"put_count":8}} -{"i":46,"series":1,"stats":{"get_bytes":48243,"get_count":6,"put_bytes":47899,"put_count":5}} -{"i":47,"series":1,"stats":{"get_bytes":49562,"get_count":6,"put_bytes":49305,"put_count":7}} -{"i":48,"series":1,"stats":{"get_bytes":50017,"get_count":7,"put_bytes":49896,"put_count":9}} -{"i":49,"series":1,"stats":{"get_bytes":50187,"get_count":8,"put_bytes":49754,"put_count":7}} -{"i":50,"series":1,"stats":{"get_bytes":51198,"get_count":8,"put_bytes":50850,"put_count":7}} -{"i":51,"series":1,"stats":{"get_bytes":53051,"get_count":10,"put_bytes":52725,"put_count":10}} -{"i":52,"series":1,"stats":{"get_bytes":50437,"get_count":6,"put_bytes":50387,"put_count":9}} -{"i":53,"series":1,"stats":{"get_bytes":51498,"get_count":9,"put_bytes":51336,"put_count":11}} -{"i":54,"series":1,"stats":{"get_bytes":50134,"get_count":8,"put_bytes":49875,"put_count":8}} -{"i":55,"series":1,"stats":{"get_bytes":50034,"get_count":8,"put_bytes":49782,"put_count":9}} -{"i":56,"series":1,"stats":{"get_bytes":50933,"get_count":10,"put_bytes":50590,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":52132,"get_count":10,"put_bytes":51806,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":51525,"get_count":9,"put_bytes":51425,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":49674,"get_count":8,"put_bytes":49272,"put_count":7}} -{"i":60,"series":1,"stats":{"get_bytes":52856,"get_count":10,"put_bytes":52444,"put_count":8}} -{"i":61,"series":1,"stats":{"get_bytes":52101,"get_count":9,"put_bytes":51786,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":54027,"get_count":11,"put_bytes":53845,"put_count":12}} -{"i":63,"series":1,"stats":{"get_bytes":52892,"get_count":10,"put_bytes":52642,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":52242,"get_count":9,"put_bytes":51820,"put_count":7}} -{"i":65,"series":1,"stats":{"get_bytes":52702,"get_count":8,"put_bytes":52656,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":51050,"get_count":10,"put_bytes":50705,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":53911,"get_count":12,"put_bytes":53581,"put_count":12}} -{"i":68,"series":1,"stats":{"get_bytes":52683,"get_count":11,"put_bytes":52419,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":52014,"get_count":10,"put_bytes":51755,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":52331,"get_count":10,"put_bytes":52229,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":51718,"get_count":11,"put_bytes":51308,"put_count":10}} -{"i":72,"series":1,"stats":{"get_bytes":53183,"get_count":13,"put_bytes":52992,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":52222,"get_count":13,"put_bytes":51895,"put_count":13}} -{"i":74,"series":1,"stats":{"get_bytes":49845,"get_count":9,"put_bytes":49859,"put_count":13}} -{"i":75,"series":1,"stats":{"get_bytes":51409,"get_count":14,"put_bytes":51005,"put_count":13}} -{"i":76,"series":1,"stats":{"get_bytes":48190,"get_count":10,"put_bytes":48106,"put_count":12}} -{"i":77,"series":1,"stats":{"get_bytes":50866,"get_count":15,"put_bytes":50483,"put_count":14}} -{"i":78,"series":1,"stats":{"get_bytes":49045,"get_count":13,"put_bytes":48854,"put_count":14}} -{"i":79,"series":1,"stats":{"get_bytes":49470,"get_count":14,"put_bytes":49128,"put_count":14}} -{"i":80,"series":1,"stats":{"get_bytes":47508,"get_count":11,"put_bytes":47233,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":47448,"get_count":12,"put_bytes":47204,"put_count":13}} -{"i":82,"series":1,"stats":{"get_bytes":46971,"get_count":12,"put_bytes":46556,"put_count":10}} -{"i":83,"series":1,"stats":{"get_bytes":49196,"get_count":13,"put_bytes":48861,"put_count":13}} -{"i":84,"series":1,"stats":{"get_bytes":49475,"get_count":15,"put_bytes":49309,"put_count":16}} -{"i":85,"series":1,"stats":{"get_bytes":46206,"get_count":12,"put_bytes":45875,"put_count":12}} -{"i":86,"series":1,"stats":{"get_bytes":47698,"get_count":13,"put_bytes":47432,"put_count":13}} -{"i":87,"series":1,"stats":{"get_bytes":46048,"get_count":14,"put_bytes":45799,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":50423,"get_count":18,"put_bytes":50021,"put_count":16}} -{"i":89,"series":1,"stats":{"get_bytes":45196,"get_count":12,"put_bytes":44947,"put_count":13}} -{"i":90,"series":1,"stats":{"get_bytes":45575,"get_count":14,"put_bytes":45398,"put_count":15}} -{"i":91,"series":1,"stats":{"get_bytes":43671,"get_count":13,"put_bytes":43415,"put_count":14}} -{"i":92,"series":1,"stats":{"get_bytes":42758,"get_count":12,"put_bytes":42654,"put_count":14}} -{"i":93,"series":1,"stats":{"get_bytes":41305,"get_count":12,"put_bytes":41122,"put_count":14}} -{"i":94,"series":1,"stats":{"get_bytes":42380,"get_count":15,"put_bytes":42122,"put_count":15}} -{"i":95,"series":1,"stats":{"get_bytes":42472,"get_count":15,"put_bytes":42068,"put_count":14}} -{"i":96,"series":1,"stats":{"get_bytes":42834,"get_count":16,"put_bytes":42433,"put_count":14}} -{"i":97,"series":1,"stats":{"get_bytes":41671,"get_count":14,"put_bytes":41273,"put_count":13}} -{"i":98,"series":1,"stats":{"get_bytes":40423,"get_count":13,"put_bytes":40160,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":40461,"get_count":13,"put_bytes":40203,"put_count":14}} -{"i":0,"series":2,"stats":{"get_bytes":38738,"get_count":12,"put_bytes":38481,"put_count":12}} -{"i":1,"series":2,"stats":{"get_bytes":42382,"get_count":17,"put_bytes":41976,"put_count":16}} -{"i":2,"series":2,"stats":{"get_bytes":40419,"get_count":15,"put_bytes":40089,"put_count":14}} -{"i":3,"series":2,"stats":{"get_bytes":43361,"get_count":16,"put_bytes":42957,"put_count":15}} -{"i":4,"series":2,"stats":{"get_bytes":40460,"get_count":14,"put_bytes":40131,"put_count":13}} -{"i":5,"series":2,"stats":{"get_bytes":36956,"get_count":11,"put_bytes":36631,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":40076,"get_count":14,"put_bytes":39743,"put_count":13}} -{"i":7,"series":2,"stats":{"get_bytes":40117,"get_count":15,"put_bytes":39702,"put_count":14}} -{"i":8,"series":2,"stats":{"get_bytes":38651,"get_count":13,"put_bytes":38318,"put_count":12}} -{"i":9,"series":2,"stats":{"get_bytes":41518,"get_count":15,"put_bytes":41036,"put_count":13}} -{"i":10,"series":2,"stats":{"get_bytes":41570,"get_count":15,"put_bytes":41300,"put_count":15}} -{"i":11,"series":2,"stats":{"get_bytes":37972,"get_count":13,"put_bytes":37647,"put_count":13}} -{"i":12,"series":2,"stats":{"get_bytes":39768,"get_count":15,"put_bytes":39497,"put_count":15}} -{"i":13,"series":2,"stats":{"get_bytes":36941,"get_count":12,"put_bytes":36618,"put_count":12}} -{"i":14,"series":2,"stats":{"get_bytes":42378,"get_count":17,"put_bytes":42048,"put_count":16}} -{"i":15,"series":2,"stats":{"get_bytes":39654,"get_count":14,"put_bytes":39258,"put_count":13}} -{"i":16,"series":2,"stats":{"get_bytes":40829,"get_count":16,"put_bytes":40425,"put_count":14}} -{"i":17,"series":2,"stats":{"get_bytes":41343,"get_count":17,"put_bytes":40959,"put_count":16}} -{"i":18,"series":2,"stats":{"get_bytes":38989,"get_count":15,"put_bytes":38734,"put_count":15}} -{"i":19,"series":2,"stats":{"get_bytes":37464,"get_count":15,"put_bytes":37045,"put_count":14}} -{"i":20,"series":2,"stats":{"get_bytes":41584,"get_count":17,"put_bytes":41170,"put_count":15}} -{"i":21,"series":2,"stats":{"get_bytes":39945,"get_count":17,"put_bytes":39544,"put_count":16}} -{"i":22,"series":2,"stats":{"get_bytes":38615,"get_count":13,"put_bytes":38500,"put_count":15}} -{"i":23,"series":2,"stats":{"get_bytes":35353,"get_count":14,"put_bytes":34950,"put_count":13}} -{"i":24,"series":2,"stats":{"get_bytes":37762,"get_count":18,"put_bytes":37364,"put_count":16}} -{"i":25,"series":2,"stats":{"get_bytes":38130,"get_count":17,"put_bytes":37734,"put_count":16}} -{"i":26,"series":2,"stats":{"get_bytes":35569,"get_count":13,"put_bytes":35382,"put_count":14}} -{"i":27,"series":2,"stats":{"get_bytes":36408,"get_count":16,"put_bytes":36025,"put_count":15}} -{"i":28,"series":2,"stats":{"get_bytes":37285,"get_count":16,"put_bytes":36879,"put_count":14}} -{"i":29,"series":2,"stats":{"get_bytes":35941,"get_count":16,"put_bytes":35606,"put_count":16}} -{"i":30,"series":2,"stats":{"get_bytes":36983,"get_count":16,"put_bytes":36648,"put_count":15}} -{"i":31,"series":2,"stats":{"get_bytes":34153,"get_count":14,"put_bytes":33903,"put_count":15}} -{"i":32,"series":2,"stats":{"get_bytes":34072,"get_count":15,"put_bytes":33808,"put_count":15}} -{"i":33,"series":2,"stats":{"get_bytes":33366,"get_count":16,"put_bytes":32962,"put_count":15}} -{"i":34,"series":2,"stats":{"get_bytes":34797,"get_count":16,"put_bytes":34464,"put_count":15}} -{"i":35,"series":2,"stats":{"get_bytes":36660,"get_count":17,"put_bytes":36254,"put_count":16}} -{"i":36,"series":2,"stats":{"get_bytes":33707,"get_count":16,"put_bytes":33441,"put_count":16}} -{"i":37,"series":2,"stats":{"get_bytes":34185,"get_count":17,"put_bytes":33725,"put_count":15}} -{"i":38,"series":2,"stats":{"get_bytes":33680,"get_count":17,"put_bytes":33339,"put_count":16}} -{"i":39,"series":2,"stats":{"get_bytes":35655,"get_count":17,"put_bytes":35261,"put_count":16}} -{"i":40,"series":2,"stats":{"get_bytes":30667,"get_count":15,"put_bytes":30491,"put_count":16}} -{"i":41,"series":2,"stats":{"get_bytes":27793,"get_count":12,"put_bytes":27532,"put_count":13}} -{"i":42,"series":2,"stats":{"get_bytes":31038,"get_count":18,"put_bytes":30633,"put_count":16}} -{"i":43,"series":2,"stats":{"get_bytes":30405,"get_count":15,"put_bytes":30072,"put_count":15}} -{"i":44,"series":2,"stats":{"get_bytes":29625,"get_count":16,"put_bytes":29370,"put_count":16}} -{"i":45,"series":2,"stats":{"get_bytes":32500,"get_count":18,"put_bytes":32018,"put_count":16}} -{"i":46,"series":2,"stats":{"get_bytes":28696,"get_count":17,"put_bytes":28361,"put_count":16}} -{"i":47,"series":2,"stats":{"get_bytes":27669,"get_count":18,"put_bytes":27197,"put_count":16}} -{"i":48,"series":2,"stats":{"get_bytes":32459,"get_count":18,"put_bytes":32049,"put_count":16}} -{"i":49,"series":2,"stats":{"get_bytes":31614,"get_count":17,"put_bytes":31132,"put_count":15}} -{"i":50,"series":2,"stats":{"get_bytes":31899,"get_count":18,"put_bytes":31494,"put_count":16}} -{"i":51,"series":2,"stats":{"get_bytes":31605,"get_count":17,"put_bytes":31131,"put_count":15}} -{"i":52,"series":2,"stats":{"get_bytes":31813,"get_count":18,"put_bytes":31402,"put_count":16}} -{"i":53,"series":2,"stats":{"get_bytes":31868,"get_count":18,"put_bytes":31395,"put_count":16}} -{"i":54,"series":2,"stats":{"get_bytes":31740,"get_count":17,"put_bytes":31342,"put_count":15}} -{"i":55,"series":2,"stats":{"get_bytes":32009,"get_count":18,"put_bytes":31527,"put_count":16}} -{"i":56,"series":2,"stats":{"get_bytes":31347,"get_count":16,"put_bytes":31018,"put_count":15}} -{"i":57,"series":2,"stats":{"get_bytes":29593,"get_count":15,"put_bytes":29189,"put_count":14}} -{"i":58,"series":2,"stats":{"get_bytes":31050,"get_count":16,"put_bytes":30772,"put_count":16}} -{"i":59,"series":2,"stats":{"get_bytes":30197,"get_count":17,"put_bytes":29714,"put_count":15}} -{"i":60,"series":2,"stats":{"get_bytes":31553,"get_count":18,"put_bytes":31158,"put_count":16}} -{"i":61,"series":2,"stats":{"get_bytes":32181,"get_count":18,"put_bytes":31703,"put_count":16}} -{"i":62,"series":2,"stats":{"get_bytes":31285,"get_count":17,"put_bytes":30874,"put_count":15}} -{"i":63,"series":2,"stats":{"get_bytes":31431,"get_count":18,"put_bytes":30951,"put_count":16}} -{"i":64,"series":2,"stats":{"get_bytes":29160,"get_count":17,"put_bytes":28830,"put_count":16}} -{"i":65,"series":2,"stats":{"get_bytes":30935,"get_count":18,"put_bytes":30455,"put_count":16}} -{"i":66,"series":2,"stats":{"get_bytes":31200,"get_count":17,"put_bytes":30868,"put_count":16}} -{"i":67,"series":2,"stats":{"get_bytes":27638,"get_count":17,"put_bytes":27159,"put_count":15}} -{"i":68,"series":2,"stats":{"get_bytes":31144,"get_count":18,"put_bytes":30738,"put_count":16}} -{"i":69,"series":2,"stats":{"get_bytes":32138,"get_count":17,"put_bytes":31734,"put_count":16}} -{"i":70,"series":2,"stats":{"get_bytes":30134,"get_count":18,"put_bytes":29725,"put_count":16}} -{"i":71,"series":2,"stats":{"get_bytes":31815,"get_count":18,"put_bytes":31332,"put_count":16}} -{"i":72,"series":2,"stats":{"get_bytes":30219,"get_count":17,"put_bytes":29905,"put_count":16}} -{"i":73,"series":2,"stats":{"get_bytes":31054,"get_count":18,"put_bytes":30582,"put_count":16}} -{"i":74,"series":2,"stats":{"get_bytes":28948,"get_count":17,"put_bytes":28618,"put_count":16}} -{"i":75,"series":2,"stats":{"get_bytes":29502,"get_count":17,"put_bytes":29018,"put_count":15}} -{"i":76,"series":2,"stats":{"get_bytes":30232,"get_count":18,"put_bytes":29821,"put_count":16}} -{"i":77,"series":2,"stats":{"get_bytes":30164,"get_count":17,"put_bytes":29683,"put_count":15}} -{"i":78,"series":2,"stats":{"get_bytes":29538,"get_count":17,"put_bytes":29134,"put_count":15}} -{"i":79,"series":2,"stats":{"get_bytes":31377,"get_count":18,"put_bytes":30908,"put_count":16}} -{"i":80,"series":2,"stats":{"get_bytes":29839,"get_count":18,"put_bytes":29433,"put_count":16}} -{"i":81,"series":2,"stats":{"get_bytes":27235,"get_count":17,"put_bytes":26760,"put_count":15}} -{"i":82,"series":2,"stats":{"get_bytes":30089,"get_count":18,"put_bytes":29682,"put_count":16}} -{"i":83,"series":2,"stats":{"get_bytes":31652,"get_count":18,"put_bytes":31174,"put_count":16}} -{"i":84,"series":2,"stats":{"get_bytes":26822,"get_count":15,"put_bytes":26488,"put_count":14}} -{"i":85,"series":2,"stats":{"get_bytes":32331,"get_count":18,"put_bytes":31856,"put_count":16}} -{"i":86,"series":2,"stats":{"get_bytes":32612,"get_count":17,"put_bytes":32220,"put_count":15}} -{"i":87,"series":2,"stats":{"get_bytes":33002,"get_count":18,"put_bytes":32527,"put_count":16}} -{"i":88,"series":2,"stats":{"get_bytes":31468,"get_count":18,"put_bytes":31059,"put_count":16}} -{"i":89,"series":2,"stats":{"get_bytes":34197,"get_count":18,"put_bytes":33722,"put_count":16}} -{"i":90,"series":2,"stats":{"get_bytes":33539,"get_count":17,"put_bytes":33126,"put_count":15}} -{"i":91,"series":2,"stats":{"get_bytes":33660,"get_count":18,"put_bytes":33177,"put_count":16}} -{"i":92,"series":2,"stats":{"get_bytes":32179,"get_count":16,"put_bytes":31847,"put_count":15}} -{"i":93,"series":2,"stats":{"get_bytes":31824,"get_count":17,"put_bytes":31353,"put_count":15}} -{"i":94,"series":2,"stats":{"get_bytes":31448,"get_count":16,"put_bytes":31103,"put_count":15}} -{"i":95,"series":2,"stats":{"get_bytes":32030,"get_count":17,"put_bytes":31547,"put_count":15}} -{"i":96,"series":2,"stats":{"get_bytes":27386,"get_count":16,"put_bytes":26984,"put_count":14}} -{"i":97,"series":2,"stats":{"get_bytes":31920,"get_count":18,"put_bytes":31443,"put_count":16}} -{"i":98,"series":2,"stats":{"get_bytes":31778,"get_count":17,"put_bytes":31368,"put_count":15}} -{"i":99,"series":2,"stats":{"get_bytes":33883,"get_count":18,"put_bytes":33409,"put_count":16}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3912,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":3821,"get_count":3,"put_bytes":3429,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":5437,"get_count":3,"put_bytes":5114,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":7122,"get_count":3,"put_bytes":6724,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":8732,"get_count":3,"put_bytes":8406,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":10414,"get_count":3,"put_bytes":10020,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":12028,"get_count":3,"put_bytes":11698,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":13706,"get_count":3,"put_bytes":13310,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":15318,"get_count":3,"put_bytes":14988,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":16996,"get_count":3,"put_bytes":16743,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":17641,"get_count":3,"put_bytes":17309,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":19317,"get_count":3,"put_bytes":18918,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":20926,"get_count":3,"put_bytes":20595,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":22603,"get_count":3,"put_bytes":22266,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":23729,"get_count":3,"put_bytes":23397,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":25405,"get_count":3,"put_bytes":25005,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":27013,"get_count":3,"put_bytes":26761,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":28211,"get_count":3,"put_bytes":28040,"put_count":5}} +{"i":18,"series":1,"stats":{"get_bytes":28369,"get_count":3,"put_bytes":28036,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":30044,"get_count":3,"put_bytes":29721,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":31730,"get_count":4,"put_bytes":31397,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":32710,"get_count":3,"put_bytes":32374,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":33832,"get_count":3,"put_bytes":33499,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":36065,"get_count":4,"put_bytes":35661,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":36980,"get_count":3,"put_bytes":36718,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":38173,"get_count":3,"put_bytes":37840,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":39297,"get_count":3,"put_bytes":38961,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":41519,"get_count":4,"put_bytes":41283,"put_count":5}} +{"i":28,"series":1,"stats":{"get_bytes":41480,"get_count":3,"put_bytes":41218,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":43786,"get_count":5,"put_bytes":43606,"put_count":7}} +{"i":30,"series":1,"stats":{"get_bytes":43118,"get_count":4,"put_bytes":43018,"put_count":6}} +{"i":31,"series":1,"stats":{"get_bytes":43218,"get_count":4,"put_bytes":43186,"put_count":8}} +{"i":32,"series":1,"stats":{"get_bytes":42426,"get_count":4,"put_bytes":42162,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":44039,"get_count":5,"put_bytes":43764,"put_count":6}} +{"i":34,"series":1,"stats":{"get_bytes":44423,"get_count":5,"put_bytes":44174,"put_count":5}} +{"i":35,"series":1,"stats":{"get_bytes":45353,"get_count":5,"put_bytes":45109,"put_count":6}} +{"i":36,"series":1,"stats":{"get_bytes":45723,"get_count":5,"put_bytes":45463,"put_count":5}} +{"i":37,"series":1,"stats":{"get_bytes":47473,"get_count":6,"put_bytes":47306,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":47642,"get_count":6,"put_bytes":47443,"put_count":7}} +{"i":39,"series":1,"stats":{"get_bytes":46309,"get_count":4,"put_bytes":46072,"put_count":5}} +{"i":40,"series":1,"stats":{"get_bytes":48898,"get_count":7,"put_bytes":48642,"put_count":7}} +{"i":41,"series":1,"stats":{"get_bytes":48038,"get_count":5,"put_bytes":47868,"put_count":7}} +{"i":42,"series":1,"stats":{"get_bytes":48212,"get_count":5,"put_bytes":48102,"put_count":7}} +{"i":43,"series":1,"stats":{"get_bytes":48723,"get_count":6,"put_bytes":48521,"put_count":8}} +{"i":44,"series":1,"stats":{"get_bytes":49292,"get_count":6,"put_bytes":49331,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":49823,"get_count":9,"put_bytes":49424,"put_count":8}} +{"i":46,"series":1,"stats":{"get_bytes":48155,"get_count":5,"put_bytes":47899,"put_count":5}} +{"i":47,"series":1,"stats":{"get_bytes":49474,"get_count":5,"put_bytes":49305,"put_count":7}} +{"i":48,"series":1,"stats":{"get_bytes":49929,"get_count":6,"put_bytes":49896,"put_count":9}} +{"i":49,"series":1,"stats":{"get_bytes":50099,"get_count":7,"put_bytes":49754,"put_count":7}} +{"i":50,"series":1,"stats":{"get_bytes":51110,"get_count":7,"put_bytes":50850,"put_count":7}} +{"i":51,"series":1,"stats":{"get_bytes":52963,"get_count":9,"put_bytes":52725,"put_count":10}} +{"i":52,"series":1,"stats":{"get_bytes":50349,"get_count":5,"put_bytes":50387,"put_count":9}} +{"i":53,"series":1,"stats":{"get_bytes":51410,"get_count":8,"put_bytes":51336,"put_count":11}} +{"i":54,"series":1,"stats":{"get_bytes":50046,"get_count":7,"put_bytes":49875,"put_count":8}} +{"i":55,"series":1,"stats":{"get_bytes":49946,"get_count":7,"put_bytes":49782,"put_count":9}} +{"i":56,"series":1,"stats":{"get_bytes":50845,"get_count":9,"put_bytes":50590,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":52044,"get_count":9,"put_bytes":51806,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":51437,"get_count":8,"put_bytes":51425,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":49586,"get_count":7,"put_bytes":49272,"put_count":7}} +{"i":60,"series":1,"stats":{"get_bytes":52768,"get_count":9,"put_bytes":52444,"put_count":8}} +{"i":61,"series":1,"stats":{"get_bytes":52013,"get_count":8,"put_bytes":51786,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":53939,"get_count":10,"put_bytes":53845,"put_count":12}} +{"i":63,"series":1,"stats":{"get_bytes":52804,"get_count":9,"put_bytes":52642,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":52154,"get_count":8,"put_bytes":51820,"put_count":7}} +{"i":65,"series":1,"stats":{"get_bytes":52614,"get_count":7,"put_bytes":52656,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":50962,"get_count":9,"put_bytes":50705,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":53823,"get_count":11,"put_bytes":53581,"put_count":12}} +{"i":68,"series":1,"stats":{"get_bytes":52595,"get_count":10,"put_bytes":52419,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":51926,"get_count":9,"put_bytes":51755,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":52243,"get_count":9,"put_bytes":52229,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":51630,"get_count":10,"put_bytes":51308,"put_count":10}} +{"i":72,"series":1,"stats":{"get_bytes":53095,"get_count":12,"put_bytes":52992,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":52134,"get_count":12,"put_bytes":51895,"put_count":13}} +{"i":74,"series":1,"stats":{"get_bytes":49757,"get_count":8,"put_bytes":49859,"put_count":13}} +{"i":75,"series":1,"stats":{"get_bytes":51321,"get_count":13,"put_bytes":51005,"put_count":13}} +{"i":76,"series":1,"stats":{"get_bytes":48102,"get_count":9,"put_bytes":48106,"put_count":12}} +{"i":77,"series":1,"stats":{"get_bytes":50778,"get_count":14,"put_bytes":50483,"put_count":14}} +{"i":78,"series":1,"stats":{"get_bytes":48957,"get_count":12,"put_bytes":48854,"put_count":14}} +{"i":79,"series":1,"stats":{"get_bytes":49382,"get_count":13,"put_bytes":49128,"put_count":14}} +{"i":80,"series":1,"stats":{"get_bytes":47420,"get_count":10,"put_bytes":47233,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":47360,"get_count":11,"put_bytes":47204,"put_count":13}} +{"i":82,"series":1,"stats":{"get_bytes":46883,"get_count":11,"put_bytes":46556,"put_count":10}} +{"i":83,"series":1,"stats":{"get_bytes":49108,"get_count":12,"put_bytes":48861,"put_count":13}} +{"i":84,"series":1,"stats":{"get_bytes":49387,"get_count":14,"put_bytes":49309,"put_count":16}} +{"i":85,"series":1,"stats":{"get_bytes":46118,"get_count":11,"put_bytes":45875,"put_count":12}} +{"i":86,"series":1,"stats":{"get_bytes":47610,"get_count":12,"put_bytes":47432,"put_count":13}} +{"i":87,"series":1,"stats":{"get_bytes":45960,"get_count":13,"put_bytes":45799,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":50335,"get_count":17,"put_bytes":50021,"put_count":16}} +{"i":89,"series":1,"stats":{"get_bytes":45108,"get_count":11,"put_bytes":44947,"put_count":13}} +{"i":90,"series":1,"stats":{"get_bytes":45487,"get_count":13,"put_bytes":45398,"put_count":15}} +{"i":91,"series":1,"stats":{"get_bytes":43583,"get_count":12,"put_bytes":43415,"put_count":14}} +{"i":92,"series":1,"stats":{"get_bytes":42670,"get_count":11,"put_bytes":42654,"put_count":14}} +{"i":93,"series":1,"stats":{"get_bytes":41217,"get_count":11,"put_bytes":41122,"put_count":14}} +{"i":94,"series":1,"stats":{"get_bytes":42292,"get_count":14,"put_bytes":42122,"put_count":15}} +{"i":95,"series":1,"stats":{"get_bytes":42384,"get_count":14,"put_bytes":42068,"put_count":14}} +{"i":96,"series":1,"stats":{"get_bytes":42746,"get_count":15,"put_bytes":42433,"put_count":14}} +{"i":97,"series":1,"stats":{"get_bytes":41583,"get_count":13,"put_bytes":41273,"put_count":13}} +{"i":98,"series":1,"stats":{"get_bytes":40335,"get_count":12,"put_bytes":40160,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":40373,"get_count":12,"put_bytes":40203,"put_count":14}} +{"i":0,"series":2,"stats":{"get_bytes":38650,"get_count":11,"put_bytes":38481,"put_count":12}} +{"i":1,"series":2,"stats":{"get_bytes":42294,"get_count":16,"put_bytes":41976,"put_count":16}} +{"i":2,"series":2,"stats":{"get_bytes":40331,"get_count":14,"put_bytes":40089,"put_count":14}} +{"i":3,"series":2,"stats":{"get_bytes":43273,"get_count":15,"put_bytes":42957,"put_count":15}} +{"i":4,"series":2,"stats":{"get_bytes":40372,"get_count":13,"put_bytes":40131,"put_count":13}} +{"i":5,"series":2,"stats":{"get_bytes":36868,"get_count":10,"put_bytes":36631,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":39988,"get_count":13,"put_bytes":39743,"put_count":13}} +{"i":7,"series":2,"stats":{"get_bytes":40029,"get_count":14,"put_bytes":39702,"put_count":14}} +{"i":8,"series":2,"stats":{"get_bytes":38563,"get_count":12,"put_bytes":38318,"put_count":12}} +{"i":9,"series":2,"stats":{"get_bytes":41430,"get_count":14,"put_bytes":41036,"put_count":13}} +{"i":10,"series":2,"stats":{"get_bytes":41482,"get_count":14,"put_bytes":41300,"put_count":15}} +{"i":11,"series":2,"stats":{"get_bytes":37884,"get_count":12,"put_bytes":37647,"put_count":13}} +{"i":12,"series":2,"stats":{"get_bytes":39680,"get_count":14,"put_bytes":39497,"put_count":15}} +{"i":13,"series":2,"stats":{"get_bytes":36853,"get_count":11,"put_bytes":36618,"put_count":12}} +{"i":14,"series":2,"stats":{"get_bytes":42290,"get_count":16,"put_bytes":42048,"put_count":16}} +{"i":15,"series":2,"stats":{"get_bytes":39566,"get_count":13,"put_bytes":39258,"put_count":13}} +{"i":16,"series":2,"stats":{"get_bytes":40741,"get_count":15,"put_bytes":40425,"put_count":14}} +{"i":17,"series":2,"stats":{"get_bytes":41255,"get_count":16,"put_bytes":40959,"put_count":16}} +{"i":18,"series":2,"stats":{"get_bytes":38901,"get_count":14,"put_bytes":38734,"put_count":15}} +{"i":19,"series":2,"stats":{"get_bytes":37376,"get_count":14,"put_bytes":37045,"put_count":14}} +{"i":20,"series":2,"stats":{"get_bytes":41496,"get_count":16,"put_bytes":41170,"put_count":15}} +{"i":21,"series":2,"stats":{"get_bytes":39857,"get_count":16,"put_bytes":39544,"put_count":16}} +{"i":22,"series":2,"stats":{"get_bytes":38527,"get_count":12,"put_bytes":38500,"put_count":15}} +{"i":23,"series":2,"stats":{"get_bytes":35265,"get_count":13,"put_bytes":34950,"put_count":13}} +{"i":24,"series":2,"stats":{"get_bytes":37674,"get_count":17,"put_bytes":37364,"put_count":16}} +{"i":25,"series":2,"stats":{"get_bytes":38042,"get_count":16,"put_bytes":37734,"put_count":16}} +{"i":26,"series":2,"stats":{"get_bytes":35481,"get_count":12,"put_bytes":35382,"put_count":14}} +{"i":27,"series":2,"stats":{"get_bytes":36320,"get_count":15,"put_bytes":36025,"put_count":15}} +{"i":28,"series":2,"stats":{"get_bytes":37197,"get_count":15,"put_bytes":36879,"put_count":14}} +{"i":29,"series":2,"stats":{"get_bytes":35853,"get_count":15,"put_bytes":35606,"put_count":16}} +{"i":30,"series":2,"stats":{"get_bytes":36895,"get_count":15,"put_bytes":36648,"put_count":15}} +{"i":31,"series":2,"stats":{"get_bytes":34065,"get_count":13,"put_bytes":33903,"put_count":15}} +{"i":32,"series":2,"stats":{"get_bytes":33984,"get_count":14,"put_bytes":33808,"put_count":15}} +{"i":33,"series":2,"stats":{"get_bytes":33278,"get_count":15,"put_bytes":32962,"put_count":15}} +{"i":34,"series":2,"stats":{"get_bytes":34709,"get_count":15,"put_bytes":34464,"put_count":15}} +{"i":35,"series":2,"stats":{"get_bytes":36572,"get_count":16,"put_bytes":36254,"put_count":16}} +{"i":36,"series":2,"stats":{"get_bytes":33619,"get_count":15,"put_bytes":33441,"put_count":16}} +{"i":37,"series":2,"stats":{"get_bytes":34097,"get_count":16,"put_bytes":33725,"put_count":15}} +{"i":38,"series":2,"stats":{"get_bytes":33592,"get_count":16,"put_bytes":33339,"put_count":16}} +{"i":39,"series":2,"stats":{"get_bytes":35567,"get_count":16,"put_bytes":35261,"put_count":16}} +{"i":40,"series":2,"stats":{"get_bytes":30579,"get_count":14,"put_bytes":30491,"put_count":16}} +{"i":41,"series":2,"stats":{"get_bytes":27705,"get_count":11,"put_bytes":27532,"put_count":13}} +{"i":42,"series":2,"stats":{"get_bytes":30950,"get_count":17,"put_bytes":30633,"put_count":16}} +{"i":43,"series":2,"stats":{"get_bytes":30317,"get_count":14,"put_bytes":30072,"put_count":15}} +{"i":44,"series":2,"stats":{"get_bytes":29537,"get_count":15,"put_bytes":29370,"put_count":16}} +{"i":45,"series":2,"stats":{"get_bytes":32412,"get_count":17,"put_bytes":32018,"put_count":16}} +{"i":46,"series":2,"stats":{"get_bytes":28608,"get_count":16,"put_bytes":28361,"put_count":16}} +{"i":47,"series":2,"stats":{"get_bytes":27581,"get_count":17,"put_bytes":27197,"put_count":16}} +{"i":48,"series":2,"stats":{"get_bytes":32371,"get_count":17,"put_bytes":32049,"put_count":16}} +{"i":49,"series":2,"stats":{"get_bytes":31526,"get_count":16,"put_bytes":31132,"put_count":15}} +{"i":50,"series":2,"stats":{"get_bytes":31811,"get_count":17,"put_bytes":31494,"put_count":16}} +{"i":51,"series":2,"stats":{"get_bytes":31517,"get_count":16,"put_bytes":31131,"put_count":15}} +{"i":52,"series":2,"stats":{"get_bytes":31725,"get_count":17,"put_bytes":31402,"put_count":16}} +{"i":53,"series":2,"stats":{"get_bytes":31780,"get_count":17,"put_bytes":31395,"put_count":16}} +{"i":54,"series":2,"stats":{"get_bytes":31652,"get_count":16,"put_bytes":31342,"put_count":15}} +{"i":55,"series":2,"stats":{"get_bytes":31921,"get_count":17,"put_bytes":31527,"put_count":16}} +{"i":56,"series":2,"stats":{"get_bytes":31259,"get_count":15,"put_bytes":31018,"put_count":15}} +{"i":57,"series":2,"stats":{"get_bytes":29505,"get_count":14,"put_bytes":29189,"put_count":14}} +{"i":58,"series":2,"stats":{"get_bytes":30962,"get_count":15,"put_bytes":30772,"put_count":16}} +{"i":59,"series":2,"stats":{"get_bytes":30109,"get_count":16,"put_bytes":29714,"put_count":15}} +{"i":60,"series":2,"stats":{"get_bytes":31465,"get_count":17,"put_bytes":31158,"put_count":16}} +{"i":61,"series":2,"stats":{"get_bytes":32093,"get_count":17,"put_bytes":31703,"put_count":16}} +{"i":62,"series":2,"stats":{"get_bytes":31197,"get_count":16,"put_bytes":30874,"put_count":15}} +{"i":63,"series":2,"stats":{"get_bytes":31343,"get_count":17,"put_bytes":30951,"put_count":16}} +{"i":64,"series":2,"stats":{"get_bytes":29072,"get_count":16,"put_bytes":28830,"put_count":16}} +{"i":65,"series":2,"stats":{"get_bytes":30847,"get_count":17,"put_bytes":30455,"put_count":16}} +{"i":66,"series":2,"stats":{"get_bytes":31112,"get_count":16,"put_bytes":30868,"put_count":16}} +{"i":67,"series":2,"stats":{"get_bytes":27550,"get_count":16,"put_bytes":27159,"put_count":15}} +{"i":68,"series":2,"stats":{"get_bytes":31056,"get_count":17,"put_bytes":30738,"put_count":16}} +{"i":69,"series":2,"stats":{"get_bytes":32050,"get_count":16,"put_bytes":31734,"put_count":16}} +{"i":70,"series":2,"stats":{"get_bytes":30046,"get_count":17,"put_bytes":29725,"put_count":16}} +{"i":71,"series":2,"stats":{"get_bytes":31727,"get_count":17,"put_bytes":31332,"put_count":16}} +{"i":72,"series":2,"stats":{"get_bytes":30131,"get_count":16,"put_bytes":29905,"put_count":16}} +{"i":73,"series":2,"stats":{"get_bytes":30966,"get_count":17,"put_bytes":30582,"put_count":16}} +{"i":74,"series":2,"stats":{"get_bytes":28860,"get_count":16,"put_bytes":28618,"put_count":16}} +{"i":75,"series":2,"stats":{"get_bytes":29414,"get_count":16,"put_bytes":29018,"put_count":15}} +{"i":76,"series":2,"stats":{"get_bytes":30144,"get_count":17,"put_bytes":29821,"put_count":16}} +{"i":77,"series":2,"stats":{"get_bytes":30076,"get_count":16,"put_bytes":29683,"put_count":15}} +{"i":78,"series":2,"stats":{"get_bytes":29450,"get_count":16,"put_bytes":29134,"put_count":15}} +{"i":79,"series":2,"stats":{"get_bytes":31289,"get_count":17,"put_bytes":30908,"put_count":16}} +{"i":80,"series":2,"stats":{"get_bytes":29751,"get_count":17,"put_bytes":29433,"put_count":16}} +{"i":81,"series":2,"stats":{"get_bytes":27147,"get_count":16,"put_bytes":26760,"put_count":15}} +{"i":82,"series":2,"stats":{"get_bytes":30001,"get_count":17,"put_bytes":29682,"put_count":16}} +{"i":83,"series":2,"stats":{"get_bytes":31564,"get_count":17,"put_bytes":31174,"put_count":16}} +{"i":84,"series":2,"stats":{"get_bytes":26734,"get_count":14,"put_bytes":26488,"put_count":14}} +{"i":85,"series":2,"stats":{"get_bytes":32243,"get_count":17,"put_bytes":31856,"put_count":16}} +{"i":86,"series":2,"stats":{"get_bytes":32524,"get_count":16,"put_bytes":32220,"put_count":15}} +{"i":87,"series":2,"stats":{"get_bytes":32914,"get_count":17,"put_bytes":32527,"put_count":16}} +{"i":88,"series":2,"stats":{"get_bytes":31380,"get_count":17,"put_bytes":31059,"put_count":16}} +{"i":89,"series":2,"stats":{"get_bytes":34109,"get_count":17,"put_bytes":33722,"put_count":16}} +{"i":90,"series":2,"stats":{"get_bytes":33451,"get_count":16,"put_bytes":33126,"put_count":15}} +{"i":91,"series":2,"stats":{"get_bytes":33572,"get_count":17,"put_bytes":33177,"put_count":16}} +{"i":92,"series":2,"stats":{"get_bytes":32091,"get_count":15,"put_bytes":31847,"put_count":15}} +{"i":93,"series":2,"stats":{"get_bytes":31736,"get_count":16,"put_bytes":31353,"put_count":15}} +{"i":94,"series":2,"stats":{"get_bytes":31360,"get_count":15,"put_bytes":31103,"put_count":15}} +{"i":95,"series":2,"stats":{"get_bytes":31942,"get_count":16,"put_bytes":31547,"put_count":15}} +{"i":96,"series":2,"stats":{"get_bytes":27298,"get_count":15,"put_bytes":26984,"put_count":14}} +{"i":97,"series":2,"stats":{"get_bytes":31832,"get_count":17,"put_bytes":31443,"put_count":16}} +{"i":98,"series":2,"stats":{"get_bytes":31690,"get_count":16,"put_bytes":31368,"put_count":15}} +{"i":99,"series":2,"stats":{"get_bytes":33795,"get_count":17,"put_bytes":33409,"put_count":16}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png index 49c7f8198ed9b628823bbceb9fd3361d521cfd1f..dbc6a4dc8c22671f33d10394e65aab5d489fbe5b 100644 GIT binary patch literal 22390 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 BqzwQ7 literal 21008 zcmb@u2UJtR_wO4}kfI1kQKT!VRFNiK5JZroLJHl`JBaiu61o(@fPfT1Y61zpw;&)O zO(b-XUPAAkcjE7V*Z;oz-d*p#wO$tD_&s}O&dlu0IkWd?8U9jB^%@lm6$k{nruJO< zH3)PW3j$r@q__kqm*wpqfHWpSH}4}speOG^7w<0uF8*Zm@c~tz{Otqkv9Pd+ii&D#YFb)a1_T5o zB_)-XmNqpt{rdH5etsV4V)N0X*!9@L;e}1`=HH8p^@$G`70sCS^@|}8&>P4c_#MC< z$t@HK{rB(4TeqBnTLFQ(Lwg7y;w@f)dipOI;x7VTZoYqip#nO7@3NfWy}9tQzS6EL zgnDmpJmWp*MK);t!}@2?z(mGG?8_%$JK^^Xp!W<6@1Foa@87>^djIC)J@NjZTx0-0 zZ!R*NFPfSzfbm>3byucsUTkJmbT?l#cQ;{uFP>yva5gkFq^GBIaBxgbO*J+)nwXd< zC@8eFwB+XIg3{hW%9a<>L7Q_*AeR#A?g2GmA}F1o>%9YkXnqj?kRbVKZi7IaAT?!0 zUC*y;sdg2pyqYIwGOOC7(>vqh?t2#(jJ`jvaB|;Y`fB#axQ#Q8){#5eBDyw017`JA_4u=e@u1S3c&xOa8zpK=rpq;*%SLb~U#9xw?Rac~JznZ|DB`Jv z0-W)8ykGJH)cnX);1Z}VSc@imb@KDPeWEK^uH%V&2*TzWe2*>1*3)W*yxXMXdvKXf zqW9<*EkjM|(^K9FLO$q@X_0YH*9%omClBGAN1!HyC4K*m&~zTeErgqYOR>wUpr#zU zNEocWF*h$evDv8wQ^7n~i+%Z3(y6p0nG&@YERG}r8NY{#a6;%mH!g3zO)S&Va}Ah7 zDZ=)&VPN@R0oNi50;IZwvv1OC?Zz9rEj}v6D)aTb-H1-^yl4E;0`z`yKrdsX@)RMy zMlVdzI95gr$CB&qVt;H!eYs#^7`!)5@j_%-UPQfMlsTaecK=zR&QZvE0x)1%j*;bj zCONbXG&j|~!sShJOPpFQ6!}M4GH>h_{r)=L5NmGK$Wdr^HaR>>bO-PPlOsfZ8!*hB zW%HMV4vVpR*X_o6B&_@rmsd=5B9LSbR?((a>Um6%q3O=;8i*ikYJ#|{f#xgbsNG&? zq_gPqWSIR5#5&&rb)h*=Fg~4vk%K%*RFcY5VVxta_$orFAqzN~fGv=?!H+qvwLNx& zaUiiFD1YP({%bn+c+8Qha$$Evu!vsEW~Bt3yWPQzZXtc2cEsYo#ufwu9q5Sf=W}4$4g~q~hH5%`>^X2^x_Z+JUk|r+N z1m*&?bxFx9Iho;IvFSfYn8kI|k4q7ylL`&^HHBnHjBLf?7TB@N-p^04$fVc!T-_T! zZy}gAVd+N&du3tqig6-w#3Q4o=QDh^!Sr}xNn7+`!|n4CCa+!DhE9j1LJX#Lk1*0@ z((&-^p!qEw*u9(V3SYU>IM=`*J)KCkgBEI4 zy8qL~OVp?Wf&ep$%|I%25SBi3UnRxD;!d)y%ty`JQoX_t%;TPlT4(Z$qJQ_^(F4U&Il;10jXxtjVz32Aaev-E zA8t&hM*W%>)8}&dv)6$L-PS;J2-gg0g6GYkwsoP_5n^=bqKG^HT`>_GF%|jtcfBND z3C2;aYgnbe{|$j{^I<*V(U|RXlC`CUjG8G4b(1bty&`TIXR$BssSd&p`DYrnV0+j< zCG=;QDVO(eYwQ3+$y&hC*$&F0{R^Q+%+KIa{IKA%A9Y9uyyvabPAh)WHukZ*=o!&P zpU)tU5q1Pw#%vlM*bhop;BG4gDpDux5T2zN$F?*EIf3_3XKlhwv!rAn*!CpTtEM_6 zKg%2|q`y&5jVB88rG?a!OtL&$)-IyPx@5rv`BG##?Rw-I%3r;~0p-XE(=A`MW!ym_ za1H}}ens@8vY%`9RVNZq2JepJej$+82w3@9+7h8If3}2SJ$pR-468oApOGU;nu4Lw;WzN$M0Yf~qqJ^lhiruwWg*vw1t zHLFUY5W+HP*?r7nzh6VvQQ`iEpM#Y`f}Mz9v?{DW5W8P$z}&LuN}~UHXlF@&s{a&n za?nsw%F$K+g{l1xdg%%5WTY^1+PelM*h%nmP2aHgN1geYjE%(1>uW^LZqP%*Xgi+9 z{!!JasjXeG)W zx6&f2|M%kzfP~G#^(tBcN4q6T!T__(KVC?9MDgmise4`)C!xM+GQeOIq4cujWU!X z5bnYt+74$)F7vIfcbJ3Sa-knB*$GMnPUT1A(Unr)F)r^^^-NH@ew|h^tdfOUx6;>! zEJJZT2x2W66BDDvq?hx|2T$x^Zbd9|a9BiS(_~|CWf)h<~6 z&Rx$r#RQYTv1#SiCySKW*>H^e1DFEad71#GcJP6)l{yoGkz;C^gX7ko(km-Tg1&nQ}Z47fQa(wzgVCe9HDy3_ho9vCTL*9zF&!ZD&Iq^D^rKi~< z5{sqjACq|?JgNCBC?R7++bBvaa;2XF)fvIdKaf_Z#fV@`!R{KdIh;@VPy9Ldy~1N! z`(+xnQe3YA%chv6N2gfOaUIc~ef0@w{`)C@oucc-*4JogO!dd37{9c5ob*Em@b2)8 zDC9Z3qw4jy936Y^y9N(iH4;!PIn?CZhWPi`I);t!*xeh`U8jwxp*FMqp0)MVrH^h( zun|+gqnT9r^Fzu?(g8lD5;C1%bLYmmtq+Q<)T(3OW``N=Kk~zezXW1u=o4K0nxXYV zcDZ#lmDN8P=?G+p0%6w529Xb!^rs0pDt~RXql`OfJ59YG9oNLar zwqakn7LVp^Jf@O^K8i)a&1i5D>M)a56vi>nQV0_-+7s61eS}xJ%#d*PJNpu^X)Q&E zrXG|V^z=e9o!YLB-dHQBZloSO&H{48o`eM9 z7I8FBiu4k0Ys41R^tixdr0rH-#;?26#f2)@Pndt7%TKCIe{gwuJMS&BL9=&rYITbC zBd#EXGe0Ma4BZkzKCiLwI~{Kt{P|J(NMqt7lS3>EXItN_%bP*z(FZe%O$P7K+;@k9 zaQ=#)N>v(_cQbk6QDkfg3&tklGHglC1v!E1m#@PxjQuA1x%1T^UongrQt1P2Qq3AFZ&i}$vZVc0jaiGUnwLIBmhm0uq8ws|zk}3_N7#69! z1Pfbum0ZL-tiHCy>GL6R5pQYi1Uk03zPYzj9zM-V+@OOB#<)nEc~nTJT~~- z>!ppthR2RyjnPOgqK}BOe1NV9U{wi7b*Onm+YVMz8xl4DEhqoEvc%U=*X{4u>5>Xd zxyQREIiXZtyh__;=eDN?;cE8NnQE{{mfKdVug-*SDi_nBpf(b|yu}(nup(FBWY7Fc zof)W+q@_oHL)Tl+eev)bT`SKVtZb@LUEYZ^0ah|?>y~0elh1?^!$MZ8 zDf^t^2Br?2iJ|8+0*sF@f(9}bF7P8E#^7Dk6#@+|a4b2!*|OveMR0bEE@OGdE%0KY z6cY0h530C{XR@~{EZdO%T=VDl>O@HG*o4 z7}t28!kiF%QbDUv(K2))_m|S4N1d_{9E}fa(pRW12Bb5C_@tUo7o<>NaL1(XV%WsM zkB=t@HP#daqnAAQz8K|J8iMzPO!N%r$0kmm!O18;$qEdc{ywX(`z1eFOpkX|6)+bZ zACUflccvZC!@%>(G#S1C7FqL0!RR(9Bw0}?~e zI}vXVVac}yYY9;WF056AIVN~8(6~xVr0H~?3`N(9SYt}G)d_6^W5|hh<(=h`t}U}} zW~sb{ZVtirz!PsLItZ4wqhffUlny_`P1v%i3a8*V z1fB_rgWbLT=Q*?qtu_r2%qC}B6n23~PI8-kMEO_ash)BLEm-SD@DOc>QH}hY$ks0p zN2eHYqYB;Le;yUVlwlAUwW+wgA*J02rB4l33Guwgr%R7)Gjd-t80)TG>Ok8<<%qBd zwiBqDY9Eaz1N#||xhM;v-*CmHDC$DE;i^*KIx z25pBjpc;YKpGFE5Xl&cs1p$X{akQawuN*aIZ}6{ru1KrZ=dF z9!KuWc&DOrR~$j0h_<7LQ{a+fOHq(y8m~aF@=flMR4ZLI5KjIssqZ6|?oA`J zUTZdykmv3Tx4<*E&5=wdgE*b*vftfkxsOKPp#EU5*8ASnS9N{KW7h}n?13*iAyUw+f|tn4;oJ}EUet%&aVLA1$wB{boitTa6QrY8k~X_6DYr|seO zxM5q90apS|2*Uoj_5;xW;Eo9wJ|S3%#*(7+|%=Kqe>eP^aBxXts?e?l#2)#JJETa}}xsq4eZAezfELFSi>coVj zQVTfod}V1-Tq0$)lmd|n-_3~XAE#q zi@XHIjs9#_XR3%hF>qLA&()~Bf+JPMGbhqP?zfG#8zC%22xUQ8SoirVrmu{LRK9^& zQjrOHf8Xa{ECNPizA0elB;CblOdl+ zKa~@|!HYEf{`Oo=r1B*@2QD8@$Q!$xwP=sANG_Py^1b}=7UswjY*AV`hLS0zL=bJb zIwqK++36D0xsI#1dkgMaOtS`Mq49OI{F@ZOVywH`^)!%|b@oqw%rYLbxWj68&tBn@ zFPXd|tpDwE7@@4D>GkY+c3-ry^LWk_rMb#il-;dMYI@{VB~d|c0+YxSFW2^N;)_09 zhev;YmBOyvv)PfU^Uod8+csxntQ5a)77RQ>X%X`P~7QOK`+=s5>0{RI-KX;t8uWE)wee-jZ zoiSPvf-Lb}FcuQO;l8NDGsLpo+f^#~_a+6kPh_jH~qQO>kz129JSSQ~E;UZ|d_@s_Ji&}7xcuWL^*vtao2y9-%MMT>n+>Q?| zjCa;Bwv~?rn-pmwuEUUyj)aZ<&D%znrG*&VgH`lR>T%*EeZtm7%vQSl)BSP3@%7o; zxAXzcB!K;0J)R)k*fsg^ifF5NbL6hH-ifVL^M;hz1Cn*-lUp;#L>&4jK-uO#Hrd~2X? z@ZMl}hkKLNR>gy)AqxvyK@|QKF(S#!zarZLlZ^e2{d@zq58F@uSSf0Xmc-h!scbC^ z^7ueCQIAPU<1?{6^0uV#K(;Zw2m!Kp-7k>(Xru9>gny>kGy25kc;xZE2gGjR*OZSw z?Do*1z&-6n7Kr6W)eVmJ`{;_d9rRkUIoKYBuC7P!QM@C(U#Djg;wJ~Vfok&gF`4xr z?IM)?y?luR)0fq#y4p*xX!_;Wn~z0?QFL`J|E7neF$ziHZoOtmNVLjkIni1$kND3e zZhDABiJuiMq|_fFZP;WN%rnb(ajh8{a8l9XpAW62O*j}xrG-G{hW4>8@tBZGzRwFO zmxJd|;JXGl1SiirR+y#zhh zW1LL>xVKvfqqcPbpxY+LkxY}4}F>Ci(sP5fA$%9~-IuJn4_@X}G#|q7HytEw`tJi;2y4|8 zxF*F!y6_I4v|jiaXi^Y8>>7-9oOKc}%~P9CKCvGwbko%|P-lG+E9+n!St}SRd7W~0 zhq0|DsJm6)Pw+9M*9%^55&vSGDpuYt0colWyZlq;8eBz^^)stAUDcXcm)pCx!TTpp zdhBy}X_(G&egR*fbQuGJF}EmMjAHG(Ja?&S`48y2{u!8k!8xauzY_nd;heW6lOgeC z3hmTWQe1>CH2u;wr4xoUho@I@%cbK2kTbU@@0w`>*1_JD_^rv}zfaP{RqmlVrv);Z zA?OMIK!pJ}9*k-Syw>wjP#kk+bH*5UVr<8Z2GCP3c5Esm4L&p| zf#d9JTt(79gwSDbZqH#kKd+yOZErJW^sf51Gp0`|N-UlmII+&3-?JuQF6kr99uz7B z`?piUHauAAqaU-Kfqq1cCKk0sj6y5AAybTp9p--2u__#$IVt5H9+hMPkqWbs*tTQQ z2G<8|8y8n1Sj(%GC#6j-mf(!MX4)}Z_iLg|t<0W{gXt?V_EFi4_+?0AF6Ut1;&9k& zuXj}y?bfNNH8qbo4*nmj@9v;u=k$!N+~D3tFLjvQxYq9qmt@8j3j?vg%%ZqqKMN`D z{+zu&O%cntt8tC$<^cr`Da(#`WaP5;UJ5^P63WAd7*LK4(NM~mQC@8*Fz?NOVAZ?x z(EeAr9<2Yw_T&ZB&FBMFSKsc(i99!s1#NMiY^Ko6L>GH(^?>479h>Q4zStIdC0>Nw z&U5X%PpD8r>xYUn-I6O{bKnF=N6qhVlFw_foBFwMqKnx(RX3yJbzZ#;Ur6O;Xs%c?{n!;PvxP#=v#u;RN#uVPRKRz zq~$s-gudao8`=3e1EmtRUGaNGi6$a+($PkA{8C8UyxGkbZzA5y#Atg&=rh~ z#%LG?cK7{yoaI+UE0N@&T$(y{{*^Z2_3+LR3X_$7*&zBc;~3mGoA5@+YdK?GU~PTp z-m$+OY|i342yAKM#diBS#=&ogGC}=tJImp>&4_pM!qS_xRJV zCgBQJhTJjoGvA{048FvdxlCK%u%2FO#i@5fRrMi{;d;gO0=cV6Yldy2vTX~J1gF7i z9V3LUx#cJ14JN20TX1=;`C6(P0 zL=?-#?@UugD1@m*B%;S_|X#Vyy8qp88$lvJqIdrlvIU|xKXxP1?M`a*^m?nmaq zj3b+GmC*eC_YT|jaV~-G!OqxL3LbRbFG4}jl+Vu2Dn0z0=G=1uocbVGPL*`jNcykK z-0#?*qr!KqBs~;z%m~(VU>q%7Hx~^&TY$g0nA$AS{ms`ZiICd$ljjgvdV!E}9WAQh zhd2&jg>Fu|k3d!x$*YbHs`PeBHD}L@Qt;bLeV`ss)VMkhGjnmhU^qN{e?Abl05 zpQh>8@RzN=e$_}{Rb0a;c=^o_Z5<{re1tIE4;sb1&J0;;QGDDTj{rw0vLq+UYlG2B zUGvNbzuJAj@%M$#F0^w=mq%OLW((J=-zWc2U-04bOWAjINcx8qiAa`!UNH?m%QaDVoGb%OzK;F}%A;r8h4%ewVA-_Y3= zh4%g!HZB%;epb{0!;Id;55D7|^zr-X%0qRIZ#(A4A`|^TGm1{G`$uPS$S8^7UMuuk zWOR(y-QSKgc=y3M9cw(xk3b3t;pm~XrDr#r|F${cykju55SOKNo^qMH47!6dLm!Qs zjd0$d0JY??M$J>0&| zKq@Mv$8kMX=R_}6m8thJ-X#Ox6Zuh>O%(NsQjz*i8YNp|2~E6s1-OVR04{nHr9Aab z#=8{6_e!D^L6p9$Zw@|)4oQ)!okCvu5w8~rbQJ+~wfLB&Z)4RT%1&GC1oS|IU{s(W z2m$YthA(*zSML5RKiqy4-`jiMsw8Iri^2qu1^&tRlhJwa=r{u+Bpimn+^7ROu%X2p zaiU-ToCFVyI{czE0!(EMdFrSUjGyaHZ{0z$=+Ts$5)@$lLcm0JkL zxH{3-|Jj10S``(E1O3g3em|$T75)NnvEStI;H*!)((-p%w@kQ!=?lVs5J5x3tbena zD6RmVGMKgJ+t&n)0*A7DFk+MJ2*!w#e6lS8)>de&JOw}P-a|TYtBV~?!-p%tMBmW> zj66w!iTuC*Va|l>=+voH4$S1{171YYId^y4^bmPGWx_pqT|bJ@3s6 zssyrc;Be5*o4`zYGT~8asvG?017CWL{Z$9p5mt7{s#+C9$kl4D6g_$c5KPT zHeh}u8!1xE*y7<|#~gk5QK0u{)dwNF4?Bo49%s-S0XXT&4X8aDYu+UNlth&5iPFEeuE147eTZ`>4NT`(VCkA?fs@L#KF=Xl+I7IG+KJ|* zfO!?Tqt@5rcIb+=_sq*yAdF0t%jcDdLErIX*VO*rJ4~$gnn!qt5$7!kG)0S-ryvvp z?(3WvS9wGQuLolKZ0)EyH(cNoTfUgxCp5>P3xdQ2PUf$ zzhcsC0I1T?gHnF=y7;as)%UBx(UGTN2|v?o8Y_-C zLWyHzwuHwAORs(ni2v}t?V6{NB5Z^q&aY%p!&npOJ1L+~L00s)Qz%o98 zfuw;Iz7OnXB+r3@J<)O(xG)TSbxChNf%O*?9r)n~Y^E6)Sn)TcfNzuln_v?#{cMqZ z;oZ=hLq3_e(0v*imWBDh%hMu2aUymUHYNFLM0T2A( z19;;WD(=uX82i^4=v4zu{|!USG*0xeVs+Eq|1tp{ub&%c__+S*4eB$yj7{}G>#@X9 zF#rWxVnG)u{MRVV52>D90-CsLLShZv`KM`q&^2Hd^5{WqKt41>9pu_a3|5o$DH6hH zroi-G{-2mP9FA0sWf5+|DoMyzTa#6=2DO(9plc&d^t|P-EZr~3>@4-J7Hv+~Q2v)P zH;Oe$<3gkE@2sci7_Dn;+`FOUKF17ppWZ>0ki(6Xd1Roi1vGId9m+_yBNe34;pwQX zDvbG+74p0S|wVRGi`4YEu|IuJ!$b^PPDd*;M_7WggQ5WhaE*&;x`t0 zcQ7ePnn^J4K0m^ZEkeyBMh?Xp;5PqfwQ>ng@_67XQ`d&j&lmS=Y1VR6h3%>go>NfM zXVTAQrjS9gMvuJ@z)kgyH7>UMgh}#E@~1e$%ZcPmpew)}|1TeG^I|93vdW@aC(SES zb6r@WcZBlzO|}2F-Mkz}ROP9>8)0V$(xUkQj2`6LM)}QG9`TgE7oq>G7(bEMRe`b8 z_L9(hwaBfia3pT=iut_c#%{BV-29M)o9et^Bk=vq+&%D8S+=FKAAq|30k0KHZ?6yi z4zrh?t32cJvFCNBvgk9HjF=gKmgdUl+SkoNJ1!I>$QRe{TyJ*q^B=dF_4R)gTC=a; zy1nkDaMY0=(Y_LU6#iG3eS7RYPR>8nncS_<+{#?sr(0CGeqpGBy_66%f86QB06`y7 zD#!1Ap1LofJ+e14mozk^>E-3f`}ZTa68;vkLg`6BPUY#3u$=wG30i?6sK4Gouas9O z2FP9B=$Dd(gxXi8ZFgCK8P4S$F`qki{JTs_7A;A8?wiJ@DMCtnWp~ySX9OE12rOZh zt8H(p8@Bn!{5yMe{J{_=@U*Z(AnT2zPRw$P`cY@mMzIAl_cWo=ytn)E*Kk8vwRVf| z4}05L!CDbm(GUiEd%*JsWL8`uFz=elQHR>W?{w|kB7(-;=+GMnu`iAHw)CF`n1wv} zXS)w+P##T_(yiK*YT58)-(p$b6FvzulJq4@kL_{|E<0l*gIs{mw$O8^o z%ltPc^5mwdUyGkRb$7^3ChJtDIbGWFvFjOru$_>;VeqbI#erYp=z)L7IN-(qe}6W~ zI}LZ9#qVt}SU{K}qdrLx?vK@s+0IAv@z=DFhkSQ4*tY?JdNS#NL`FR}0-d*wPIJ&G zvzX|2Ke5CW)EK2^tkkURbR^fr*~qJi%D8Wlf|{tJuz*ujzi5~$D~$@TSbb-`h>P>_ zm#{q+(>!=4>Y*Ei1sOjZ+|4R0u`ZpSdJDzGb_nZDt~nKGJ8>1)686kZze5(hlFA2T ziv;{J=D*eV^CMO(w>nC>wm8xF`{)j5Q;?o^>y3Y89<}wDdi3v3$nKlD#@Qn78~9uI z;^ZxopyiuC@1nI#jBqOlJ&z`=p6hq{uAtK-Byf-Ru~hjT@?Jaq#l9i{q&KN#75X|k z>%wE0#|W(b=zj34{|OJg^51yiVX7V#N>mF@oA9;3X-;v?yfr7_KlO)peH2f{xa>L6 zJGWzyW==K$f8&?$?R`&paXAEff{TM*3b^z#8(TB5-~fS$!pnoOM5d6M_62zf`WA1G z&x32V-&3U%|D65b(t$rwh>{>NN>o6lVQ(HZbCH)-oKc&~jtKgtYhChdpTCE;N%Yk7 zNX;zph|v9+8Ojsw^y73WBTs(%87xBWAl#iLh-QeCIu&dFp-8uJCTc=Z55V)}O9pfV zy#KNe%u zcD9ax!+byM%8_47j9LHV1bTl-MMQSo8jyclpR5I~X~fiAdjHhC@m}nE88AdlZzPYM z&EEUV=Fb4R=E!`R-pQkb=rO7DrS<`f+DVL0fN(uwtHLCdu;?tCoOV6o4ZS0rjNJk0K27GX4ULRrgR`L2#2d}#loKiflYa-ZOQ z##%H##M%h`RX=bR?=#DQ?;CIWG`-w%3n%SzXnb5MsFFCj=Q)OH*?U)&VDj$NSmZ#9 zxfB~h>|>j9ZX#o2Gxp&n5L-%u1P$;Ww@dw)tFe9kwyf;3kO}))KwLg&j^x34I;v}9 z=>oMkVsNJ2UqOvMXtXr)9-4JqHjJqh?Coz%XdRbHeR{oXc?jKgk#$_XJKI!Tt(SfY zQZ^%v@?5I^O2zPxB217!3rxs7y;-wc)K2(a&F*B7mK7ivS?y617A!6<>HNJXS-*#r z-RN-yc6a%D8N+;gA23ei#2#P>+pK4HdcZKH40)~!pbLsh4FwvLlMQa_i?9fed9Rp+ ziCBx$=ulS^`Z}qW@%~7E_6{YDA%l0m+N{sgOy*jB41l*Hr&N=%+gVh8c3K8_Hix0= zFO;ZKiwQM!(MjOa!fvpCQt-_>E!6HdSThmR;G$}i4{6}$QYx1%PQ%I6#Q$EWdGf^ zLGU1D?n5fV0LF;XDO~ehFwU*2U}iusqZ%WcagL$7b(IzQ;gzB00OM4FjU<5>Q}^|jHTSN&{dxAJQKVK zapN}h_DID0W_JLgPM4a>x_tb{{U3wa1NX~W&Q;<<7l#h!qnPGbePGBZym`sR_f)P^ z|79E(LO1KqMpt9!Onv@mwy-0s4f+e9!as}!@7a{r3617s%76%wx5oBbMPW_gn^%J+ zDDW=~JyoS9YPVXQV)NxXbAq9scwt^LCI4+aT(g5vvN{A0Pi?hi!#g4630P2?8cU(s z0q-{z=Rakth;V=)Wc%JHU0~@ER_Uxe%7(sMaQQDgy)~N(e*NH!F z5sr$UTTh_BlS6uW{W!AR;Z;$)!9wLhOZzyG5=T}z$&UCjSK(j<{B<8pf2Qj!g8yow6@{nmH$^g zouUwdN_Vdlihr-0iWE|0+*x49Z_#!Hp{kB<{Y@eggSk)`0I3pjQaykHlxHSAB$16* z4SvpkC${M6-3vCJKkXHyL?4o4PiUv@I691DXS&-`f1UPu^jPq`u(Hc12Vs^R3Y_;1 zlaFp;|B*(*TJsRkLp~oygi7o2B5oq)fsUNKBi&%JR6xWUU<4C5qwtDh=AL(_22IR5 zQ_>S}U3pMOqja}L^yrqg_A|U(ajwS?U}1q)C@8o-U@U!3j_nM>HdT8XfBLhKtj8*82l`xb7LntjVMY{q|^Ws6S{>INMvc49wRE zW{WSpSfAGS^I5!YLfB5v9-=~(f*-?2l%LCo2-qoH?B}U@AQ&$JeBqtZ-fD$g*-Ik% zaXqz=#^@2*-P~_wW+i)5a$keX-!*KNLtu>oZny$-bX^Ni=?`9j@N4|i;WEGU_+tON z1#oI4l`u*(3hAvb|K0p4d5!gW*vtomHOJPy2VCak)``Gjs|HiM=8n@_0oJuUOFvUs z+pFk9qJVhDBeD3YoToXx)nCn|<*!-FdEnEn;G}17fv~pSiwpC}(Ygn>{a!(sDY|ZQ zc~OPP%3ie!n;6O#j&y_?_#J=g`0(mgi?c+85QN{!fkBeaLWSp&+D(ZCPV~>0dMkE@ z3!A9{S_7v1DepL>ENw{FK2cH;%+}#iJ8G(FWp1Dpc8CrhNzv4I(Y=Uc*@#H{A!gir z+TFNjCey#v=_=E|>wLAPjTOPj93d39|7hy4=oySAaXq^5LOx@yfEK5X1;8~gI(g;Z8Z+%TN(~TTQMTO!>c2K|TA?-4*gDI++FxTW5@R;#tU?;&rA3iT}X~Lih%85zhbN1gD=5 zX9mV?co>Gi@$!fSAD^tNrFmpS3=}(W96y~D6!1gLS$dkUxFTQLi@kuCx6Iz33$BLdAMM+M~1BcNPbY{!=+ z(2GSmLWxN2#)ZAJbpB@z+BU@9!KlGoRpnd!rakXi;{Z5l!s*Q|^o#U>VN4v6)>6Bv z@$H8348IDq^=-cw?=l8gpol%Cyf_`}1V_Gh*#kkvM(UO?jzh{P%R;bvVx*;D?rKDC z+f6*{coo1DUX2ImU`XJBMA_x#qsFmb*;H2lt2b^XKpxeXCJHPacnwISIl1CO$qY1V z8ozAe{5Aw5?Mz;wVFU}mOG0*37g|3Y)C|#>jjCn@fAl6xrh~YjZ_I2 z|A@N9(iJ!(`x{!cW!BKu(q3PaC%$qvHQ@2-W`rL>-J_8!F4x-gl>n>T6?nC%ME3KD zcejh^j-SB=;{)8@l8BRN4J}_cyT4zE+h*tXg2f=+nW^D=rN+hb4tLP!Q&~8chUW6H ztGMF^HQ40SyVq;cj{ed6l2KUnp^Dm9v%*43|xDN+Pp)@XyU2;!n>W@`<<>H$Ba_Ycsf9-tzeypY6vTBPKE2_WXZZG7)d z{uu%p)DwG?GNcckJYw(Y04%ruGBcUrU53R0)MSVuTIsIpNy;EX3cvShYi&ey3gk7ZL#w_I-eI{#s;dwa~;?QJDi zHsl{N=t)C;Iby$wwC{!9*>%&ii^xT)F0-(?*YdU_(#nr;>P!~znxKg#G=pZf0b2?Gz(6VhGHiSj;ZKPAho&;wBUvhn zXJV9yPGx=#$R%RRRsWSKYt-4&8K3_v)6{r_*a^-9(#g{aU*1;p8eYRCzsWh{Iz+XB z2|(P58PCAURzwcJ_6F5Xs{vE#zoK$T_B<5j6^`9^MHHnWDm%w3qV*~}S9nb~I|44w z5{>8*SN07W8)ekJLyKGkv+ySl15#=0_i2s}3$(Ma^o{L2DwB87Q>=r>aC)aLq=eX_ zccX)J`v6-j4~Mp#WjuDf%!yko4oF>9qF?Sk46i&~(Zp?k7YM?-uUlsjNI1 zJpYb9nLaC<2!k_t)H6E+X;I>O-Qy%Rc-A;%zsPFW`smzi1_30 zbK9Zo@t)F{8+fg?b>X@JN>mfHGJ_F6)o%Op3j%vpp>w0ww6t-NZ~D5?8BKhW%wXd` zxJ76t^MdqyOKaW`T#u?N@L_fG!wc>Gg4a!&0vu40tuZrcPhi945;_)mB5n8!#zXz- z)WPFuX2aI(+xJ9KANk?w#qc_FJvVg#MU1@r6}!67Ci?+n-0S&PMMnA$M+6Wt&WmfT zPCthP$ZdPAQW4@cwkELlkMlW_vSjbv1Fk1u25>+yo&v5JNMtQ4 zaHCq5dT$;`6%LfiiB_t4BgoocdNc!Iq5jd=74`j$h+k#JPZ|ubk_S=S#Vfn_{AsVg z#wr8iNjsd(-H7Xd}6#Ne_RJwd)hYP*t+`X_`5jXZz zt@!wbTa$>{G{3XC-`iVovdsIanAzBeUFUxS#k`fM?Bu>R$n{yM+-x8<;Y2?XYw6ak z?)qLkLT;D;@n#%sF8gcBqt(Z61Q28m05_O-xq8VG$=&Dr4;+->&(+0TebnD!F9prv zASt+xy=)RFNeg-IQW>1(!}oHWseW9HlAwK$?My|n`$ZR--XYa#AXX_(W+J(i5ZI_E z%a5$nkjrk&s=u4cCU`v{2Wx*xDOi1xAB!{-al8zK@EfqwukwQT0Lrj*$@kWfpmmHc z+nfFl?@SE(sbdIW>6-T+%TN{MDI%NYbU9JG5Zc30|2OZ^v=(~|5Ztf12ILMTz}vZu zc*}3gWhVz4&E=FbQ1YlG*#Rg8L4ufaqD#mZ%^t308tez}s0Qy1!yf%7dHR(xVLjFX z+Hq{Xv+eF6p*;vRM@&UPz%Hlxl2!-{Elk%!HIOx!b^sCs*aG@cpV-P5({hwAz7M~|gu!W~L+`DHrM#tny zOu|_pzxv7!y@KCjHY~i7GPULi;s6?|dIoEck>oKE5K3hd<@`q+(v0y*9aHYs?M*Q&ZsC1fUGMHPKroge4U6KKA`^)1LEsBH1+7dj)Y#`w< z-|TBY%8mBVB_n^@-$GKu6$}2u6$Y$iuLaal2Q54_DEM)cx}q?v!{rFdSjSw%35GAv zjKWr|6-aO&-2Uy13ENu~&Sc?K1RvAj!O2e&@kUFljQ>RxAZMm>z^o*JYC5pRbvFqP zJJFHW4}Uzc4s5SsE2_RY#=A$^=^ySuPz>*Vvv&4<-_DDlZ?56SW8woLg*Alj7G+)%vLbylLnjL=oZNh?kNn^ia zPi5_AoXm@zQZ(LvKQ9!g;199y&7i~Q0%>ghiIR}%yn?H`1&5#5`ZB3L?Y#&V-zxoV zIe&V;QnSR*>@9M$!2YSAC^1J^R9aWmO|6>3Yj<}ZU=c%psYqtf^tWng!p_qd_NLPOIPCq1P@H;UFaQ%_|I9ZbQlct0k2&@Z6R1 z`b0bhb8*$8pWJVcZtcH#Y2!xH2A!HEb1>IjTpSiB7dg4i99_%}X96$v4x7k-g{v3d z;g;5$pI_X=MXZhVQ1H{2mB!@myt88i4CWGh|mKj3N%%U(-Uq)=2;16xbjmWh1-@fOJi3 znW(?DW7}Y(rkT^zamCrDfV}Q;qMy6%N%e7-wZsoV|4s#-^CD7*Ekhe8=1Ck2v+j?1 z^uoh=Y)x&RhM{Qssrm8i$R=Z0NYXU_M>qXQNjtC?TOZ%=5`vcOtLn`0MMs%Oe1{t5eV1N0t|t zvE?sUq?@GZU8M3Dhjl^p8y8cmrBw%!)p(QRTTg0T>>DJ&Fq**&a&9NK;y5d>)IpVe zjvLUD#Xp}*HxB{O{Vh~&uA#(1xaNE5?HiJE{HTu}@oT|ZSgDSg-a~G5F}|Qe!>Z?< zZ7i&vmt97~XRo%`?ltfB)5e9%n?-v|o}iE4$5Q1g46=)u=B&KxFbL{4>+ zJzMet`V-`zKzEf;;`JFzzlq!q7{tF|t*5X%KQ}E|b&B^l_%k0_h}C^s+PF8?X=8tB zfhKuoLOB?$I$oh<%F&GdSup!l$)W13Rt*PDP7vSl&5Ng!rL=SS(G8Y!lfS)n-^V`X zDW1c#<}~N{GT$$#Amdp-=gtXN@F0ezyDPD+6&TC?jnU=LyI<7~#C)^Q=21neg(-~~ zcKowjC^wyCUkRheC~UkGsdzV8t5!I0MHL)Yij#J?s;GFl-ul?p)@(nzpM~nE-F0JQ zCx2T42wxOmy~6-8Z74aO7k=$nIXm+{uu`?A{oN~V>)?}QfEy$j*u;jmeOCmR866ai zPS*IQ9X9-#^Ubb$Zl(2qbaLnMQ0;vjz)?N6vR)JkF->Ee>sAPbjD&7-YcjHoacvbQ z%S0gz6OAmzBxI(NY%?5FlAVzfp@n2iaXpr?WGvY|zvFhF*K_ZEJ=gc|h zd(QlRXWqY4-c1z?;U|h83QhTvP6^QwyL%8LUcjgnma5Sf?)JW0%R^6$MAPU+mx|u8 zekm+{p!x*$1M*ZRI#_4Epv6zD;BV4zDWTU%Lhgsunyr?03X-m97aL^8x>L0zd;9%p zgf=%QI@U|A(*`Stc8_(cgjWH*Tpg(Y4Vsdc+Qfp2^yo!oF$P`7L+DQ=N6v`h$~(10)sDgj+xE8 z8_uI*y~1D8aF(%FS$nTEbsgSgd!*~|5SPMM$+DhwZRKl|~@PI-v0dZgp!{bsv>1#wY6 zN2n@^LC`O2t)m!7W=Lp>Tuf8YQr62Db-|tP9R~i3j`^J6V}>lbxo{;XsVDmc+` zgatSvj9rB6YfJXO(8l+%lm^-N_cq)N(IMBxD^N&1(S{rW!`rN^@OJCW32h-(eo*0# zD)MqPsecQ2PI^gy4pLSt4O`aJ_WG=@^0D~5BI|CwtMAx}Nhw)TNcrkc-Qn#_0!MWrstNXxmCr;DH}d zf>S7~{eG$GrTCKacRolX0pP$dEmqOsh20VLx!icC>a;4~v}I`8v@ zrh^4d=I1?Aa*;h>wVAN-tZA|OdYFskjIT&$qtBcv?qUi_bB~TZ23I;@jhhBb)!Ck5nZ596 zYIdLs9HK|lA_|{b30Z)qp}~dod-|pt5}{kbLYcz+s!~W4U#I$o>=L*D7h@afEP=6O zZ$RU}-VC;GdqDxV+5r%m12ENKARKAJ5inWgVK{PwBk6TJ;CJ^3}dSVcz3=;TK>6xGW8_-}zveU_STw4&B=p z_QQ!qpFy`TVzVp&!JykEFaVm4U9S}Jb;~E8P}!B$+uPJe9vW7Y<=8%om#;xaI+_=} zHe_qDev>*8d>ps;2Kh`=+~M4ENxXALXIwcN7}u{>x~#00fasy9r;ONqaxdsA7hnb- zsh#zHc7wzn@uonN4M%Xxs|6U<_Xs$|awXo^3UQt1DegMEjz1Yf{vLW|h;z!*oCsa7 z@rZ7ow3hB>i>i+~Fw@d={_s6=e8DE|k80p$SZ!i}C7qQ1_;)}J;jVt2&mD?;8C~UV zv(I%|1#Ppp2HT}R#o6MRHDo(b9`*1|a_U+JBWn}*feBn|1h^K_a1XW`Ty&L+;5zIE zx5KYjz%@Dot3Pla-@w+-1UG#jzCA`jyD|2lpgu=|37Gtufb(l+3s1lZ&;d-qJ=d4; zHCzh@-X)O08H)hyQn5f?cu>RXb6(Xdx8~{8l$*Ebp+z7cEy4P~f9jj7nlh6DDjc#JsPCI$)lsb1DSDE@5Qqo78J?!yDbpE*ZD>V8LV{?1EJ!0J{ zLD-!(GGN`crg(5^O}=5xdw6vdk;tCmTWm<_LoAlW4<|>YR}--)%G~HWY1NQt^G{DD zRA9Vo_|uuS$Wpzo%MmNCO_S6W|Hbt%A#u)G&tZOE9I8T;*Zi0>bI>HpT;BL*SO~Bzp4=EIm`fs8b(lUD0voR#mE!6*E)Q$#6Qwz zP^-qKZ)v5ngSh3FyMHo(vlsfsPo?_cTj;#I58eWbhKeiynwqKH77ZBy6b`sY)}i_>aRM%O~Ms5TpGVNc(}Bi02`@^?!U9o=^u z7AXaF>oXXyO?xo|#nhp`nX9~HW0}Mbl^N-qa~p(EddESjt!|VdRRFt{`NAIn#3NBY+gz~FLk^uEn?r;_J^&F=jYRM zVvlkG0hBJ@9p;YoZv@AT=40$WjH*%Bh|B+)$}mZ|9y-+NW^YUxLRM72yp5&v9DrNY zlfJmepD=WI+l=Jny!g`b=b_ha2$tnK@p^;<&NI@DA8YBnTQHxnnhc2bH}PtSDu5o;XzS&}n7p+m46&WtdV2T?5+Oxdfxm6$qo&x5oApVx{J7vKgg&swxFCI;JobMu4kS#myFktOVD*T z_Maqv@uHEGItLB9THnMEQ`q%`tWa#soqC>~;?F2;fo1!kidDqpi~HqQ`U2i8RRv|u zhoX;lEYuu8egjWn`mR)Odp^ZF_-jt1!_{hH%V>TnYvs3`1w_CFvA-7a-~X;mUKiX> VNojWIPi}+fTAH806drLQ{|nBG<{1D0 diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 0551d9e9f..79f1febd5 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":34510,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":34916,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":34101,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":34914,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":34381,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":34644,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":34375,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":34245,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":33548,"get_count":5,"put_bytes":88,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":34371,"get_count":6,"put_bytes":88,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":34283,"get_count":5,"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 index faaed96b60401340dde6efffe77720db8cd843b5..7fe009b5f869a62ff288e7170b2d47fbcde16bf1 100644 GIT binary patch literal 14782 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 X+^Tq!B2$;+Q!5=UgKOm)u(1CFYd<>l literal 16527 zcmdVB1yodD*e^Vkl$6pf-5@Zeqyhqhi5Lf-CPN0s>*l>)bO?L8H;YlXvglVJ?jzPzbsaga&PIH!6bK+d)bW^XNua zP>5n<2wDUd5(5G$hJer^=wS444j&&dRB^5!7)MV}FD53YrKM$OXBQkCoRX6A`Sa(- z#>VdM?xm$AfW@w;=*#Vw#pBDnhP!iU^!79y{RLIBy^S6N0o*_~!2iK)F>FFHMwj<7 zbabwOC?HTzWbXzDBO)5qJAkH)M+fWdhJ>J1K&K(@Ye{~)%dvG8PL&bFhlf+|LRir` zpl$ee0%&OZ-SkTxMMEdy5K2%8C1r>r@D~ySYYc&*Loo6yqTd03F!VcDbYmkL&cCS+e#Y2Iy0aIo1pRKq*jN!X9V1WX9>R@yp zRTKhZM(+b&GB?O87Jq&CT>Wj#%-sZ4B&cz%08fH9Dz#94f7Uks*b0AZMQ(^$^gPL@ zT1q&fmMBSdh*=(z{2HcS`s=%}Q3mnoC!>erlTId06$Rfp9|~Y!jXmz|P^U{6Q~J^b zP@quqabMBAqOt z+o06?O7_P%AkXG(T6K3w(}#G-gaG-EihP+WdGOZa`*+39iNVJlSokCZiE_v*!Vd&t{pt?~XKnQn=DcEoUtQ)UWsPFZW_Y8< zIG{y!%tVmWbp&_^mw!{jaQHoJsVvOp@tPt|$c>Z)R1Q6a8d1v|P|g;8kJj@gZdY}q zSDsn3$<`qhNge0TLI8OtqG6!jWj5<>qSJv*n>enrBmqX$I$UFJpw}WlhRVF()Ak93 z-pA|eowXcACVlkH#N9|u>W`$#(a>oD2B@>99nO?ELA!Fv%|`6)ctwX^_?7EF(ahh$ zb1HNtq?^HBwj$BL%ivUYcLBM12%fit-j?e0du0LnsJ$?a4x1Oe(b!MR`72w{aU`VtF-;BTy#c_sdHD032K2tGZoTuc?M%ogu@&3lXm`boQimqADTA+eN8 z;O<d+r=x2d)q2yX^yAQto9JcqY(P-uH%qcv^(=z`-=9@c3R2v?(zOQS zZ8ImbX(A@Kpf%J-^C~9qhZy+7)1375i`V6mGOL!I1wNT9+ec%~K6*S)Q$ArB%wu!R^hsVb8yR!;dAXAB)&X%MYTfD9JUu#la*0ar6ysOdIwZ@PZ z3Zr~u$RjzA8U!2Ir1{iTyEg8et9qRN4l?@nx>nXCy-N}GzTfhXx_R@t&IVqOirYO;H7m>|a#CxwBu6%=PstL^b_!t>o$X#M@wt$fkxeX!mz|EIQC`zAIG@rO z(HFGg(t?}}!D4tSWGtRKKVZ!r+8<`z$n0zP6$j_JT3QbcfYU3p-aE;-`3$7XgViD( z+c-=lhVLM>JdCCej84Skmi82p-TSX-vgBl$-rgBImQ(&9JmkPD@!pB^OY4U-F}sg< z@Em?Po_9G)jiE$~x}H3fsq%i=?$N9v;>&)j_1;^IM3eBP(Q{kPc#%L;;-uF?v9D%h15^yi|1?;Ao zEK(|5evQHG`wj#{g9oP1Q}K#+B&@d9g%zE|Z$P;^hWELw2(D}q_Hr~es3SLT4G)H& zj(JHn4o-uKlQt$>DB*a>9PgB8P2UkELWQ6$l? zV9OCHX=ttwGz{(fTA^c!L9e1?sxZ*gvftlL=+{yEf<{jKM6g z`2raS$j^|=)$uW}%K|=-5zj>ySa4O%PRi5IdOk-t|v!HS=x$$xpN#iq1MciUdE{}AqC>3{LbP&3&>xCbuCf? zA(`(hM1OeC^tW0%&I&fwrR=XrUKOl;2zsJo313Qb%j%nWXfR8Kn1*|;TPfg~%<`EN zCQ)1j#yN~U8hKU`QKE!&L=`(Fe#CVWCr6mLZkXVq9BFZjsHdtSN=SM;%jE!5KOQdv zNDV8LxJcEfwOk+hIhh_2q$StUn!18M;_@HP%el1D&CM{c7WOUX3(AN1@|E9PtZ z#gPk76D-(}k1#_27;+LvP2tB�sbWzG)MAzbR_ZK{|T%#z~3LYqB8CRqGz!x&}|S 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 diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index eb288e3f2..de29b2a9c 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":42073,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":42391,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":43501,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":42552,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":42716,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":42311,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":39008,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":41132,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":41855,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":44294,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":46582,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":41308,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":43660,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":44265,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":43363,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":43510,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":42420,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":45241,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":44417,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":42954,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":46500,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":44906,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":43650,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":45121,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":43300,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":43816,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":40603,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":43923,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":44290,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":45166,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":43776,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":43067,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":44065,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":44176,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":43942,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":43623,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":41548,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":45999,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":45287,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":43238,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":45815,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":41087,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":42987,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":41991,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":45403,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":43756,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":39811,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":42603,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":44116,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":43110,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":44070,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":44182,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":43968,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":43558,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":41909,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":40242,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":41889,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":42996,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":43111,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":41298,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":42257,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":43112,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":42862,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":42658,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":43087,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":44357,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":43241,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":42950,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":43938,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":41966,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":44572,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":40881,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":44376,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":44483,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":43902,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":44754,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":42695,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":46250,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":44652,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":43513,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":43114,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":44118,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":42995,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":44148,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":45295,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":41860,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":43922,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":42885,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":46905,"get_count":18,"put_bytes":88,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":43544,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":43961,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":43283,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":42729,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":42437,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":43922,"get_count":17,"put_bytes":88,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":43935,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":43793,"get_count":16,"put_bytes":88,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":42550,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":41901,"get_count":15,"put_bytes":88,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":42713,"get_count":16,"put_bytes":88,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":41985,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":42303,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":43413,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":42464,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":42628,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":42223,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":38920,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":41044,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":41767,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":44206,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":46494,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":41220,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":43572,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":44177,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":43275,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":43422,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":42332,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":45153,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":44329,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":42866,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":46412,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":44818,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":43562,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":45033,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":43212,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":43728,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":40515,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":43835,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":44202,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":45078,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":43688,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":42979,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":43977,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":44088,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":43854,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":43535,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":41460,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":45911,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":45199,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":43150,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":45727,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":40999,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":42899,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":41903,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":45315,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":43668,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":39723,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":42515,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":44028,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":43022,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":43982,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":44094,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":43880,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":43470,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":41821,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":40154,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":41801,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":42908,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":43023,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":41210,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":42169,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":43024,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":42774,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":42570,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":42999,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":44269,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":43153,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":42862,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":43850,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":41878,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":44484,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":40793,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":44288,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":44395,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":43814,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":44666,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":42607,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":46162,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":44564,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":43425,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":43026,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":44030,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":42907,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":44060,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":45207,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":41772,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":43834,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":42797,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":46817,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":43456,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":43873,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":43195,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":42641,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":42349,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":43834,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":43847,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":43705,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":42462,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":41813,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":42625,"get_count":15,"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 index a4d64fed884e3a62ef26df507407a05765e6066c..6ab9899cf4dd10a82f1693eb5f33a2c7a18b59a0 100644 GIT binary patch literal 18131 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 RkVWzHYgdhROLT5V{6Akzwjlrj diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index b13197609..bbc2563b3 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2213,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2210,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2235,"get_count":4,"put_bytes":139,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2213,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2122,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2147,"get_count":3,"put_bytes":139,"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 index 12c56697053c2841a9dd108f04b2163f4fbe2e90..5fa94a0384cd434023e6ac81ed8b6bee46833d84 100644 GIT binary patch literal 15291 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 literal 15108 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 diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 5728edb94..23f4347b7 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":2187,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2187,"get_count":4,"put_bytes":181,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2277,"get_count":4,"put_bytes":262,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2358,"get_count":4,"put_bytes":346,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2442,"get_count":4,"put_bytes":420,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2516,"get_count":4,"put_bytes":494,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2590,"get_count":4,"put_bytes":568,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2664,"get_count":4,"put_bytes":642,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2738,"get_count":4,"put_bytes":716,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2812,"get_count":4,"put_bytes":790,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2886,"get_count":4,"put_bytes":864,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2960,"get_count":4,"put_bytes":938,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":3034,"get_count":4,"put_bytes":1012,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":3108,"get_count":4,"put_bytes":1086,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":3182,"get_count":4,"put_bytes":1160,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":3256,"get_count":4,"put_bytes":1234,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":3330,"get_count":4,"put_bytes":1309,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":3405,"get_count":4,"put_bytes":1384,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":3480,"get_count":4,"put_bytes":1459,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":3555,"get_count":4,"put_bytes":1534,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":3630,"get_count":4,"put_bytes":1608,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":3704,"get_count":4,"put_bytes":1683,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":3779,"get_count":4,"put_bytes":1758,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":3854,"get_count":4,"put_bytes":1832,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":3928,"get_count":4,"put_bytes":1908,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":4004,"get_count":4,"put_bytes":1983,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":4079,"get_count":4,"put_bytes":2058,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":4154,"get_count":4,"put_bytes":2133,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":4229,"get_count":4,"put_bytes":2208,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":4304,"get_count":4,"put_bytes":2283,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":4379,"get_count":4,"put_bytes":2358,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":4454,"get_count":4,"put_bytes":2433,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2507,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":4603,"get_count":4,"put_bytes":2582,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":4678,"get_count":4,"put_bytes":2657,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":4753,"get_count":4,"put_bytes":2732,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":4828,"get_count":4,"put_bytes":2806,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":4902,"get_count":4,"put_bytes":2881,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":4977,"get_count":4,"put_bytes":2956,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":5052,"get_count":4,"put_bytes":3031,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":5127,"get_count":4,"put_bytes":3106,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":5202,"get_count":4,"put_bytes":3181,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":5277,"get_count":4,"put_bytes":3256,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":5352,"get_count":4,"put_bytes":3331,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":5427,"get_count":4,"put_bytes":3405,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":5501,"get_count":4,"put_bytes":3480,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":5576,"get_count":4,"put_bytes":3555,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":5651,"get_count":4,"put_bytes":3630,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":5726,"get_count":4,"put_bytes":3705,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":5801,"get_count":4,"put_bytes":3780,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":5876,"get_count":4,"put_bytes":3855,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":5951,"get_count":4,"put_bytes":3930,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":6026,"get_count":4,"put_bytes":4005,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":6101,"get_count":4,"put_bytes":4080,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":6176,"get_count":4,"put_bytes":4155,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":6251,"get_count":4,"put_bytes":4230,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":6326,"get_count":4,"put_bytes":4305,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":6401,"get_count":4,"put_bytes":4380,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":6476,"get_count":4,"put_bytes":4455,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":6551,"get_count":4,"put_bytes":4530,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":6626,"get_count":4,"put_bytes":4604,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":6700,"get_count":4,"put_bytes":4678,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":6774,"get_count":4,"put_bytes":4753,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":6849,"get_count":4,"put_bytes":4828,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":6924,"get_count":4,"put_bytes":4903,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":6999,"get_count":4,"put_bytes":4978,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":7074,"get_count":4,"put_bytes":5053,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":7149,"get_count":4,"put_bytes":5127,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":7223,"get_count":4,"put_bytes":5202,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":7298,"get_count":4,"put_bytes":5277,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":7373,"get_count":4,"put_bytes":5352,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":7448,"get_count":4,"put_bytes":5427,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":7523,"get_count":4,"put_bytes":5502,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":7598,"get_count":4,"put_bytes":5577,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":7673,"get_count":4,"put_bytes":5651,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":7747,"get_count":4,"put_bytes":5725,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":7821,"get_count":4,"put_bytes":5800,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":7896,"get_count":4,"put_bytes":5874,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":7970,"get_count":4,"put_bytes":5948,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":8044,"get_count":4,"put_bytes":6023,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":8119,"get_count":4,"put_bytes":6098,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":8194,"get_count":4,"put_bytes":6172,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":8268,"get_count":4,"put_bytes":6247,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":8343,"get_count":4,"put_bytes":6322,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":8418,"get_count":4,"put_bytes":6397,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":8493,"get_count":4,"put_bytes":6471,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":8567,"get_count":4,"put_bytes":6545,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":8641,"get_count":4,"put_bytes":6619,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":8715,"get_count":4,"put_bytes":6693,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":8789,"get_count":4,"put_bytes":6768,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":8864,"get_count":4,"put_bytes":6843,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":8939,"get_count":4,"put_bytes":6917,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":9013,"get_count":4,"put_bytes":6991,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":9087,"get_count":4,"put_bytes":7065,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":9161,"get_count":4,"put_bytes":7140,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":9236,"get_count":4,"put_bytes":7215,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":9311,"get_count":4,"put_bytes":7289,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":9385,"get_count":4,"put_bytes":7363,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":9459,"get_count":4,"put_bytes":7438,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":9534,"get_count":4,"put_bytes":7512,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":9608,"get_count":4,"put_bytes":88,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":9608,"get_count":4,"put_bytes":7586,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":9682,"get_count":4,"put_bytes":7659,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":9755,"get_count":4,"put_bytes":7733,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":9829,"get_count":4,"put_bytes":7807,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":9903,"get_count":4,"put_bytes":7880,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":9976,"get_count":4,"put_bytes":7953,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":10049,"get_count":4,"put_bytes":8027,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":10123,"get_count":4,"put_bytes":8101,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":10197,"get_count":4,"put_bytes":8175,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":10271,"get_count":4,"put_bytes":8248,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":10344,"get_count":4,"put_bytes":8321,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":10417,"get_count":4,"put_bytes":8395,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":10491,"get_count":4,"put_bytes":8469,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":10565,"get_count":4,"put_bytes":8542,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":10638,"get_count":4,"put_bytes":8615,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":10711,"get_count":4,"put_bytes":8690,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":10786,"get_count":4,"put_bytes":8765,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":10861,"get_count":4,"put_bytes":8840,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":10936,"get_count":4,"put_bytes":8915,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":11011,"get_count":4,"put_bytes":8990,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":11086,"get_count":4,"put_bytes":9065,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":11161,"get_count":4,"put_bytes":9139,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":11235,"get_count":4,"put_bytes":9214,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":11310,"get_count":4,"put_bytes":9288,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":11384,"get_count":4,"put_bytes":9363,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":11459,"get_count":4,"put_bytes":9438,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":11534,"get_count":4,"put_bytes":9512,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":11608,"get_count":4,"put_bytes":9587,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":11683,"get_count":4,"put_bytes":9661,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":11757,"get_count":4,"put_bytes":9736,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":11832,"get_count":4,"put_bytes":9810,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":11906,"get_count":4,"put_bytes":9884,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":11980,"get_count":4,"put_bytes":9958,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":12054,"get_count":4,"put_bytes":10032,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":12128,"get_count":4,"put_bytes":10106,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":12202,"get_count":4,"put_bytes":10180,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":12276,"get_count":4,"put_bytes":10255,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":12351,"get_count":4,"put_bytes":10330,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":12426,"get_count":4,"put_bytes":10404,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":12500,"get_count":4,"put_bytes":10478,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":12574,"get_count":4,"put_bytes":10553,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":12649,"get_count":4,"put_bytes":10628,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":12724,"get_count":4,"put_bytes":10703,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":12799,"get_count":4,"put_bytes":10778,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":12874,"get_count":4,"put_bytes":10853,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":12949,"get_count":4,"put_bytes":10927,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":13023,"get_count":4,"put_bytes":11001,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":13097,"get_count":4,"put_bytes":11075,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":13171,"get_count":4,"put_bytes":11150,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":13246,"get_count":4,"put_bytes":11224,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":13320,"get_count":4,"put_bytes":11298,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":13394,"get_count":4,"put_bytes":11372,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":13468,"get_count":4,"put_bytes":11447,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":13543,"get_count":4,"put_bytes":11522,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":13618,"get_count":4,"put_bytes":11597,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":13693,"get_count":4,"put_bytes":11672,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":13768,"get_count":4,"put_bytes":11746,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":13842,"get_count":4,"put_bytes":11821,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":13917,"get_count":4,"put_bytes":11896,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":13992,"get_count":4,"put_bytes":11970,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":14066,"get_count":4,"put_bytes":12044,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":14140,"get_count":4,"put_bytes":12119,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":14215,"get_count":4,"put_bytes":12193,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":14289,"get_count":4,"put_bytes":12267,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":14363,"get_count":4,"put_bytes":12342,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":14438,"get_count":4,"put_bytes":12417,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":14513,"get_count":4,"put_bytes":12491,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":14587,"get_count":4,"put_bytes":12565,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":14661,"get_count":4,"put_bytes":12640,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":14736,"get_count":4,"put_bytes":12790,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":14556,"get_count":4,"put_bytes":12534,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":14630,"get_count":4,"put_bytes":12609,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":14705,"get_count":4,"put_bytes":12683,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":14779,"get_count":4,"put_bytes":12757,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":14853,"get_count":4,"put_bytes":12831,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":14927,"get_count":4,"put_bytes":12986,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":14748,"get_count":4,"put_bytes":12727,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":14823,"get_count":4,"put_bytes":12801,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":14897,"get_count":4,"put_bytes":12875,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":14971,"get_count":4,"put_bytes":12950,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":15376,"get_count":5,"put_bytes":13355,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":15046,"get_count":4,"put_bytes":13023,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":15119,"get_count":4,"put_bytes":13098,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":15194,"get_count":4,"put_bytes":13172,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":15268,"get_count":4,"put_bytes":13246,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":15342,"get_count":4,"put_bytes":13320,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":15416,"get_count":4,"put_bytes":13394,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":15490,"get_count":4,"put_bytes":13469,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":15565,"get_count":4,"put_bytes":13544,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":15640,"get_count":4,"put_bytes":13619,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":15715,"get_count":4,"put_bytes":13693,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":15789,"get_count":4,"put_bytes":13768,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":15864,"get_count":4,"put_bytes":13842,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":15938,"get_count":4,"put_bytes":13917,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":16013,"get_count":4,"put_bytes":13992,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":16088,"get_count":4,"put_bytes":14066,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":16162,"get_count":4,"put_bytes":14141,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":16237,"get_count":4,"put_bytes":14215,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":16645,"get_count":5,"put_bytes":14624,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2187,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":181,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2189,"get_count":3,"put_bytes":262,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2270,"get_count":3,"put_bytes":346,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":420,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2428,"get_count":3,"put_bytes":494,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2502,"get_count":3,"put_bytes":568,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2576,"get_count":3,"put_bytes":642,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2650,"get_count":3,"put_bytes":716,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2724,"get_count":3,"put_bytes":790,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2798,"get_count":3,"put_bytes":864,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2872,"get_count":3,"put_bytes":938,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2946,"get_count":3,"put_bytes":1012,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":3020,"get_count":3,"put_bytes":1086,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":3094,"get_count":3,"put_bytes":1160,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":3168,"get_count":3,"put_bytes":1234,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":3242,"get_count":3,"put_bytes":1309,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":3317,"get_count":3,"put_bytes":1384,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":3392,"get_count":3,"put_bytes":1459,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":3467,"get_count":3,"put_bytes":1534,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":3542,"get_count":3,"put_bytes":1608,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":3616,"get_count":3,"put_bytes":1683,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":3691,"get_count":3,"put_bytes":1758,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":3766,"get_count":3,"put_bytes":1832,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1908,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":3916,"get_count":3,"put_bytes":1983,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":3991,"get_count":3,"put_bytes":2058,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":4066,"get_count":3,"put_bytes":2133,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":4141,"get_count":3,"put_bytes":2208,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":4216,"get_count":3,"put_bytes":2283,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":4291,"get_count":3,"put_bytes":2358,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":4366,"get_count":3,"put_bytes":2433,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":4441,"get_count":3,"put_bytes":2507,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":4515,"get_count":3,"put_bytes":2582,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":4590,"get_count":3,"put_bytes":2657,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":4665,"get_count":3,"put_bytes":2732,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":4740,"get_count":3,"put_bytes":2806,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":4814,"get_count":3,"put_bytes":2881,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":4889,"get_count":3,"put_bytes":2956,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":4964,"get_count":3,"put_bytes":3031,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":5039,"get_count":3,"put_bytes":3106,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":5114,"get_count":3,"put_bytes":3181,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":5189,"get_count":3,"put_bytes":3256,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":5264,"get_count":3,"put_bytes":3331,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":5339,"get_count":3,"put_bytes":3405,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":5413,"get_count":3,"put_bytes":3480,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":5488,"get_count":3,"put_bytes":3555,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":5563,"get_count":3,"put_bytes":3630,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":5638,"get_count":3,"put_bytes":3705,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":5713,"get_count":3,"put_bytes":3780,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":5788,"get_count":3,"put_bytes":3855,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":5863,"get_count":3,"put_bytes":3930,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":5938,"get_count":3,"put_bytes":4005,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":6013,"get_count":3,"put_bytes":4080,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":6088,"get_count":3,"put_bytes":4155,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":6163,"get_count":3,"put_bytes":4230,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":6238,"get_count":3,"put_bytes":4305,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":6313,"get_count":3,"put_bytes":4380,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":6388,"get_count":3,"put_bytes":4455,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":6463,"get_count":3,"put_bytes":4530,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":6538,"get_count":3,"put_bytes":4604,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":6612,"get_count":3,"put_bytes":4678,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":6686,"get_count":3,"put_bytes":4753,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":6761,"get_count":3,"put_bytes":4828,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":6836,"get_count":3,"put_bytes":4903,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":6911,"get_count":3,"put_bytes":4978,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":6986,"get_count":3,"put_bytes":5053,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":7061,"get_count":3,"put_bytes":5127,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":7135,"get_count":3,"put_bytes":5202,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":7210,"get_count":3,"put_bytes":5277,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":7285,"get_count":3,"put_bytes":5352,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":7360,"get_count":3,"put_bytes":5427,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":7435,"get_count":3,"put_bytes":5502,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":7510,"get_count":3,"put_bytes":5577,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":7585,"get_count":3,"put_bytes":5651,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":7659,"get_count":3,"put_bytes":5725,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":7733,"get_count":3,"put_bytes":5800,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":7808,"get_count":3,"put_bytes":5874,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":7882,"get_count":3,"put_bytes":5948,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":7956,"get_count":3,"put_bytes":6023,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":8031,"get_count":3,"put_bytes":6098,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":8106,"get_count":3,"put_bytes":6172,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":8180,"get_count":3,"put_bytes":6247,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":8255,"get_count":3,"put_bytes":6322,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":8330,"get_count":3,"put_bytes":6397,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":8405,"get_count":3,"put_bytes":6471,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":8479,"get_count":3,"put_bytes":6545,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":8553,"get_count":3,"put_bytes":6619,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":8627,"get_count":3,"put_bytes":6693,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":8701,"get_count":3,"put_bytes":6768,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":8776,"get_count":3,"put_bytes":6843,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":8851,"get_count":3,"put_bytes":6917,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":8925,"get_count":3,"put_bytes":6991,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":8999,"get_count":3,"put_bytes":7065,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":9073,"get_count":3,"put_bytes":7140,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":9148,"get_count":3,"put_bytes":7215,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":9223,"get_count":3,"put_bytes":7289,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":9297,"get_count":3,"put_bytes":7363,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":9371,"get_count":3,"put_bytes":7438,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":9446,"get_count":3,"put_bytes":7512,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":9520,"get_count":3,"put_bytes":88,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":9520,"get_count":3,"put_bytes":7586,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":9594,"get_count":3,"put_bytes":7659,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":9667,"get_count":3,"put_bytes":7733,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":9741,"get_count":3,"put_bytes":7807,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":9815,"get_count":3,"put_bytes":7880,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":9888,"get_count":3,"put_bytes":7953,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":9961,"get_count":3,"put_bytes":8027,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":10035,"get_count":3,"put_bytes":8101,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":10109,"get_count":3,"put_bytes":8175,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":10183,"get_count":3,"put_bytes":8248,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":10256,"get_count":3,"put_bytes":8321,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":10329,"get_count":3,"put_bytes":8395,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":10403,"get_count":3,"put_bytes":8469,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":10477,"get_count":3,"put_bytes":8542,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":10550,"get_count":3,"put_bytes":8615,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":10623,"get_count":3,"put_bytes":8690,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":10698,"get_count":3,"put_bytes":8765,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":10773,"get_count":3,"put_bytes":8840,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":10848,"get_count":3,"put_bytes":8915,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":10923,"get_count":3,"put_bytes":8990,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":10998,"get_count":3,"put_bytes":9065,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":11073,"get_count":3,"put_bytes":9139,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":11147,"get_count":3,"put_bytes":9214,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":11222,"get_count":3,"put_bytes":9288,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":11296,"get_count":3,"put_bytes":9363,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":11371,"get_count":3,"put_bytes":9438,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":11446,"get_count":3,"put_bytes":9512,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":11520,"get_count":3,"put_bytes":9587,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":11595,"get_count":3,"put_bytes":9661,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":11669,"get_count":3,"put_bytes":9736,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":11744,"get_count":3,"put_bytes":9810,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":11818,"get_count":3,"put_bytes":9884,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":11892,"get_count":3,"put_bytes":9958,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":11966,"get_count":3,"put_bytes":10032,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":12040,"get_count":3,"put_bytes":10106,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":12114,"get_count":3,"put_bytes":10180,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":12188,"get_count":3,"put_bytes":10255,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":12263,"get_count":3,"put_bytes":10330,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":12338,"get_count":3,"put_bytes":10404,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":12412,"get_count":3,"put_bytes":10478,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":12486,"get_count":3,"put_bytes":10553,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":12561,"get_count":3,"put_bytes":10628,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":12636,"get_count":3,"put_bytes":10703,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":12711,"get_count":3,"put_bytes":10778,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":12786,"get_count":3,"put_bytes":10853,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":12861,"get_count":3,"put_bytes":10927,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":12935,"get_count":3,"put_bytes":11001,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":13009,"get_count":3,"put_bytes":11075,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":13083,"get_count":3,"put_bytes":11150,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":13158,"get_count":3,"put_bytes":11224,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":13232,"get_count":3,"put_bytes":11298,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":13306,"get_count":3,"put_bytes":11372,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":13380,"get_count":3,"put_bytes":11447,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":13455,"get_count":3,"put_bytes":11522,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":13530,"get_count":3,"put_bytes":11597,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":13605,"get_count":3,"put_bytes":11672,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":13680,"get_count":3,"put_bytes":11746,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":13754,"get_count":3,"put_bytes":11821,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":13829,"get_count":3,"put_bytes":11896,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":13904,"get_count":3,"put_bytes":11970,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":13978,"get_count":3,"put_bytes":12044,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":14052,"get_count":3,"put_bytes":12119,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":14127,"get_count":3,"put_bytes":12193,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":14201,"get_count":3,"put_bytes":12267,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":14275,"get_count":3,"put_bytes":12342,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":14350,"get_count":3,"put_bytes":12417,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":14425,"get_count":3,"put_bytes":12491,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":14499,"get_count":3,"put_bytes":12565,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":14573,"get_count":3,"put_bytes":12640,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":14648,"get_count":3,"put_bytes":12790,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":14468,"get_count":3,"put_bytes":12534,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":14542,"get_count":3,"put_bytes":12609,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":14617,"get_count":3,"put_bytes":12683,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":14691,"get_count":3,"put_bytes":12757,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":14765,"get_count":3,"put_bytes":12831,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":14839,"get_count":3,"put_bytes":12986,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":14660,"get_count":3,"put_bytes":12727,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":14735,"get_count":3,"put_bytes":12801,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":14809,"get_count":3,"put_bytes":12875,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":14883,"get_count":3,"put_bytes":12950,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":15288,"get_count":4,"put_bytes":13355,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":14958,"get_count":3,"put_bytes":13023,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":15031,"get_count":3,"put_bytes":13098,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":15106,"get_count":3,"put_bytes":13172,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":15180,"get_count":3,"put_bytes":13246,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":15254,"get_count":3,"put_bytes":13320,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":15328,"get_count":3,"put_bytes":13394,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":15402,"get_count":3,"put_bytes":13469,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":15477,"get_count":3,"put_bytes":13544,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":15552,"get_count":3,"put_bytes":13619,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":15627,"get_count":3,"put_bytes":13693,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":15701,"get_count":3,"put_bytes":13768,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":15776,"get_count":3,"put_bytes":13842,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":15850,"get_count":3,"put_bytes":13917,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":15925,"get_count":3,"put_bytes":13992,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":16000,"get_count":3,"put_bytes":14066,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":16074,"get_count":3,"put_bytes":14141,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":16149,"get_count":3,"put_bytes":14215,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":16557,"get_count":4,"put_bytes":14624,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png index e46f9d98662c49b72e96b256b4a1e179888f5b32..03dd6c5c2e376bdeac7f50e46940aa6a84050718 100644 GIT binary patch literal 16168 zcmb802Urtd)94|9H0e^LiimU&qzO_)I?@S5YN%4ANs|srl`8d*NRdtm66u`?3R0yA zNR5c}-kTwJ6aC)zd!PH==iZwKahW}5&e=J$XJ_X(iPh6lqab4@!^6X)&`?)4z{4X% z;NcP6ARz!Ggz_$T@bK{E^&T0iU@#a!lAoWC{SS$U7luLNVeq!Mk&1XgnfdPByV}~?4h{~Xp`lq>Sv55^ zNF;J#U|?x!2^eBmOe}djx$^t+E_n9`2D3f)0#pC7YI_?qi3f~^X9xTay%~2?IL-w3 zcii>s9>7%a@CKuYHt?`h#NZ8$V6LTNLiKjT!Z0d$=V8y*G6Hs&wqIAZ&n4?Af?Y(yuHl7UyB4Mh{Dg%;kzr6w7_35;2uF)I@yP@1FQXmXql)O4M2Nj^}|M1aco`MGzq*Sqt5r5ur^A?CO zDw$cAygx2iQQGD8NFeg*U`E#!54Gv{K|iCBV%5PEg4}7M7LGnn-!vDeqPH%$L4B_0 znLb}XbStP;tZ|kT<8AJ7B5Ffg2{X23^(&u+O9U$?I(^$w_t6E9#TyVZc-%FRDv+Hs zRPyzQrXON_n?_yvWT#eh$K=4T>SyX*Pf}F7Ev{DW=lSi4J^ds?W4YqDdFiGcRK}L{ zD;P?l`|L@=?T~wz2Y$`Ggiqp-+Uir^RcojDtvOs>TBshK!acJ@-NYAt{q`q{qvBhb zESKa_jbEu)x-#zs;Uh$3or2XR~BvTXB|6zC+9v~-gmnSKZPwEi0H;tdFASoy0RMk{mgHv^rpqGw-218^T`sGYM9}3D=?;NF zkYmD#6{ugX-{;$OZzg&8cuqhRzBt~b6a`$E?%Xmyi5&HTS&|_DBKb%urs~ZJqLWUZ z(D*9fg&ZO%sq%t<@j>@?2Ka>zg9rH-a$n;YacxEq;{1pI^#W$gBj2kV!O*PBMO`_# zwAc>&$ph%hsj z_pIiv5yQ=t+OjNmps>3^l!AWUrPo)l=tYUh34S&L{dYglx7%yycN?efC|-P-S<8^{}q*;-(F?`B#$CFm#`ZX1;7rW5Tf9c);O4)H}B6nS3!J~wJ9+l(% zuz3&B+1X|)_!MX;+r&-VzP3|wbFhnKuFB2n{PR+-q>h~C4_I^UO~k|o@!Z^2U-tLZ z^#Jfe!dN2p>F18qQeO9@zI2B0x_P@{3rRwT^^>odD)F7fJ|S;aXeFat(I^TNYp7(7 z>clI4=qy#k=V*8ktvjxohL=T zUr=pSZ}#B5yTrMH5NlWWP^*Zk?%h`elsD1B0{0%Z9}xIvRY#|9--#{^m@f8V_8)eY zOVjK2erMmEod^k)lAb!(e!>7bM?24M2f6&y>fZ^Q>k0;&Dnu0Ja>EPp^>e$+;QsB_zLT%bye#g@*Pr~NV!Xj| zDO9A_n}kRN<|z`GS;xA3Q?^aTkJUSGPI}VHf6L8cEqCi&PZ*Ia5*%9^vIh=^=6L8d zs&FfG%KUq*pMg&DCG&Z-&DxoA+b^t7Fv5sO3R1}C%Sq`Fw&<2~B+Sm&;Xk^#x2NQR zpz)VX5Ko&_av>?VB?5s&w;2bq2)4kd524@>n@0c9zTZZD3M>Z~ZjP6&AMV;e5bCS| zZ)yauTBFfFV|I?@7~JrDznCbN=w^JC6Fk|@XXy$Mugn|y0okZ_J4& z`bJWmV62p|Lo0l>IoglZBB|FeYkET4ethEeeMWA()@rpYu#Ag-q~RA|9xIlli)1cG zRLPSHj(qJ3_++TgW&cm~l6?7-14MUk_DN1O(X3h%(cw|G(!GQ-%^?zUH}`x{F+(!C z-Qd7b6+HTpzrHP;|C*_yX)P-7QL1%Cs0Nlru1d0^qEMUtA|{Dg&qdoBeo+YNrqknR&F!w_o;_`k(2t z&}>os{&i;Db8VQN6Y&$QRNqnHXl)y$a%@ZJeEiJCm&`HAL#1I@v+=R@ZOa-t6qA+b z=xY!-pjUv*F;gB_nn;`ZOA=fMC;)>%$)rg?7>OtMmv7Iyee36VRHmUH==E}tEE|X zS$GEQV04MJyr{`CGT!{bsJg~@z3!^{&!6;v_@OSqTx5g!04o3)(ybXA1cL9J$OC{yowS4Lk?>VO#p8-YyTgre|3zwSCLli0ID|r&EWs zGX9cE;NnkO3gfm1xWi7$M=*AxG{&t2Ety&~i9bS{XDVPt?CC;}4m;f;>;zjK-P5+* z!8UBN)tC6V_62CU_QMZDny`5-dC4rPdW>qsDgLR6*bop*DOwv z5H59i5pc>SrtqNFlhj?+u*S4a zeav8CqrC2iTaJ)}%jti}L$)!M?_ScHIc*wF+K$g{SJh|ET&@| z-COmim4xjJ!j5CY$0ZZIKP)vH0FU?hsxP6*yc`;J3`DhR91siBCL~#==ymI5mPLUf z=|^dCZsYE}`Ep9orjRsp_;BjFJl6ZMedzSDaB^ zY;70J1KduC&EO6_-5TfNV9&=K<{TR7gpoDTRDv7g@3WUO>pf0(|8c)sCB35P$zQ;K zoy_s>OwOK!L!)ia%h>eFW>V}pvJ&Uu1||HheC0)qYf*BDmLE%iKd>Y6bV0}2 z^cAzOPemaOl(M^Bo!_}~4U7UCBF_VZIbfp#oPrwC?pHjW1h`5Y-5e~iFzk3$mo!IJ z9=@aL^Z_B-e)ELWl31UJ%rOrL&)2OZwj|^493}HpOs@v<+u5z4_PInJjzF;GPv+G9+ zF_7_6HVrq);DsOkxJEtG#*i$k8Ksj>Z%V4*81KE!28oB;mQI44BUhu%;mOm^52OUj zTdpu@MBZ8^vOBj-r{6{`zo++2mX+kukF;YTQ@>c7+D_Z0dE(BM9!SK1=D$^ae{nk6 zaDX>dQXN|ClZv!Kko7$0&-WNHyH9?&!yqYk!D%r~0g5Z?^voqMJEBtqlL&F8Sr5y7 z&?07-<7eGkO4B>aTt2mA$XqV+8$sTLCHJV1mq8exx)+e0nIpnp`4%5L*s)Dnl!rNc zBplx^_egQMrYE=V4xHzKb%d=Ocm%c6OG$iSh4V3&1Zp*)|W(uu&z)5?|B|s+F4Ceh>0vgq<{aB|{aqoez}d8QYg-{0mZMicnEVWPqz+eq0{;G4s1w(1~kjU``Gz z0X0d zP^yV){Q@U8vQSQvFtc_MnbKs=$^YuB`L( zmcR|4E#!Igqj$ho7pP|%Gz~-&!|z6`I)yd`^5aB)ocLrZ0Wre^;?T)WDwe92N6{bo zXM5X0VkYc0Rv&N-1Q1HU1?nJXNj26R+&abjg9GYAmf}R0tGX z0{ar3GxzYkrKJTo)(GT{ZB2xWNr@WOa!huvmr69o z70w(l0{5FXapxDAAFdSeBt!nHE?&NG*_?vd8*cd^Xk?B~O@kk5vytE8SUa+Q9=jx_ zCHOVTwW}#dZgZ5ifb97PpSmc$2tE;V;WSs%kv=-t3SmLeUMS-|d}XH)hHZ6`KlAE0bN@AV#EcA4H>*jgV4iJqSBnxRY?m-oY=S zP9v$I$4}jx!AKdiK*~NUX&&><-?uHNv&%psAI^+=n1hdEArs9fBr|?v%#vBPQu;){ z*MAl+%R=~OUldlS^SO8OUh2WU6&zUY4WUyZtNc!=LUGM?)!J=YSCNp4fWslmM?u4w z`1)(f331|O0w~>aZ3&m41EwsuDnf?cR1H(PdivxP&yBtv+Ge^3Tyw^K;0_?X&RKa6 zcGtpBn0#aIUm_b$^wyTIJiv_1ku{=y|5YI&Lo#ZnjO>_bqBsz;-92$Bq5;K^z8J1z zcIC_W{YT`@CT$C-j&AK(6N)dwKwL5Z-9XXw@FgTc-REVf9X}xIOmH!6!wCaZH zGpC`iB!b9`cV-eFD#L2MO=v?b<(afd1ZU^#%gE{(lIs|A?*`lekvG)K!9dtY0?Wx^ z|Ce)O>@!4a`?j{h5%$sHKC97LMrKJ?_WEl>%xkeV6N2!_K=o-L+G0#jnO^N}YhmV0 z4gDO*h4AoHtaV3 z$p!yHTKomsIm?{8!0qmilJ%aP;qmY^j$j-=L@i3^RqUx~;Nwdmz5^OwX z{_z9X28ZCaWXh9wlWreq2A*{yIz0G8M$_1J8FG6_wXsk|<1v}| zRqEz63wJpW5RVUsRC06~JYrP=^Tz>-G{dyK^wQV=D`w|`IU?36aI4Exe*bUyUR}}t zHz?;S6!;sIbEQ`NJz8`;N`i;tqJ>3i6P>?-ysf$XUoR=2%eRui1tzZtiUB9Y1wOAo zZKc5e1f-=bz7@A|uJhD)=Wmq%RO#>gLWoL-^9x*TD)I%!h;y|GYos$SKuU0rfl=W) z1}PZ@<6atUN7$ZJy8n59AksJ@$6W}W@^}@l+4IAS`ghoXYA5j{%mORgk>x7)EyRgp z-M}|-LDL*HSOXUh`6(H3 zQ^y&Snp&EXc}7(uHsJkGw{Fg=M2j164~{{=^cN|sCQCr@#Hd5ulAG}->d9}biu?#0 z;LV?d4{NHA`&35+H+BpHYc94G%yw&>6ng>)4Mq;-%HNwvpqO$W1zO?H!N&9yTON-b zy=PCjfp7~elerEoy`Sdk?#j!zw3aOTU_3HMg@r3~El*7)R@rmB%D%sN4MdpI7jblf1!1&5M&G);vjPia?bhO`3|_+- z$%Y0jfm^lyuIw;+oCUR%48YOYbrB}g2N!38oQq;ys9Le_UhspFD%f{y)YKJ!6B9mM zLUYU5fN%mEilPkhA1m0a_wJ57-MhD`2F-~mYL1Eoy|L8zStb2IhS1p8yW;(?y0Cct zXV18M3&Ho2md{Dx@iDLsf1!Nin6>m<3053Lz0{B_YVf4E$PKO@9q>V#<@Y3{3^Uh8 zX1$(bz$&I*=f4NqU{W5}zE7_Q>2Fw5DDAB{B!Pch zHJR)4!mkVYSw{AbJKa-91`&4?kz$QlPsHA*p%OZbkP?L_zg#=hNZ_}C># zZ4MGvx3>nN^uA4j_{G+d9a7UCwzX%f-YY<$hM37A=gq12YH~YK0k7|v>V7` zXKTV}H%9x2j3H*zBzFOa3TQaK4NLwuN484xo?{6l0Bkx*%Pd`9JKyG&=~G$^@!3Al zJjNV{{R+yfrRs7DF2`rZCmVzvEwV4cTZi`reuLuy%d`N*Rx)bOt>s}E9#T}m5Su4I-pg!A736W1$n zCR@G9?+8Qyye33M-hA8*QZK>0mc2yYD#i6u8j<{IrivzD%fPWpC6zvbd>4W8wpf0n z(jrjvO9SA^u9U9Hm)|1Y)^`1|+Mtd|e)hc&Z5sUFl8}8HMSyw_3PNMcshR8$`u7pY zJ^`!1yO2oon~43svH4r{!mofe>PcAbO7hsa2TkvAD&DS6 z>4Mv4Mst)LJrU6?!H7*xeu+M^;1gnyyb`C-8BAa-aals&(yvKblW5@}Gfc9F0MtI(A7hwpr+Y0(&{V zPY-sHw&i0EMQ?&B^Y}P41awF-vBR5~OLX9U*VfB_wfXtD8Xxyw1rfXhl0lX0SNOx{H-wiCk)I5r~ z$f-&<{3$GJP%>w~p>`8I8vI6b&(DqV$|FD7pDT;UPNQ31$8ovMDG0mz=>d5l%3t}% zqvN3N+3}9pd;FBc8TW{wTR>OTW*y&&Q*pKFZS)YDWz;SBMVbPKt@h76-7z$C`Ddbx z`hPG5JnwG_W>-qqBuymkdOd1Hod_&Gl>oHt&op#a@u!^2a!96Zj_rKXey(xx^5o$4 z*w!e9lKb-PG`OCBM1G}XHCBK$Zn6Qv}onT$9O$zLzUJ&GuXTX$4~Ld zbmIXuuopk=TD52nHs!1v*YI0Az56b$#YicF6Ykw|wN27Gb?e}a5gn%4Y0e!!eoKe3 zrxMrG`<4&Xf9~<4KBPf8(1|FYc6*enxxe-UY&O5~7GmAFhi{?Z^q$6X*74zwcg4ZC z6u1u`^>x+Co2G2<(^NH7w12c%h4X&2A(lt%EB*$kt7D&L}iGT&J9EkB=9*WPCs z=cDo}t1gPZKF^}nphbrv03QZQU2V==@Tto}pZf)lZTYnu4_PiU-{|#q%IWs0p$%D) zHv#u`U6u8e`;M3cXZDVJZ&LPq4Ju|eo=;lojI6-yPo1n zwT#zl*F?r3rUFl@JSmckQ0-LkqRf$^j!mw!;(H$+rg>+onZFrB513^;=aF};n!mwT z5&XY@%yk-f&dD^}-5zmV``WU1qLo>>R&R3b@=~ssPVEuhd|eAc$mIv57N79c!E(J_}p z%8V)m*zmd~{Y~9oPuqR&^SfV){Ybm6&tD+dDf8EcM7M4srsWalI|+13KO}QO{n|IE zZLSI))Y80E>p_Yw^kONoRqiey!Y=5p2MIh`Vc$o2t~tb1_RN_R(R}yooscU(t^kBD zQVEr?=f5Cp+|{i_)qh@l-BM!5I+7>oD!g(0r01{&xv#t0{^fc6hZg(v{8XaZE^$N< z?HPXMk|zLI@}cu!7QA=~k~cSkAmuUZ8_UNsT^ zcfVKc{TU37Z)xswU6o#W(vJUwM5N;G&m4eI0|`G18>e?&uNn_1@YEb5?q5*zi_V&S zwldui-aMHTJQ)chK;pw8&84?8B9X5$BO>T|>mc9pJR*FbY26B1Bmw({Me&z@o~zohJ8Eb*Ose{E&FvJswu2pyP)KOhBKjpssJDBi4 zwKgq%tu{B=g^C?*%uaX&>Yt;0PyXSOA^iY3x|6x+%VXZPIrzKZm+Ay8s&rjy^)mS9 zYRCJwR377m4yVt5g9(4igGR&7%X+SAv`5v>V`%xwgI|`C1M}ijJ9*->CiX|g_b@?$ zr8;ou&@s(fd+ul;m=U3cY^@;48l1_#>Q!nH9mJfRN{+E`e~x)2RR#Y!#QVD>z3opB z0`Te`_`3CxRM$88gytSJOJ=9G_j>M(%G7g#K56a_D!9zNVA(GU&s7{XAWZb$gwl-`ErX(SK0qol`&3U@1r7#uKf2xh?$8-0|$GS2%rigX`VvUE5J^7*6w&P9J>Y&&vGtZcOkW04NbIShjjnU%#;yrF&TDHe5yCT3 zQ`slMKxIf$DyaV}oY}!h8>Dr3fQTw(;p@6Dc%mVf9W605-@0{Q@P|B^aDG)LeV>!^ zxTh-QSs=lZKz-QeN%5OJF<2s{aT9=~^S^;c3()hnZ?2l<)R=bdXu7)RlU*mrBHE67 z!Twn&8VHi|9)Zi}buoeXjls9Atpm5Qz;^<&%3LRA8M~nMn_OrCf*!d_#9i1YY2RyM@`f|sH2hg^Vh74$F zj%VxSNwhvQO6doUm3|N0O1C^Hcw3!L!d^9m+h z{^E4}*Mu=H^#Sk(vpoPy^NI?}iWFa%^<1OOmJ~JDsV%C=q zJVjRPEH*z5IU{MfJOFYapYIWqrLB|}tpj8!0l2914MdhS8T=p+NDB=+EKY|fHF^+c zF5_7}T{B6*<#uMr-Cu$M=mTJ^#zM5lcpzVXV_Gwvj{iF3OAP>bx4BgsoPRcjrF~PS zV*J8Kw-fZr2*_=!0B`=eWM9tr7A_@AyAm{CQ-?>&O5cFJhy=YFiz0y&P(vDPZviR9 zR)FEaI<`;`%Z#Z_V`(6bWiL}^N+4IzC!VEY|mMd2gajI zbdAC7)st}f@@qIqT+npmJXy8rE8deoASazlNB|3RBtYcc7lG$1Yp-hx`Jdn8HbNKQ zd!K1Potb_G!4V$$EFt{xWd98Gm(c6Oop>W7`tDb^gRohK1cSC;2G-bgBbMVw(N7sd zW&S|@*Eu`Ll^RY5_MD5Dq63NW|_vNa4skopi|gkgSs$F!e!+`DqcA7C7pjY zngrGnYy9s{b1~mqc)qa!NlENL7Vjl)9?9bTINQSgzqqqx({jwW9o)kGULNH#=)g_n z!$f6(jtfo=LpMLd!;EgeNN2)aALDK`iPl6Ieh127^KIz9Z!DX)e z!OrkC-F?B#Wh7U5B8~=IG0(vYaTZ}zFpznl(gqh<+BQda1 zoapmSm@_V`n;CtN{NGu>mEtgFKD(OVkBStb=`YG)qc}jyaBITzZ|ctw|8d8gs+1Mh zJ|Sudj|~DALQW*!)=N&yh0swlu;{|(@rxb@-8Q&^sBSTDShxD^g%k)u`BA181^{V3 zlD1v4TS`!S@20YK^Z`b8nZ9F3HhQlNAfvwr6+Ewk1X5j8!8@o9tGo7ST)!@Tp=-&$ ze&4%9N#KPRp>}91k1x0rX#KO9P8(s}vuB7nl(m5exXo1uL$ zm1dkSQ0Aa>Zzw>tI*|wPHF-p*vKiMvG~k#ktd5cD{DQcq^fG!DPu9D-Phb5ee$4p? zXj4PVN2)s2fKG2|Z+AiICxDjU?*Mt;XRVO{t7ZWFO`-CmMbH{U#w9|u^=EvSAmbeW z@2J1-c6Q$?Ew*rn8vOkQXc|tj;+vu66{2EfEkMRw6^PAPeP5d=N<&%9WEUyX`U&ON z%zgkeTerM=Kr&-((?BYWw#5WLXXiFH^O)$F3gYjkn9c?WvzwlxN|QH`x#}DdwooK$ zb_5c6Ult39jNs(;K+-En7^0}sVi0(@I&t&8k)lMSYGA67bgu+i8T(NKaGq}5O3|mh zscp?4yOhTT*IpP>!(wNaUBERj*XFr4|U(HqcP*4GWHhNWAMzU0%M zVVcUJU)Rc|9j#J|1fZ|x_;aVEk~^v-YeQs5C#CPff8lY?gvZv%ad@5NmR(IM0RX_~ zBpH$)-IFReH*IDqhDD$@ox)Wb$V>oyEgT~J-6dkw0$RsECk#AU3&4|?CLElW)oCoD7(v+<$%XFI*{<;EvV6IK2o=^Wwe4tX8` zcH#1d-76*XidQN;c*u!<3Fmnk7rk!wg;tuEAYOOtM*A^pwyf;|yX}1`C&1E;+1Tf7 zVubVO%;Dx!P>4$=pV30A=%2LVqEBYvNM)zoAbA-L8j=2#hrqOdART3A-uCDnhxbl_ zoEES+jWv()CB9~!OZL+Gh>*2QCKG?Zcg1 z(yV>{7Eoo}VzQdW!%AlFq!1Zu)d^28cd}A{i>L=8#1nG07267;hltr{eQw=7qpF84 zRB=V1+qvZGx(9uX6#-_}$Ai}@$@P_L1tvP3(M%bd@}6mBAj{T)u6-|+1ditFf{Pzp z`4`Q6>Em}beFt~99`}k2zOy0{4B)>pmUr7!VZv=X(g!As;1b50q{8IDh#mdGlO*O}7&yW@jEb(si zh#@TEp8SKj={@lT9*cEsMq|0PPpsV(lgpbptKawipMV-VYz1e{ldypsg8JDz@a)lH9 z{^676_rC#dsz$I}I8ji&G@qvybgEJPW%EATXwIkxVI27y$DUC^&kusMpiN8tF9kZZ zR&@vyyv_Jb&^HlbRPBQYQwDL1M=~b4YdTQ;>y<5nu3AEgFNaMmJ=2h&>eD5%>ELjm zHTH~_4|NzVS_o1!r0tunmQa0H(1=~;clxp%^#&6V`EI>MMm;ye+x!@9kM;|iY>@Jg zyqNYP+A67G`)_!Ac51r<-9Yixaqayr^O~SLxj-OSs_bIu#->|;-07uCr3_tgOA#&% zT<|AVxry-L>74{f^*(S!MO>+^0bNpoF6<|^rjv!IGXl}x#f7XGoE0P>0wSMGTch*= za{7b}?{9Amij%@y0r>o7;YrwCoKToE)QAXo^5 z22f)PeW$si25ci+Pl~Ijxic81CEfhHfIuGlPBKy%D__YJ6))-FiN0`br=fwA^*jcm zQ)tNCqhquEiS0eqk;E=7H61(EkOKQ$uy8ZKc*K?KC*(tSQHU^;SJh#T20 zN+l>~o0&2gK@F+w3uS6pn7Kfq7E!k6D@RuByc^*+DQ(WeJAkh{?VXgrEtFXe?=gT% zT)wbi<*GgD*d1e%vcxQfSF`i}ykRN7@B(&WWv%A3NdSBmgam!gS2UIfWMmlMU}*l` z>}S-)(Ozko3L7|s*de^Qgt48o$wQMVo}~)2ulShinkTjAV%3+zzP&K3wO>uS%=_z* zL&SPee%Q;^b0Z>l@G`Mw0OU=p%s=is9o1KGFk*#0f&+douvIqn94bkrb$ungFv75N zQsIxrw|>-|6+`afV(ia>uK|&6Sno?Ncq&nCY!)h>2?zNPvYY>Y3pgqkl;f-!q+}cn z@Fo?gHO(B%2PbMh<*Eo##K~Tiozr6-n<&X`ToUW76LX$d8p*LDpC@+XZa9$~4LBCy zQ#262E@TP9B88; z26i97saTr%(HNkaV}Pq5o7oHaxj+I$0%<{y+}_HeV$1^%`h)2QAYT%Zn(tg^x2h}u zaPu38_u)k{q~Ym`SmF?G^%rJpLcvLJ89Kl*H`IV~Qn~VNY;m(WsKUW+e2oN-Ha!A5 zCVYe$P(xaroHzhj&ZrNj1-VF#qgX2ejyZppjq+sSE-(YLlRc5b7K?JXcy+1G2)imD zB4CO}>93HQT{*PxbEGvn)8gU$*lVJKr)MtfQAFOX=mXfD3N$@N3cmtp-t;Iq47W#s zDGOjqSVI_-N)y2uaHf={T7ZP1pef)qYpWF zt2V(j!n7Fu9NTbIGOvvC4vLPNirFFDZu-5L0 z$#XyaRC(~~wa=z9bgNVukdeExSZ7EU0Hu`rkw7H^`p;`%fh_iKI}*4u^BLkV61T)g zU%++|TMNsxbBV}uzpC4!0rk8RV@nNj68~4N>D8|-51_BX(v;!p%3Oypfig)CallEJ zxYoK$Bi@2;Qh@}*LwXmYUTTS$btEso zf!K!$Bfjvb0TjBT0GNOtJ3y6z0!c%~G=M@^oMJ^{fJhiYrcKZcE(17p=BYqcoQlyq zX28pei5MP7!hjnVsC87P=T`t zl>^}_#tWu^Q#~VM0C4s#z^ND^J+}d4l4G&ABrq*=R-mhXxQfvN1JEvocmnX7M!+Gf zim);_Ko$hu(!~}J0xnrKhZPP1LSO8xS^y=}3=r=MJS=pdME=xPguVbS$$baB_s|Gm z1ZX!w1Qe@6X@D^@kw33epGF{MGBT9bdwV!QE-uDwdtCRSc|Mze81_9%!`Rzs#wHP< zax&t&!rpDf@nHo&T3f&?Go96et>ymZorI^P@U$Wq?eWCCm@T>3lD5(?@MTGQ^PkKz zd+0x=3MB9d_>TFS2A2Yn@tXx0KairH`;(%sVZ9xmo=h`=k;|n6{K3txZ30z$^AGjb z0#!)9bGNsJq7SyvB6}@ibpSPQ`lFT>Qs++!7yj@G)$z@$=s)|^71?bGbF^KDGtZX!%OuJM~&@OStQKjApa$ z*zL=qN+wm;zRR=5;5XhEZ_x#%08!2Yi1JrNqiQFb|DE>Y@f){1P3OX~?dma-Gxg?v zmQPw8C z$4=-_Qj3J*-^Z06fsZb8s8}?P*Zh9Y`{@2ZaOs&Zzb>B6)V4g$-oBB#Eq2AYGngHr z)kE*xY0-_;Z0tu=s20tv9EhCbr$;oMIp1wL%d_1_`(m6PHS7A(+NNw*YY8wy`26^c zm^?HabOmZ@w{4eBnlKM@Z<|XJf^)vBGPRZwmj!>1|8fX)qPY%P}E17p6L(ANn zdm_*rpx&g_KnJKuGN`vc6rPi>OrBmZYVrP^rPpV{&)G?B+-wnLnT_Z@KKmUPqR4Ii zJ_YGaTN-EEoT%Dq(LD0{TlkXC6Hz__t>XFO#gO}aerv>Y_R|oso1Qr>#HV60ZMdT; z^*6*Ym;HWiZPKsOh+GZ-eg3v)0Z)!ofcM}&r#KQ0>B$0U|LUS;i1 zenAh+y)Kzv=@$t&*X(@pX>2a)vjpw7$@yBU#>v!qY$5+q!_JEvOSg1Kf>&Zo2LLbW zEgg7u=cDY>*PaCHV)PmB+Wd7L339*7xS>xY_eU-DhD$mgb+LM^MT4Ww-z*Xctl5jN ziCn0MIO%LsbI&c&WG;Iv983UXc8bUVdENiv6V`Idk(=JDqEIz)1AAISMMwF)67CzDrLX$)UgwU%XEeZls73o!)5L%=cfe46F zl@gFHAVi835otosPJG|<{m*yq_uPAVKqphy%$_|nYt8zt#F!ZAFwk<-l97=y+}72+ zLqL`?zYP^u!O$jHuUni!aC5eNhzCqF-*_#c*xES!KPBam%wVsDat{781o zd4YhvL>7J%8%~gdhsTnU-3%uqgcHIDW5r@(K+~IFKLhQ!xw&O!Wep7t9UUFR!oo5# zGHPpUu~_WDz`*kIGSJ1gj7-vIQstMGZOHal0%3FZF`?mI_2wpFf(+=5>^|^sm|(1+ zWUK}0bL@o+p1@GZ$OfZ^*2#!NB#;da6V4|S!c4Zq!wFhsN8#S9sX^N-aczyrrl)j! zdo%grmk7mVn~yhNl8w&h&nB7NgdnelpC=1He?I&s@EaZu$A-fR;l%OZB;*6Xa6-N( z0gEL7&qKftHs)>tkwdR*&dYCI}ZV2|L;HYPuy>` zfBrrZzsiY2$~HjscqZKZc%HA`#nrBYt8jXan_07GJ$1%Cf)}!|-Yp>v5`}R-wh#QA z$6PoMqqa^r$i-ZH8vUwwB-?v3S0yvfk!|dNFcl$hDN}!(Gp(dSflr==mYB{bt=@Gh zx!^!8@zt@;%{-vV>E-56EYC&4rg!nQOlZw;Tgjs`s9WQY>CLP0e3@MH>}vdr4l`Ws zT0wTM@_!~v7xaJl7>6=e>}V~N4l~S4hJL;%DfWI?(38zduHCe9Py0}3`e>6PO?6$} zQCTh%?;o%|Ui_3KNp(KrQzFk&qh|=&AVt6NIh!s41fj$UKAE1r3U)mD(W&23?Z@w? za+k`aUaDuOFG5x;xinfRk6HXvJjgAe>mq;RQZ5So7WwB-Ki$)Ps$C6rwQMNU)8tL} znOGF=B=GR1X;)g;Io0;;x8$1wcTeb=7oxB(h6KwA2AdKduT~{iU$2|H-p|mFES@PB z=J6zZ4D;WrqDnQC-dhqte0fr$^zm0R*5w#W+afSR`kTD zu%sF%Gd~Tussmf4@*qc2Um-k#zh|-Dx6M^jpl^I@N?#LhoqiT-Q1DP4dBO|jSHAYT zv?t)?A>%q8>wWTJOB>deak#z5729Rh;{%gMpVyH1%Hb#^fqOX7@Wa@GChBX3ok(yy zFe`tkmX2G_q`7oDV7;s~8XYQXwKMRL&iA(i*yg@XVUpd}3HyKiEBB2ot60e!rIn%_ z>Rnn*hM7*XL%|tQ;Iqc%bs<9u-1Fd}PnpJJL1%4S=)a4iQrufJW;2u9Z?x*kxA!>z z=<(oGjc)uc#YERszccg>XH6Z#^|>xK*?`RR={|}*;Xp#P<;=(Je(HXyl>c$v-()PY z8&gu|7jd6N_$4o&4X+##tgh%zPZ@Qil_<#D#M9e7h<9n1S=?==R(i*?Rh*VmkZE0M2rUBPhw;4_*$RmiOhi<@&@8f&8r zBD|;#DBRzjyF4nUF5Yok)M_f0(@D$kTZmrnaehQ?FvbdNsjo5>hPo#wZ_W~!s3oW^ z^x;|zukU!rqEd3h<{U=HHPor#?!_&o-r`Wm-_<4+9~YB; z@@ZME9jxcyJ%WDoc(eOG*o2Xl&-`BWYuE;>63>6E(n zOrxY>Zx)rmC&^g9@k*f7qIWryh9DUd-rOseMVFb$@TYkF??MDMWc#?U9@?1 zv4OBpj}YlmR5rKWz&q=1kwM(1e*7%Z2?gx%8k$CJfbL{cqHv854jEU#?x4{$djXMwI5YNpKnF(w&Wz^<-h)P}Zi7qU@V2H=fW}{gcMG!txzQpo>Mvxnc%^)v3XM5z@!A5Pl z^Qzp+mLkF!Rom9O2(d6UZ`JhXhba#Dxg9+>mqS~*56nY?y%+i3EZ>pHZ zZ;!O#XY{j5SWMZp=%XSM)@H&e@Ee^WiIt6dbu7kOIO68MROhe;BPTixIp7 ze~$xo3LxLgFslMG=O7QSp&qxlBu8Y*=WZn&WJ;a%U0Le99RNyL9QU934XiC!n82aX zYWAZRPVJAk_x-audMXu~YUu;M1CwYC)t!Ue$SUNv@oh!2$W|^WIR!8#-EJtRG^&a* z6c_f8s>;aT6yeAsYWJzE6EpUVE?0^-cFn{nO?CN=qH5YY9;hwTePi1)COa@2bTiAzx()~w}!lC6Lh zbyZwA>-2fq5~LG@SV7@RKA0A1J^kCOld12VMzK+K41>6E;%@cRj*zA< zkvUCeN~n$Op>$kn?tUkr?hZQVC+$YBW&iorT8`@ueyl7zi@Ns5*0q~yDAWXRUk_S* zvQUW7GdKC;=XtBBEF4HeH8^G4TnXXe(k)>w5-P)lSZat-bj zh*^$BSnN()hlXuXB&fp|C?796JDak($tIXmS0uB*K5m{oyglye@*ZjE>kK{%Ewp`D z<-MTN8I4tkhVWM29|_z&ulnl_8^ZBmSHc8#SX*NTOH+pnB&W$|xK|C0(zP7=q^QleQG3LI?4DVpKsm=IxG{>abBx=roH*@<|p;<~A z7vf@rrnj0&#iM8~IAT^N{wT3rA@H;^B~#Z4Wv;BPPshx^(j@ZY*x0S*@X)Si*GYj4 z6=|V{AFhAmvMq^8=m6t^;?{}c<4KQeXQfBIz!?V2$Q1pL=q9}fKQP^YFdR>F8CaZn%z%B=XK%ANw+i;c zvi2IqSPKSHSJZJl-C}aLxUsS&)|xCoiW4<)@UG8fZXuo&CsthUO~II%^p5gh?u+!o zxN}udHuW@JBNVtI^11e%S)Km)!`8k{;XWa>s&8{E7-HG;_)@V%QX;lg@f=Zr5X%x; z)|Bn>u)+%|2U~JtJDszS^%#i>e6VJU1W~|H2A9QcO``hAep31(mMGv#V(E)`QX<9+ z!ca={?t8XidX)(asU2Jx;z!D&V1YmiK$L|(jPIJz_ef)L)P(Gj%HpEY`uW7N8n}(0 z0I3@zP292xaV$#AkTg<%!I56LNYe255s_^?`5-+hbWE6>)oRoG!nIw&(#^-fIZLuK zE0CQfka4-bN;iXH1?wb8fVv^?rzsuU3i%e9AJg$y6!9tvWLr_0mkZQ{>A6p~y)HZ8 zhzdj`;Ek9j&K5ZE4am#EOs|6*7hYNum4h!o@|OAR1+ZziEGAPjb3_--10m`Zv8E_6 z+=j}fAX*y3?Z8ht9+h4leg>$<3J?*FBSQ6W~UQILl8m34e7sdj^H=Dp& zARq7DWmA3N%Wvw%xAbt3`Ms?$CR*%%?HxAhg3dDW*Ktcl$1!LoU_c)?Y~fzWpO`{FxX26?8|s7^2P6Q&5qP3 zPKHqwx=a!^w8cLvV@^-*Zp}V-d0!v@L!cJ9e3nBeT>e9Qrj&NZ_h!A-*`|Z@yn8Lb z?{ctG#Flnu{KiFq(T47ibids23Rx;SIVt8(=74Fk4>dG`5=uwWz7!QwAAjmZ#Aajf z*-%~hHl|tJf}w3ZYjXp56y2Ie&|7D}m-bFBV>cvc+V59C)YUb=l{p&~F?HQEwXH14 zOfRaibK;!tH_y9p>8i&Dt*^l|zg>?%J_`G+T=x_8{-xhXprcrk)&?6X9J^m} zem#922DO}zSVw!@C}I2#%yQ|}W*|%@$KDA&X22KMFf{e6yH3XdJlZ~+-p2(Su)vCJ zRK_6QJe|(<-ICv5m#E-^sV3xC1T{*BStPUxm3j(foQk@;^T4K|QrXJM4NBp`wbEe| zb2&n2Pw5Qu^>(@gH|uv&XQA9T{hpM`@gUv8o_MiKUNM6=XXU<~Z+VP(gI%;?mvkc- z?CZ|^+E!GGbA<-#%73X5r(}1B?+<=@uKEqyBIc^U_Qbe_4bl{o#n-ny>K=5esa7d4 za)y}*v##x*q@sXUFACI-BnX|l~GOb0SA0_;{$0qBC5H^uY(`{B7-0W{V_V?F< zN6}EcUBBm)!hi*tjE#r^BN)^XSw`0S<+ti@{%yx(>h=p7?<{KN-v(Ci$r{hDq?c6N)ybt)b(lGE+~cFKed zpJr|k%{>%M-Cy8F9Q%_DiJ1wcxyV>I-`#S|*HZUAflp3FKAt-LUbxZr%OtiYxT*7$DdK8FFLfArKMek;Q1)>ogR%`^C9M$|layq%6)=;fhm(K5q z^%4u3E>!*~Vm*oK9ks+vXRAGyv(;rWaCh3!sf7)O-9PTc@FL+eB?qQA%`{~L9pj&UEt8muNb2)o{^MrDRAR%J3XT(gt=tfT&i~$SwL6nq&=v5(~FWrSeysat8e&lIrjqi zm~5E^mQ?nuX>GL_)H!&=o>(W4lA}mL96bjAZOOy2$Rp$O`;7MkUqZz9wnX%~J|(0% zU^Ed=zuE(`3tT_m(h+ir8>?P2mB);dncmU`u>u^sEBeOkc`i_=hhyM!8fgu%#?Sdv&6>+Y=b0b2n9)WT=@#gYB+A0_KmD0C6xqA=SY<{pOzjm5PR?u ziYvcC%5q29W0^?u({#*3qKS{uEY$YM|c=q$4Pr5{ac%;PC3w^zf7I`X@GYF6%mIV8hu?hOoI!NEp;b*Otd zh#692!Y1N{G}8M2V%Mohqg^V-BF4L*g*eX+Nu!%^_M1?MgC$%1$VsZ{IQUt&A_L0r za>5z$x>B0zxJ@~Wi3jQml;Z;FHFw`=8P*x5KRdZjpcY|)Ww4mz9#zIj=B=>cGQE|a zD3FT_6|Aw|brGD2P@<~;gysx%|uI>*8_clCC6^By6hhg#}&^quS{k$pb5k zd8qrP6jPc_r?Y?_0Yjqwd|J$Nu(A77@Y6jj=Z5UY#{dan~~tSsiyi+P2(a+B0QnN8BoIbFe2*&6PK zH}B(_&E=E3GjAGO;h7=wS#_D%h-~0th&C5+Km(1bZVQi5hI;w~#Yrn0nOGjYA272Y zHQ_Y=+Qhs#ENg`?F_8cyZ17T5>KCac-qDgwMs{+2jD}KFk$NPM#kdYkMv5SYQYqhu z%i@&_Wh&f~_zYCcu*VL>GR|9Yn$%Z7*}@&5y1y@@_{PEuAl1Ic`59KS`)LFfUJ3?sEW=uKvvs&_O*mFvip8>M9*zyCR>{iCDb$?+Cszg zhz~;8TNNAbP%H@8gc2Rb^(%El$5*AK`dYO8{`Vo$KQ!q_efdbv^w-cV8gRKbj@T6X z&Uv72@!Dqm{?Rj5=Obft_WViKz}!d95a>FIKR&Ag=jHUDd}&XVL!{Ha>yz_i20`=Q4E zFR?iu)s7Khr=#=8gv#mGnl5U+0E$VS=_dRPPyel$6D6`Iwm{2B!?bv`Q$8PrWDK`sf&C|{cqp_qATc`Mn)JGNS^bl@ z72U{-X3I?EQnx~Kd2IK)yT{6!w1g#$G@!D2+~K z%f-;zskWY@NW^FN`b5tow&1Fjw>Re2T-99PdI(lDW|WC$2OI_!tcDs6B$kJT-9Mzu zu9i>gUn}Ufd-MDGk&vW(Pe1p!ZmRk| zI|0IW#@#EOYuApmtWU}D$NL(fbzmUkzXOIquYqAqG#py#>#sPvxKm4H3{t&v*}X1D z3C$h9BR*&hbX)J!QE@pzwz49rUSne`+t7$JXl~M69_=-0wGQ++mMybdWR|<~wnjVB zPvPgDro^oBAovZZYeOE`;n}CrnLnKJ9$uHCQoy&?eaPAOyL(u-yV!l7$b8&ALF z^jtBWgJq^q7a>yfZq`Ck>%)GnL^%Kpp2CfcAWD+43&lr*FNe z`XmMeaVHgrpkDtS*PlZ#AkGZ|CArjiOwC@z@WM}zRE5TVJ2)tT_T`VwCpXqF@*;tE z_to$>KnaLT&&O@<(XE=fOxd_+-r%&u@$4#l>%8LT6g#;4;FDob5Ccucs%lBUPhfatsplA!xeG z_>o56K6!q-U5sOk&dM3GRzN=@`D3xq!dCscVLP&)uCGl?&vFi2FQi9N&BQmvuQBe& zjo4m#ShzHATjgurV|=4!!uJ{u{i7jdx~+IvgKepG5wFo6$J5oQyA{h2rN)B`D`Tqo z?FO&!g35>cb_K-p`$om;dDT(RM2^RMm?PUv+w~ddL%xnU*;2>jwRdMW8NaoB`An45 ziO^i2zfUpfJl@7Z3tI!l`E3*!V5i@ULzyBs7Z_km|Ccn zzG3(<@IX0`YDV+z5$BywCKN97w6=<;*+6d5KS22VV)V-LdB}d%`9N(oInaE8+GCqm zz4U}_0HV8}%iQD|H6}0rK_MPDCx403-JS!6AV=N#g!QhC7kIeA43lho_;BLjFpcvq zorH=FJuij$a$=}T;bxb7WlnJ8dD!f8C&jR{U(c4HaFOb2aWR1`=b%eHeboIQ>MqRJ z^CF5l-!fDn%J~x~^T@^9^N&HFLU^BA&s{!iiB&P65C@qmzp4*qEN9A1Gc#Xm4ONb+ z7r`7AajWjoOU!qO1dEg`ViI?Fp~1=bqN!%OTqBCPx28Nj8$we&sdEn@o%q(CPoM0h z+N*gy%X*rZA)QT=A-xTwvIG^jxg{%hW8Xu9N{97&n^|gk1U6(~2w_<{C29UkL+!Pl z;R+)D-Kdyb-e_S@K>Tj)3R`{dt+dUiopRLPgA%!~168aIT@LE6>v=k?5Au(>cQN6u zJX^drZIPd&SPWa``zgts9`eH$qCa_;@PyNM#9`=tX#U6b{|$ZwX|siWka7L;FK}kX z{0p3`;~NFuRMf0acJwolW!3jEBwA`nj{i=yiiwSm(&6exKo1@?pMlrnmM^@qrma}8 zlT$&CqEi^irdePgqEX8!U@>I_5tR?4dx^tYZp!mN?YxIh$ss0=@>k=skFdbm=Pnyx z)MASBUOG$#NC-Ks(0ZL)>)!JCIcQVn@MUtkkEILddhT5U3qpKM?n=TNbANF&)6mdW z2CJpK*^W&>9|~Z8CSYU;VV6C8{xuPBMA!@r>4t$dwXt$yqBIk5qtK@07|a;~bpIna zNl*#^W5wq$?tl?TDP}#9$(vcAgwy@`FtII&`fx|gzkwpcC~uBRd>N9XBAKc+`#xbstYjWc-+maSUoK#|u)i|e0c`M9lGPgz}D8E>5Z zdXE)0o8IjwaATm^jxzP+aOIUJx%*qrlFDOHAv)AgvtHZhjuqS74BqM@VdTek%%W2+ zqLx}>*M~W>G5)`?=NOub-y!e|m2q2kh$+vws%X8ozNGDPU%^2{Nv*)-7jV* zUiN~s04wSq`T7a9zTBK2|Lgv=*$E9su$Tc)Bf`IZe^BbBOj)x4hTHb5j(prkdx=5S zBrrDsS+M>3r4hBKJ8IdL*2yJC35_d~%o{mhvhdmCAwaDlxDOGp0~jw2tb(6QzKXPO zl|!-L(D)s(XVDpE3vUiZ7c(tY<9)*yd@cB4?Q0?>Mq9i$(?^klcSYj5@})6%dfw*x zGSRTBi#u9VS0HM_JwO&Wj_V$s9slzJpJ#mWAepfFYv@b_Kw^)4V2L}wl5J@!5D_@7 zG`dIG+A!7VYuk=ox1~GDB{zO$K0FJ}Gw%{DL9u%{)3i?g7lIC+*k-|F!s(RSK)Jxa zfh@TyC8VSRew*kkq0Md~0hb=M#cEttYSZJYaeK@86xfBu*>~BJvCm}7doDndv3E|C zD|VKR)~_>(2aiauAXK&@5qun;BK5fL>PGf8XH8s)Cd?L3rc+)H`XhDims4#V`cXK< z5>NJ_91W^)3FDVGG_t0#tXx1s{1{D0h=F2&meQR%ln9DeCGr{dxx|ev+!YiMPk8bSt*`kO`dr4$HgC_J82# zL$KH9fAMqfoI*IY{oNcpQk5bo49iqHCtE#K9b=!W%Lly_Vk>yK^*Z6taF|~Zo{Zd} zeESy(csm}Le5CF#-BEN>eyf5v-Mbm@SoriWU9PmcdtLeY=npPf{753^$@m8oTX%z; zIR)(MijKnTiEb8sVI&)<@R_CHl#;uD?keTMbDUy4+%~{*-|8*&zys%L4nw!e+aZ#TUI!=n%xe%9E7$N zRG>;t+}(|OZoUuP?@p^~lxV28PZxtBVACzu-Yb|z$`C{NnerP0!T=nKMmf}n1^esD zU+(C-c{|HV>~pXya<*i1Rzc;*?=$l& zX-~>@%1j+%$TW^xGtQ2CTKwWN=rTXRw@GyzUztlx)lR{Z|8CkW_)SHjX<_c;RX}PVBs*C6P+~MJVSjAEL|bGJ;rMd=7H+|wm+c9 zWA2Hq?qL^sIg;MZyTw4n(BqusToY^$6(5R?0&`?Y_&ByBhWH}23&f~>Fztps&D-zv zOkqCgng6h5Um?pU4p3Sz6%_up>l^HmSL8RZQ`~kV6MkHsBOb**VZeB=U6r)Ew<9}} z)h615r)0NRz&kd^mqO2jV!|Ac^o8f;C=AZVWwBYt6#Rxk&0_FqK1!_J8B;NN~uLIrUQJK#yZ>g z1H&J_>qx{)Ri9JkNG2c-CraYEt9clk@F=&ZS4Z~CAWj_!749o3c;#OtJ(ZPwCnY|+ zLGk|#VL{nc2madMi!BZ)O#*r-{VxK2?9{utF@(}oqt7Y97d_iDQ*fH-Hq{pnsMha4 zagzrLV@zTioqcyo^?*o%!c<-NJF=ueg-dhG0GI{_1)NH*N6;HrpBun$Nfo%P0mE^O z`R2$4qXA-DcFOEe1P=ikZOsg+1_WpT!7cU%G4fH%zFDCWUZLrbe@>QJ1$m|=v>(v7 zfWGkh0p*uLePRBKu<_se1M8C?Jwxo*Hiq6^h(-qhLWELv{q<&8dy^fQTW9x9-u?K? zC0WRsAsx*&h97MN0|E8+v}NJvJL0KPa2#zWT{ZR18!X@x^UVn!d5CZp#>Jykd3FQX zXEd^lTBZ{}`L$&-H*5anf_a52CVB2HJZ~Z^SkoUL2{Z9&{+4* zRH)UL`=MNoD>G-8&=n+?ESd#!j>L4)TILr?&f_xXCA0=ffq)S`K{UkGVRTU!?M+%X zhtcR7Qlgj3B8TLWeI8S)$V5~$LQJN9lEkH_!`cI3CAr&JF`c=>B;Q;!wOAmjzg=dC z3`w{X+(zUAv7H%*smQ#Z;RD;?YnW$Pv8YmXR}F}}xPR;HI5SL3stWzF0Q6SoDB=W5 zOTG^0rGPpSgEIm#{6<>vi_xX%5n^117X0=t_{GOwkoSOVQkg#y`{FUS@7a}g&h0yF zm4VE#d(5y5W^HCQ$lG<)II{#E{G4(!Lx)0$aVtWH;WtyjpA9JBl>KuSs($o z{vvFNi>KE~2-}ISX3eNHM(`f|Q2uev+AM&t-|yE-@>0w(flW2(C0+;r8EpfQ0uV_6 zMO|6n*CJ^MhWwUyV_BqNh5J<0=+;8evrCxYLg<@@=YK8(PQVkh{jMh3C#@6e0@=O< zz=x**uyh36o{q6b??~}0afq&JkM+@SyKYsa5ClXpsI=~qLY>J zj+^1kt;ie=lLZs@@$L|FqNkJ5!qRX?A50V#X>P_AV6FkU>@GWd82%EM58OX>IC%OC zD_CwaHJAB)ers>;Vj0vXZuUqhGnEI%OR>m%?9$2QAXvG(VHDE2Bvf&yIyTZ#KpMcn z+0J%`u)?Wt4N(zpY@GK!rsnOAq@7V(<_oTmfY>#~N_vgq75PbfgulU(-e00QgA zAGpPo83k3va9P}HXam1u$_O`SEAJ9IH^@R@6anbAI>T?^(G#g2#mh9%RgCj~_Wy4b z*=z9sks=%C&nq*P59@t+pmt)qH(+rywQ_9QG&*U5;Wv;x_HR@KagCPyA$|n{T@hDM1AIiW5Tcp zU@`wi&%Rxs-LART#hMv(4MrjQ%7`>9PXu@qHo*4HmTkGl<6u=U9W&rYv0lhdZ^#{6 z{7+2Z-F78nt$_lN6?o2}zbQmQQG!Q+OH~(icFgezH2`mI z>WnPD4L4lq_Mey=xm_}rq6sopojGsEGlF!BPe+Fk?c_;_m~ZCAQOeg#80VfIpZW81eEDQVP3#sG zy?-KRq?hOQ{-$oF6*wE)$3OMV2Z9~=zgYHp1eNkB<;m|&Qxqblqibego`?F>pjK?k zj%{v|$GH2as*RAVzA=)lY7-1t5BHd=Auvz##-k*>?1(~B;d0X&u$vA}$8gdV^oMZ` z@h)9zo$GGlmdvoG6t%7Y4zgQzM^-5^MKPXi9ik8q3q$(Y4i9U?!^fTfL9($urF#Cj z?p^J!tRY`fO!$@?2}Z}@Ggh*y>)Y+o9ts6~L^8G|WYSlcBWXO~dd^rVkLb$eDBn z{UQg5)M!ffJm!9PK#7V(JMaMfyNYIOOTnp*lAI25REy|4N+T(^v6a$)SZ8VPJ4<$w zzAEiP=!E|@rHaHkAvC1BRd1g*M4d>QZkCMvi&RIliKV75kjJ8$)|Deu%Kh;)isxQr z^AK~BCVHlmND&?+5beu&Uml20)HFPQexlB^$}S%qTsMz-*N;nE>PmUUg`30wCvnXe7@8x z;;;&M`O_GX=TTIFMSK+P`2eHzvK`<2?g8wauMs)#@C*jhmP&oVZs{9#Lq@7`*Ls#jVHwq{A{s}(v z5@x%+J-v~S{H`RXMH-sz7r)B_I~dGkDlA^X9<8=p@oaU!U!|7#Q=w+*SBRy}0``A$ z9}7%|83vP6IBoTV7w;Kz(`ubPI#bQTdB)=+Eb)dB?=o*HJtar3q*DEwP<2$qQ){1Z zmoWbq*j5rkkCV)t04?(qlIhgU-vikqtrodE>w@cl!{8ck-U$gOv1{dmH4TzA6e(SG zL`z&iKDRY-jHIk(W{480C1+=o7#-1!!m~!B<488s8Ve-Dlr7Tv;^{Zjy|fQaYs_Ps zmc&ajb^V?pOv0;KS_^OfoylxG5m-pekYGJG8}9+&TnB)+Ej4-x2$ZWgEYxs~{0pCa zjZ{9OWq`NVQb!AXz2YY+c!IQ6!>l(j-$QK_H}90LJ&y@JQ0U zJYk05{>CcaG>4Ev8+)I5;iyQ}Eu|bz)Je5+sL}nThW?k(lB8gTL)s-YT_G`8K^1`1 zb4bJMR_}GbQMuXhkG`A}p{K53VumyVhNHISM5*cnq=+Flx#Bo=dcEoOUxkPhzg} zf2wcwUv}=kjCUt8rGw(v~eYQ)6YWDG;)Kw;&G1>*bgR1T0Lk6z&MkqMnOJ5d=2Q!!aba_X`}`VY*~40 zY$h0K1SvK)U+@HAF^&=lcRa5Srva&9nvfS9SYVHDGfKRmb-IM1Oy&mE?hsHm?w5dp z!)+pch^4fEo@WJ!@NF6(aR*4`@WbNDFVUeyz67G)Yb*$`p+sQcvqv&+ncR{A5b_8x zxwjO7gg0?M{Q$Y*tbklGKrV+8{-cyYxyc2fl!Ag7>lcG%-AE#a2TB838$iof-oPL) z5Z|OE0H!7UhN{v5lUOnmfYcUn7W1Y{K)q^W^rIFqHh|m4m3uk^E%>Q8Oe1-KzSfoS z`{xDGo@_v-HPIg0i~zc@g@;Jn3HaFI-GJ{3k!Z9I{PvsE(xdMpFWXfuMUE_V{-x9p zs#e!ZdXKMSP{feiLA7T33fm811z~Jj|6uUy!3z$yg}N1siv$q^CBnDQ3RepK^D0>X zi)24?bFX*zl;=NZ@w-?q7nQO+(DHp=6Zq0mIgzEi_{0w#t89w{HyR&rgM+PkHQWj3 z>FNH;mR-BV@n0}(JQquN!DM$jefQ7Lnoh#mt~up#igK_6`h4N_hM$zsNDnn7%smzl zriz39y+`o2Fw0f!+WyKLZkXYp4i!B?%vy*N%Fmq`W3r~mxZfop5Z*j7B3(T(Zi?-@ zz3)TFgRcDT=@UOf#r=9`P<=XGgrKjy#}}h(pK_1I@BC9tQ^7{j4@8^5jCfV{EsFku z+<6ya`z?A0>O7n;#mwTy0kpTBux{+?mBIXUbg=iQf9^3+a~3PzE7YvUER>Q z4Uf}dy3L@9P1a7--lyBTtp5|6{s3{Vz3x=!cf%TatMHeQg^ydd?hhX@zJ3Slbc|RM z_9(2~i`bR4Grm4sefzC+V3<$8-PhpvD4U;GPhEZp5%Km1sP}dJ5jka?6 zkL8E>*}?Z`JL82973Jn`0BG_+=|#2ZJs*T~+sWY7FqGv5%j`X^@C8y;0p-G$R z6=bDb=d!Axv1OHlLyb0)ah zG>nt9Mwx=c$nDq4gMkk5wCmpA)?;8TftkpA*S@%_A^Xnfy>33d=1CHOOxgql*e4?( zQ#OP3ViFfJ%IZJgq^>X+T)mM~-9MUbF%^_m*?ftQ!@*_fa*7ec!TBn~mzV#WY@ZuO z;qJ$!>&Tb6c-M0nW!|T+C{?QJWru;Bd~VtB$s7CZ6txbemVmN3fv@n{>Z%GGl#e(9 zpiGAmdaN^~(fM(CN{N3-#M&qERYdJ|eAGsNz0Yj;;C*C*hWaXHp6^z*sFR`s&*}Hj zGrM(;M`gDShr(2|kXt=9{S5)D>XxZ1GuvST&nFz^S0(4XhreAsD(|$+##e;7Z z@Lj4gY~%qXjMxxw5!q@U!OWhiD{t3A$Agpaw?4cP=+yh$xT$Z9ZGjiskSnEVb2=!4 z_dlc?=r%>w%!Yh+Yvkd#;-&Su5hrzOEHBy|NE lifRU*`_Hxef22JF2$_yK8}y_P_Vp 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 IbHC(&15^GUf&c&j diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index 82338efe9..6b76b37a5 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":29611,"get_count":12,"put_bytes":27515,"put_count":10}} -{"i":1,"series":1,"stats":{"get_bytes":29962,"get_count":13,"put_bytes":27866,"put_count":11}} -{"i":2,"series":1,"stats":{"get_bytes":28938,"get_count":10,"put_bytes":26842,"put_count":8}} -{"i":3,"series":1,"stats":{"get_bytes":30012,"get_count":11,"put_bytes":27916,"put_count":9}} -{"i":4,"series":1,"stats":{"get_bytes":31157,"get_count":14,"put_bytes":29061,"put_count":12}} -{"i":5,"series":1,"stats":{"get_bytes":28650,"get_count":10,"put_bytes":26554,"put_count":8}} -{"i":6,"series":1,"stats":{"get_bytes":31221,"get_count":13,"put_bytes":29125,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":29432,"get_count":11,"put_bytes":27336,"put_count":9}} -{"i":8,"series":1,"stats":{"get_bytes":30509,"get_count":13,"put_bytes":28413,"put_count":11}} -{"i":9,"series":1,"stats":{"get_bytes":30661,"get_count":13,"put_bytes":28565,"put_count":11}} -{"i":10,"series":1,"stats":{"get_bytes":29641,"get_count":12,"put_bytes":27545,"put_count":10}} -{"i":11,"series":1,"stats":{"get_bytes":30389,"get_count":12,"put_bytes":28293,"put_count":10}} -{"i":12,"series":1,"stats":{"get_bytes":29398,"get_count":11,"put_bytes":27302,"put_count":9}} -{"i":13,"series":1,"stats":{"get_bytes":30494,"get_count":13,"put_bytes":28398,"put_count":11}} -{"i":14,"series":1,"stats":{"get_bytes":30949,"get_count":13,"put_bytes":28853,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":30566,"get_count":12,"put_bytes":28470,"put_count":10}} -{"i":16,"series":1,"stats":{"get_bytes":29710,"get_count":11,"put_bytes":27614,"put_count":9}} -{"i":17,"series":1,"stats":{"get_bytes":29670,"get_count":12,"put_bytes":27574,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":28797,"get_count":11,"put_bytes":26701,"put_count":9}} -{"i":19,"series":1,"stats":{"get_bytes":30007,"get_count":13,"put_bytes":27911,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":30640,"get_count":14,"put_bytes":28544,"put_count":12}} -{"i":21,"series":1,"stats":{"get_bytes":29581,"get_count":12,"put_bytes":27485,"put_count":10}} -{"i":22,"series":1,"stats":{"get_bytes":30141,"get_count":13,"put_bytes":28045,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":29251,"get_count":11,"put_bytes":27155,"put_count":9}} -{"i":24,"series":1,"stats":{"get_bytes":29826,"get_count":12,"put_bytes":27730,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":30359,"get_count":13,"put_bytes":28263,"put_count":11}} -{"i":26,"series":1,"stats":{"get_bytes":30961,"get_count":14,"put_bytes":28865,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":30378,"get_count":13,"put_bytes":28282,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":29136,"get_count":10,"put_bytes":27040,"put_count":8}} -{"i":29,"series":1,"stats":{"get_bytes":30314,"get_count":11,"put_bytes":28218,"put_count":9}} -{"i":30,"series":1,"stats":{"get_bytes":29483,"get_count":11,"put_bytes":27387,"put_count":9}} -{"i":31,"series":1,"stats":{"get_bytes":28764,"get_count":9,"put_bytes":26668,"put_count":7}} -{"i":32,"series":1,"stats":{"get_bytes":28786,"get_count":10,"put_bytes":26690,"put_count":8}} -{"i":33,"series":1,"stats":{"get_bytes":31232,"get_count":13,"put_bytes":29136,"put_count":11}} -{"i":34,"series":1,"stats":{"get_bytes":29827,"get_count":12,"put_bytes":27731,"put_count":10}} -{"i":35,"series":1,"stats":{"get_bytes":29690,"get_count":11,"put_bytes":27594,"put_count":9}} -{"i":36,"series":1,"stats":{"get_bytes":28402,"get_count":10,"put_bytes":26306,"put_count":8}} -{"i":37,"series":1,"stats":{"get_bytes":28478,"get_count":10,"put_bytes":26382,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":29001,"get_count":10,"put_bytes":26905,"put_count":8}} -{"i":39,"series":1,"stats":{"get_bytes":29683,"get_count":10,"put_bytes":27587,"put_count":8}} -{"i":40,"series":1,"stats":{"get_bytes":30213,"get_count":13,"put_bytes":28117,"put_count":11}} -{"i":41,"series":1,"stats":{"get_bytes":30412,"get_count":12,"put_bytes":28316,"put_count":10}} -{"i":42,"series":1,"stats":{"get_bytes":30039,"get_count":12,"put_bytes":27943,"put_count":10}} -{"i":43,"series":1,"stats":{"get_bytes":30404,"get_count":12,"put_bytes":28308,"put_count":10}} -{"i":44,"series":1,"stats":{"get_bytes":28548,"get_count":10,"put_bytes":26452,"put_count":8}} -{"i":45,"series":1,"stats":{"get_bytes":28065,"get_count":8,"put_bytes":25969,"put_count":6}} -{"i":46,"series":1,"stats":{"get_bytes":28865,"get_count":10,"put_bytes":26769,"put_count":8}} -{"i":47,"series":1,"stats":{"get_bytes":29210,"get_count":10,"put_bytes":27114,"put_count":8}} -{"i":48,"series":1,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":26767,"put_count":8}} -{"i":49,"series":1,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":27769,"put_count":9}} -{"i":50,"series":1,"stats":{"get_bytes":27686,"get_count":8,"put_bytes":25590,"put_count":6}} -{"i":51,"series":1,"stats":{"get_bytes":29355,"get_count":11,"put_bytes":27259,"put_count":9}} -{"i":52,"series":1,"stats":{"get_bytes":29016,"get_count":10,"put_bytes":26920,"put_count":8}} -{"i":53,"series":1,"stats":{"get_bytes":30050,"get_count":12,"put_bytes":27954,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":27831,"get_count":8,"put_bytes":25735,"put_count":6}} -{"i":55,"series":1,"stats":{"get_bytes":30038,"get_count":12,"put_bytes":27942,"put_count":10}} -{"i":56,"series":1,"stats":{"get_bytes":30073,"get_count":11,"put_bytes":27977,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":29809,"get_count":12,"put_bytes":27713,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":30120,"get_count":12,"put_bytes":28024,"put_count":10}} -{"i":59,"series":1,"stats":{"get_bytes":30352,"get_count":13,"put_bytes":28256,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":30593,"get_count":13,"put_bytes":28497,"put_count":11}} -{"i":61,"series":1,"stats":{"get_bytes":29464,"get_count":11,"put_bytes":27368,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":29491,"get_count":11,"put_bytes":27395,"put_count":9}} -{"i":63,"series":1,"stats":{"get_bytes":29598,"get_count":12,"put_bytes":27502,"put_count":10}} -{"i":64,"series":1,"stats":{"get_bytes":29886,"get_count":12,"put_bytes":27790,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":30436,"get_count":14,"put_bytes":28340,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":29354,"get_count":11,"put_bytes":27258,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":29657,"get_count":12,"put_bytes":27561,"put_count":10}} -{"i":68,"series":1,"stats":{"get_bytes":30356,"get_count":13,"put_bytes":28260,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":29210,"get_count":11,"put_bytes":27114,"put_count":9}} -{"i":70,"series":1,"stats":{"get_bytes":29867,"get_count":11,"put_bytes":27771,"put_count":9}} -{"i":71,"series":1,"stats":{"get_bytes":28470,"get_count":9,"put_bytes":26374,"put_count":7}} -{"i":72,"series":1,"stats":{"get_bytes":31164,"get_count":13,"put_bytes":29068,"put_count":11}} -{"i":73,"series":1,"stats":{"get_bytes":29626,"get_count":11,"put_bytes":27530,"put_count":9}} -{"i":74,"series":1,"stats":{"get_bytes":30044,"get_count":12,"put_bytes":27948,"put_count":10}} -{"i":75,"series":1,"stats":{"get_bytes":29889,"get_count":12,"put_bytes":27793,"put_count":10}} -{"i":76,"series":1,"stats":{"get_bytes":29994,"get_count":11,"put_bytes":27898,"put_count":9}} -{"i":77,"series":1,"stats":{"get_bytes":29800,"get_count":11,"put_bytes":27704,"put_count":9}} -{"i":78,"series":1,"stats":{"get_bytes":29142,"get_count":9,"put_bytes":27046,"put_count":7}} -{"i":79,"series":1,"stats":{"get_bytes":29717,"get_count":11,"put_bytes":27621,"put_count":9}} -{"i":80,"series":1,"stats":{"get_bytes":30767,"get_count":14,"put_bytes":28671,"put_count":12}} -{"i":81,"series":1,"stats":{"get_bytes":30242,"get_count":12,"put_bytes":28146,"put_count":10}} -{"i":82,"series":1,"stats":{"get_bytes":29000,"get_count":10,"put_bytes":26904,"put_count":8}} -{"i":83,"series":1,"stats":{"get_bytes":29851,"get_count":11,"put_bytes":27755,"put_count":9}} -{"i":84,"series":1,"stats":{"get_bytes":27979,"get_count":8,"put_bytes":25883,"put_count":6}} -{"i":85,"series":1,"stats":{"get_bytes":28547,"get_count":10,"put_bytes":26451,"put_count":8}} -{"i":86,"series":1,"stats":{"get_bytes":30799,"get_count":13,"put_bytes":28703,"put_count":11}} -{"i":87,"series":1,"stats":{"get_bytes":30307,"get_count":13,"put_bytes":28211,"put_count":11}} -{"i":88,"series":1,"stats":{"get_bytes":29948,"get_count":12,"put_bytes":27852,"put_count":10}} -{"i":89,"series":1,"stats":{"get_bytes":29590,"get_count":12,"put_bytes":27494,"put_count":10}} -{"i":90,"series":1,"stats":{"get_bytes":30423,"get_count":13,"put_bytes":28327,"put_count":11}} -{"i":91,"series":1,"stats":{"get_bytes":29593,"get_count":12,"put_bytes":27497,"put_count":10}} -{"i":92,"series":1,"stats":{"get_bytes":29107,"get_count":11,"put_bytes":27011,"put_count":9}} -{"i":93,"series":1,"stats":{"get_bytes":29339,"get_count":11,"put_bytes":27243,"put_count":9}} -{"i":94,"series":1,"stats":{"get_bytes":28896,"get_count":9,"put_bytes":26800,"put_count":7}} -{"i":95,"series":1,"stats":{"get_bytes":29538,"get_count":11,"put_bytes":27442,"put_count":9}} -{"i":96,"series":1,"stats":{"get_bytes":28292,"get_count":8,"put_bytes":26196,"put_count":6}} -{"i":97,"series":1,"stats":{"get_bytes":28430,"get_count":9,"put_bytes":26334,"put_count":7}} -{"i":98,"series":1,"stats":{"get_bytes":29697,"get_count":11,"put_bytes":27601,"put_count":9}} -{"i":99,"series":1,"stats":{"get_bytes":29644,"get_count":11,"put_bytes":27548,"put_count":9}} -{"i":0,"series":2,"stats":{"get_bytes":29611,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":29962,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":2,"series":2,"stats":{"get_bytes":28938,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":3,"series":2,"stats":{"get_bytes":30012,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":4,"series":2,"stats":{"get_bytes":31157,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":5,"series":2,"stats":{"get_bytes":28650,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":6,"series":2,"stats":{"get_bytes":31221,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":7,"series":2,"stats":{"get_bytes":29432,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":8,"series":2,"stats":{"get_bytes":30509,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":9,"series":2,"stats":{"get_bytes":30661,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":10,"series":2,"stats":{"get_bytes":29641,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":11,"series":2,"stats":{"get_bytes":30389,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":12,"series":2,"stats":{"get_bytes":29398,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":13,"series":2,"stats":{"get_bytes":30494,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":14,"series":2,"stats":{"get_bytes":30949,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":15,"series":2,"stats":{"get_bytes":30566,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":16,"series":2,"stats":{"get_bytes":29710,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":17,"series":2,"stats":{"get_bytes":29670,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":18,"series":2,"stats":{"get_bytes":28797,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":19,"series":2,"stats":{"get_bytes":30007,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":20,"series":2,"stats":{"get_bytes":30640,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":21,"series":2,"stats":{"get_bytes":29581,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":22,"series":2,"stats":{"get_bytes":30141,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":23,"series":2,"stats":{"get_bytes":29251,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":24,"series":2,"stats":{"get_bytes":29826,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":25,"series":2,"stats":{"get_bytes":30359,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":26,"series":2,"stats":{"get_bytes":30961,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":27,"series":2,"stats":{"get_bytes":30378,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":28,"series":2,"stats":{"get_bytes":29136,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":29,"series":2,"stats":{"get_bytes":30314,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":30,"series":2,"stats":{"get_bytes":29483,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":31,"series":2,"stats":{"get_bytes":28764,"get_count":9,"put_bytes":88,"put_count":1}} -{"i":32,"series":2,"stats":{"get_bytes":28786,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":33,"series":2,"stats":{"get_bytes":31232,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":34,"series":2,"stats":{"get_bytes":29827,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":35,"series":2,"stats":{"get_bytes":29690,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":36,"series":2,"stats":{"get_bytes":28402,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":37,"series":2,"stats":{"get_bytes":28478,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":38,"series":2,"stats":{"get_bytes":29001,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":39,"series":2,"stats":{"get_bytes":29683,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":40,"series":2,"stats":{"get_bytes":30213,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":41,"series":2,"stats":{"get_bytes":30412,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":42,"series":2,"stats":{"get_bytes":30039,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":43,"series":2,"stats":{"get_bytes":30404,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":44,"series":2,"stats":{"get_bytes":28548,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":45,"series":2,"stats":{"get_bytes":28065,"get_count":8,"put_bytes":88,"put_count":1}} -{"i":46,"series":2,"stats":{"get_bytes":28865,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":47,"series":2,"stats":{"get_bytes":29210,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":48,"series":2,"stats":{"get_bytes":28863,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":49,"series":2,"stats":{"get_bytes":29865,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":50,"series":2,"stats":{"get_bytes":27686,"get_count":8,"put_bytes":88,"put_count":1}} -{"i":51,"series":2,"stats":{"get_bytes":29355,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":52,"series":2,"stats":{"get_bytes":29016,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":53,"series":2,"stats":{"get_bytes":30050,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":54,"series":2,"stats":{"get_bytes":27831,"get_count":8,"put_bytes":88,"put_count":1}} -{"i":55,"series":2,"stats":{"get_bytes":30038,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":56,"series":2,"stats":{"get_bytes":30073,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":57,"series":2,"stats":{"get_bytes":29809,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":58,"series":2,"stats":{"get_bytes":30120,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":59,"series":2,"stats":{"get_bytes":30352,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":60,"series":2,"stats":{"get_bytes":30593,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":61,"series":2,"stats":{"get_bytes":29464,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":62,"series":2,"stats":{"get_bytes":29491,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":63,"series":2,"stats":{"get_bytes":29598,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":64,"series":2,"stats":{"get_bytes":29886,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":65,"series":2,"stats":{"get_bytes":30436,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":66,"series":2,"stats":{"get_bytes":29354,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":67,"series":2,"stats":{"get_bytes":29657,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":68,"series":2,"stats":{"get_bytes":30356,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":69,"series":2,"stats":{"get_bytes":29210,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":70,"series":2,"stats":{"get_bytes":29867,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":71,"series":2,"stats":{"get_bytes":28470,"get_count":9,"put_bytes":88,"put_count":1}} -{"i":72,"series":2,"stats":{"get_bytes":31164,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":73,"series":2,"stats":{"get_bytes":29626,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":74,"series":2,"stats":{"get_bytes":30044,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":75,"series":2,"stats":{"get_bytes":29889,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":76,"series":2,"stats":{"get_bytes":29994,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":77,"series":2,"stats":{"get_bytes":29800,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":78,"series":2,"stats":{"get_bytes":29142,"get_count":9,"put_bytes":88,"put_count":1}} -{"i":79,"series":2,"stats":{"get_bytes":29717,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":80,"series":2,"stats":{"get_bytes":30767,"get_count":14,"put_bytes":88,"put_count":1}} -{"i":81,"series":2,"stats":{"get_bytes":30242,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":82,"series":2,"stats":{"get_bytes":29000,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":83,"series":2,"stats":{"get_bytes":29851,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":84,"series":2,"stats":{"get_bytes":27979,"get_count":8,"put_bytes":88,"put_count":1}} -{"i":85,"series":2,"stats":{"get_bytes":28547,"get_count":10,"put_bytes":88,"put_count":1}} -{"i":86,"series":2,"stats":{"get_bytes":30799,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":87,"series":2,"stats":{"get_bytes":30307,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":88,"series":2,"stats":{"get_bytes":29948,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":89,"series":2,"stats":{"get_bytes":29590,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":90,"series":2,"stats":{"get_bytes":30423,"get_count":13,"put_bytes":88,"put_count":1}} -{"i":91,"series":2,"stats":{"get_bytes":29593,"get_count":12,"put_bytes":88,"put_count":1}} -{"i":92,"series":2,"stats":{"get_bytes":29107,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":93,"series":2,"stats":{"get_bytes":29339,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":94,"series":2,"stats":{"get_bytes":28896,"get_count":9,"put_bytes":88,"put_count":1}} -{"i":95,"series":2,"stats":{"get_bytes":29538,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":96,"series":2,"stats":{"get_bytes":28292,"get_count":8,"put_bytes":88,"put_count":1}} -{"i":97,"series":2,"stats":{"get_bytes":28430,"get_count":9,"put_bytes":88,"put_count":1}} -{"i":98,"series":2,"stats":{"get_bytes":29697,"get_count":11,"put_bytes":88,"put_count":1}} -{"i":99,"series":2,"stats":{"get_bytes":29644,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":29523,"get_count":11,"put_bytes":27515,"put_count":10}} +{"i":1,"series":1,"stats":{"get_bytes":29874,"get_count":12,"put_bytes":27866,"put_count":11}} +{"i":2,"series":1,"stats":{"get_bytes":28850,"get_count":9,"put_bytes":26842,"put_count":8}} +{"i":3,"series":1,"stats":{"get_bytes":29924,"get_count":10,"put_bytes":27916,"put_count":9}} +{"i":4,"series":1,"stats":{"get_bytes":31069,"get_count":13,"put_bytes":29061,"put_count":12}} +{"i":5,"series":1,"stats":{"get_bytes":28562,"get_count":9,"put_bytes":26554,"put_count":8}} +{"i":6,"series":1,"stats":{"get_bytes":31133,"get_count":12,"put_bytes":29125,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":29344,"get_count":10,"put_bytes":27336,"put_count":9}} +{"i":8,"series":1,"stats":{"get_bytes":30421,"get_count":12,"put_bytes":28413,"put_count":11}} +{"i":9,"series":1,"stats":{"get_bytes":30573,"get_count":12,"put_bytes":28565,"put_count":11}} +{"i":10,"series":1,"stats":{"get_bytes":29553,"get_count":11,"put_bytes":27545,"put_count":10}} +{"i":11,"series":1,"stats":{"get_bytes":30301,"get_count":11,"put_bytes":28293,"put_count":10}} +{"i":12,"series":1,"stats":{"get_bytes":29310,"get_count":10,"put_bytes":27302,"put_count":9}} +{"i":13,"series":1,"stats":{"get_bytes":30406,"get_count":12,"put_bytes":28398,"put_count":11}} +{"i":14,"series":1,"stats":{"get_bytes":30861,"get_count":12,"put_bytes":28853,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":30478,"get_count":11,"put_bytes":28470,"put_count":10}} +{"i":16,"series":1,"stats":{"get_bytes":29622,"get_count":10,"put_bytes":27614,"put_count":9}} +{"i":17,"series":1,"stats":{"get_bytes":29582,"get_count":11,"put_bytes":27574,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":28709,"get_count":10,"put_bytes":26701,"put_count":9}} +{"i":19,"series":1,"stats":{"get_bytes":29919,"get_count":12,"put_bytes":27911,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":30552,"get_count":13,"put_bytes":28544,"put_count":12}} +{"i":21,"series":1,"stats":{"get_bytes":29493,"get_count":11,"put_bytes":27485,"put_count":10}} +{"i":22,"series":1,"stats":{"get_bytes":30053,"get_count":12,"put_bytes":28045,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":29163,"get_count":10,"put_bytes":27155,"put_count":9}} +{"i":24,"series":1,"stats":{"get_bytes":29738,"get_count":11,"put_bytes":27730,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":30271,"get_count":12,"put_bytes":28263,"put_count":11}} +{"i":26,"series":1,"stats":{"get_bytes":30873,"get_count":13,"put_bytes":28865,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":30290,"get_count":12,"put_bytes":28282,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":29048,"get_count":9,"put_bytes":27040,"put_count":8}} +{"i":29,"series":1,"stats":{"get_bytes":30226,"get_count":10,"put_bytes":28218,"put_count":9}} +{"i":30,"series":1,"stats":{"get_bytes":29395,"get_count":10,"put_bytes":27387,"put_count":9}} +{"i":31,"series":1,"stats":{"get_bytes":28676,"get_count":8,"put_bytes":26668,"put_count":7}} +{"i":32,"series":1,"stats":{"get_bytes":28698,"get_count":9,"put_bytes":26690,"put_count":8}} +{"i":33,"series":1,"stats":{"get_bytes":31144,"get_count":12,"put_bytes":29136,"put_count":11}} +{"i":34,"series":1,"stats":{"get_bytes":29739,"get_count":11,"put_bytes":27731,"put_count":10}} +{"i":35,"series":1,"stats":{"get_bytes":29602,"get_count":10,"put_bytes":27594,"put_count":9}} +{"i":36,"series":1,"stats":{"get_bytes":28314,"get_count":9,"put_bytes":26306,"put_count":8}} +{"i":37,"series":1,"stats":{"get_bytes":28390,"get_count":9,"put_bytes":26382,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":28913,"get_count":9,"put_bytes":26905,"put_count":8}} +{"i":39,"series":1,"stats":{"get_bytes":29595,"get_count":9,"put_bytes":27587,"put_count":8}} +{"i":40,"series":1,"stats":{"get_bytes":30125,"get_count":12,"put_bytes":28117,"put_count":11}} +{"i":41,"series":1,"stats":{"get_bytes":30324,"get_count":11,"put_bytes":28316,"put_count":10}} +{"i":42,"series":1,"stats":{"get_bytes":29951,"get_count":11,"put_bytes":27943,"put_count":10}} +{"i":43,"series":1,"stats":{"get_bytes":30316,"get_count":11,"put_bytes":28308,"put_count":10}} +{"i":44,"series":1,"stats":{"get_bytes":28460,"get_count":9,"put_bytes":26452,"put_count":8}} +{"i":45,"series":1,"stats":{"get_bytes":27977,"get_count":7,"put_bytes":25969,"put_count":6}} +{"i":46,"series":1,"stats":{"get_bytes":28777,"get_count":9,"put_bytes":26769,"put_count":8}} +{"i":47,"series":1,"stats":{"get_bytes":29122,"get_count":9,"put_bytes":27114,"put_count":8}} +{"i":48,"series":1,"stats":{"get_bytes":28775,"get_count":9,"put_bytes":26767,"put_count":8}} +{"i":49,"series":1,"stats":{"get_bytes":29777,"get_count":10,"put_bytes":27769,"put_count":9}} +{"i":50,"series":1,"stats":{"get_bytes":27598,"get_count":7,"put_bytes":25590,"put_count":6}} +{"i":51,"series":1,"stats":{"get_bytes":29267,"get_count":10,"put_bytes":27259,"put_count":9}} +{"i":52,"series":1,"stats":{"get_bytes":28928,"get_count":9,"put_bytes":26920,"put_count":8}} +{"i":53,"series":1,"stats":{"get_bytes":29962,"get_count":11,"put_bytes":27954,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":27743,"get_count":7,"put_bytes":25735,"put_count":6}} +{"i":55,"series":1,"stats":{"get_bytes":29950,"get_count":11,"put_bytes":27942,"put_count":10}} +{"i":56,"series":1,"stats":{"get_bytes":29985,"get_count":10,"put_bytes":27977,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":29721,"get_count":11,"put_bytes":27713,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":30032,"get_count":11,"put_bytes":28024,"put_count":10}} +{"i":59,"series":1,"stats":{"get_bytes":30264,"get_count":12,"put_bytes":28256,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":30505,"get_count":12,"put_bytes":28497,"put_count":11}} +{"i":61,"series":1,"stats":{"get_bytes":29376,"get_count":10,"put_bytes":27368,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":29403,"get_count":10,"put_bytes":27395,"put_count":9}} +{"i":63,"series":1,"stats":{"get_bytes":29510,"get_count":11,"put_bytes":27502,"put_count":10}} +{"i":64,"series":1,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":27790,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":30348,"get_count":13,"put_bytes":28340,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":29266,"get_count":10,"put_bytes":27258,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":29569,"get_count":11,"put_bytes":27561,"put_count":10}} +{"i":68,"series":1,"stats":{"get_bytes":30268,"get_count":12,"put_bytes":28260,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":29122,"get_count":10,"put_bytes":27114,"put_count":9}} +{"i":70,"series":1,"stats":{"get_bytes":29779,"get_count":10,"put_bytes":27771,"put_count":9}} +{"i":71,"series":1,"stats":{"get_bytes":28382,"get_count":8,"put_bytes":26374,"put_count":7}} +{"i":72,"series":1,"stats":{"get_bytes":31076,"get_count":12,"put_bytes":29068,"put_count":11}} +{"i":73,"series":1,"stats":{"get_bytes":29538,"get_count":10,"put_bytes":27530,"put_count":9}} +{"i":74,"series":1,"stats":{"get_bytes":29956,"get_count":11,"put_bytes":27948,"put_count":10}} +{"i":75,"series":1,"stats":{"get_bytes":29801,"get_count":11,"put_bytes":27793,"put_count":10}} +{"i":76,"series":1,"stats":{"get_bytes":29906,"get_count":10,"put_bytes":27898,"put_count":9}} +{"i":77,"series":1,"stats":{"get_bytes":29712,"get_count":10,"put_bytes":27704,"put_count":9}} +{"i":78,"series":1,"stats":{"get_bytes":29054,"get_count":8,"put_bytes":27046,"put_count":7}} +{"i":79,"series":1,"stats":{"get_bytes":29629,"get_count":10,"put_bytes":27621,"put_count":9}} +{"i":80,"series":1,"stats":{"get_bytes":30679,"get_count":13,"put_bytes":28671,"put_count":12}} +{"i":81,"series":1,"stats":{"get_bytes":30154,"get_count":11,"put_bytes":28146,"put_count":10}} +{"i":82,"series":1,"stats":{"get_bytes":28912,"get_count":9,"put_bytes":26904,"put_count":8}} +{"i":83,"series":1,"stats":{"get_bytes":29763,"get_count":10,"put_bytes":27755,"put_count":9}} +{"i":84,"series":1,"stats":{"get_bytes":27891,"get_count":7,"put_bytes":25883,"put_count":6}} +{"i":85,"series":1,"stats":{"get_bytes":28459,"get_count":9,"put_bytes":26451,"put_count":8}} +{"i":86,"series":1,"stats":{"get_bytes":30711,"get_count":12,"put_bytes":28703,"put_count":11}} +{"i":87,"series":1,"stats":{"get_bytes":30219,"get_count":12,"put_bytes":28211,"put_count":11}} +{"i":88,"series":1,"stats":{"get_bytes":29860,"get_count":11,"put_bytes":27852,"put_count":10}} +{"i":89,"series":1,"stats":{"get_bytes":29502,"get_count":11,"put_bytes":27494,"put_count":10}} +{"i":90,"series":1,"stats":{"get_bytes":30335,"get_count":12,"put_bytes":28327,"put_count":11}} +{"i":91,"series":1,"stats":{"get_bytes":29505,"get_count":11,"put_bytes":27497,"put_count":10}} +{"i":92,"series":1,"stats":{"get_bytes":29019,"get_count":10,"put_bytes":27011,"put_count":9}} +{"i":93,"series":1,"stats":{"get_bytes":29251,"get_count":10,"put_bytes":27243,"put_count":9}} +{"i":94,"series":1,"stats":{"get_bytes":28808,"get_count":8,"put_bytes":26800,"put_count":7}} +{"i":95,"series":1,"stats":{"get_bytes":29450,"get_count":10,"put_bytes":27442,"put_count":9}} +{"i":96,"series":1,"stats":{"get_bytes":28204,"get_count":7,"put_bytes":26196,"put_count":6}} +{"i":97,"series":1,"stats":{"get_bytes":28342,"get_count":8,"put_bytes":26334,"put_count":7}} +{"i":98,"series":1,"stats":{"get_bytes":29609,"get_count":10,"put_bytes":27601,"put_count":9}} +{"i":99,"series":1,"stats":{"get_bytes":29556,"get_count":10,"put_bytes":27548,"put_count":9}} +{"i":0,"series":2,"stats":{"get_bytes":29523,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":1,"series":2,"stats":{"get_bytes":29874,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":2,"series":2,"stats":{"get_bytes":28850,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":3,"series":2,"stats":{"get_bytes":29924,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":4,"series":2,"stats":{"get_bytes":31069,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":5,"series":2,"stats":{"get_bytes":28562,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":6,"series":2,"stats":{"get_bytes":31133,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":7,"series":2,"stats":{"get_bytes":29344,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":8,"series":2,"stats":{"get_bytes":30421,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":9,"series":2,"stats":{"get_bytes":30573,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":10,"series":2,"stats":{"get_bytes":29553,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":11,"series":2,"stats":{"get_bytes":30301,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":12,"series":2,"stats":{"get_bytes":29310,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":13,"series":2,"stats":{"get_bytes":30406,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":14,"series":2,"stats":{"get_bytes":30861,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":15,"series":2,"stats":{"get_bytes":30478,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":16,"series":2,"stats":{"get_bytes":29622,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":17,"series":2,"stats":{"get_bytes":29582,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":18,"series":2,"stats":{"get_bytes":28709,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":19,"series":2,"stats":{"get_bytes":29919,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":20,"series":2,"stats":{"get_bytes":30552,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":21,"series":2,"stats":{"get_bytes":29493,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":22,"series":2,"stats":{"get_bytes":30053,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":23,"series":2,"stats":{"get_bytes":29163,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":24,"series":2,"stats":{"get_bytes":29738,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":25,"series":2,"stats":{"get_bytes":30271,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":26,"series":2,"stats":{"get_bytes":30873,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":27,"series":2,"stats":{"get_bytes":30290,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":28,"series":2,"stats":{"get_bytes":29048,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":29,"series":2,"stats":{"get_bytes":30226,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":30,"series":2,"stats":{"get_bytes":29395,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":31,"series":2,"stats":{"get_bytes":28676,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":32,"series":2,"stats":{"get_bytes":28698,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":33,"series":2,"stats":{"get_bytes":31144,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":34,"series":2,"stats":{"get_bytes":29739,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":35,"series":2,"stats":{"get_bytes":29602,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":36,"series":2,"stats":{"get_bytes":28314,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":37,"series":2,"stats":{"get_bytes":28390,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":38,"series":2,"stats":{"get_bytes":28913,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":39,"series":2,"stats":{"get_bytes":29595,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":40,"series":2,"stats":{"get_bytes":30125,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":41,"series":2,"stats":{"get_bytes":30324,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":42,"series":2,"stats":{"get_bytes":29951,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":43,"series":2,"stats":{"get_bytes":30316,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":44,"series":2,"stats":{"get_bytes":28460,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":45,"series":2,"stats":{"get_bytes":27977,"get_count":7,"put_bytes":88,"put_count":1}} +{"i":46,"series":2,"stats":{"get_bytes":28777,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":47,"series":2,"stats":{"get_bytes":29122,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":48,"series":2,"stats":{"get_bytes":28775,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":49,"series":2,"stats":{"get_bytes":29777,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":50,"series":2,"stats":{"get_bytes":27598,"get_count":7,"put_bytes":88,"put_count":1}} +{"i":51,"series":2,"stats":{"get_bytes":29267,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":52,"series":2,"stats":{"get_bytes":28928,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":53,"series":2,"stats":{"get_bytes":29962,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":54,"series":2,"stats":{"get_bytes":27743,"get_count":7,"put_bytes":88,"put_count":1}} +{"i":55,"series":2,"stats":{"get_bytes":29950,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":56,"series":2,"stats":{"get_bytes":29985,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":57,"series":2,"stats":{"get_bytes":29721,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":58,"series":2,"stats":{"get_bytes":30032,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":59,"series":2,"stats":{"get_bytes":30264,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":60,"series":2,"stats":{"get_bytes":30505,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":61,"series":2,"stats":{"get_bytes":29376,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":62,"series":2,"stats":{"get_bytes":29403,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":63,"series":2,"stats":{"get_bytes":29510,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":64,"series":2,"stats":{"get_bytes":29798,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":65,"series":2,"stats":{"get_bytes":30348,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":66,"series":2,"stats":{"get_bytes":29266,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":67,"series":2,"stats":{"get_bytes":29569,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":68,"series":2,"stats":{"get_bytes":30268,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":69,"series":2,"stats":{"get_bytes":29122,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":70,"series":2,"stats":{"get_bytes":29779,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":71,"series":2,"stats":{"get_bytes":28382,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":72,"series":2,"stats":{"get_bytes":31076,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":73,"series":2,"stats":{"get_bytes":29538,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":74,"series":2,"stats":{"get_bytes":29956,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":75,"series":2,"stats":{"get_bytes":29801,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":76,"series":2,"stats":{"get_bytes":29906,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":77,"series":2,"stats":{"get_bytes":29712,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":78,"series":2,"stats":{"get_bytes":29054,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":79,"series":2,"stats":{"get_bytes":29629,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":80,"series":2,"stats":{"get_bytes":30679,"get_count":13,"put_bytes":88,"put_count":1}} +{"i":81,"series":2,"stats":{"get_bytes":30154,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":82,"series":2,"stats":{"get_bytes":28912,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":83,"series":2,"stats":{"get_bytes":29763,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":84,"series":2,"stats":{"get_bytes":27891,"get_count":7,"put_bytes":88,"put_count":1}} +{"i":85,"series":2,"stats":{"get_bytes":28459,"get_count":9,"put_bytes":88,"put_count":1}} +{"i":86,"series":2,"stats":{"get_bytes":30711,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":87,"series":2,"stats":{"get_bytes":30219,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":88,"series":2,"stats":{"get_bytes":29860,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":89,"series":2,"stats":{"get_bytes":29502,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":90,"series":2,"stats":{"get_bytes":30335,"get_count":12,"put_bytes":88,"put_count":1}} +{"i":91,"series":2,"stats":{"get_bytes":29505,"get_count":11,"put_bytes":88,"put_count":1}} +{"i":92,"series":2,"stats":{"get_bytes":29019,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":93,"series":2,"stats":{"get_bytes":29251,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":94,"series":2,"stats":{"get_bytes":28808,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":95,"series":2,"stats":{"get_bytes":29450,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":96,"series":2,"stats":{"get_bytes":28204,"get_count":7,"put_bytes":88,"put_count":1}} +{"i":97,"series":2,"stats":{"get_bytes":28342,"get_count":8,"put_bytes":88,"put_count":1}} +{"i":98,"series":2,"stats":{"get_bytes":29609,"get_count":10,"put_bytes":88,"put_count":1}} +{"i":99,"series":2,"stats":{"get_bytes":29556,"get_count":10,"put_bytes":88,"put_count":1}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.png b/actors/evm/tests/measurements/mapping_overwrite.png index 0859ef06bec258abd6b72a46b612e03dbc527d16..1f6b204815d7995569008a62cb1a452bc360a6e3 100644 GIT binary patch literal 22925 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 literal 23406 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 literal 19046 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 JF6*2UngG8Q%B}za diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 0c337641a..8dacfbb21 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":266493,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":1,"series":1,"stats":{"get_bytes":257838,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":2,"series":1,"stats":{"get_bytes":246530,"get_count":81,"put_bytes":88,"put_count":1}} -{"i":3,"series":1,"stats":{"get_bytes":258733,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":4,"series":1,"stats":{"get_bytes":269894,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":5,"series":1,"stats":{"get_bytes":277897,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":6,"series":1,"stats":{"get_bytes":253690,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":7,"series":1,"stats":{"get_bytes":244120,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":8,"series":1,"stats":{"get_bytes":261240,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":9,"series":1,"stats":{"get_bytes":267252,"get_count":90,"put_bytes":88,"put_count":1}} -{"i":10,"series":1,"stats":{"get_bytes":247538,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":11,"series":1,"stats":{"get_bytes":260118,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":12,"series":1,"stats":{"get_bytes":251444,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":13,"series":1,"stats":{"get_bytes":249518,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":14,"series":1,"stats":{"get_bytes":266165,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":15,"series":1,"stats":{"get_bytes":262197,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":16,"series":1,"stats":{"get_bytes":272233,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":17,"series":1,"stats":{"get_bytes":265170,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":18,"series":1,"stats":{"get_bytes":255858,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":19,"series":1,"stats":{"get_bytes":269008,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":20,"series":1,"stats":{"get_bytes":250189,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":21,"series":1,"stats":{"get_bytes":282753,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":22,"series":1,"stats":{"get_bytes":252072,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":23,"series":1,"stats":{"get_bytes":260662,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":24,"series":1,"stats":{"get_bytes":260162,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":25,"series":1,"stats":{"get_bytes":239585,"get_count":79,"put_bytes":88,"put_count":1}} -{"i":26,"series":1,"stats":{"get_bytes":265026,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":27,"series":1,"stats":{"get_bytes":266673,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":28,"series":1,"stats":{"get_bytes":274444,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":29,"series":1,"stats":{"get_bytes":256024,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":30,"series":1,"stats":{"get_bytes":277885,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":31,"series":1,"stats":{"get_bytes":266984,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":32,"series":1,"stats":{"get_bytes":262072,"get_count":90,"put_bytes":88,"put_count":1}} -{"i":33,"series":1,"stats":{"get_bytes":256450,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":34,"series":1,"stats":{"get_bytes":249024,"get_count":81,"put_bytes":88,"put_count":1}} -{"i":35,"series":1,"stats":{"get_bytes":266878,"get_count":90,"put_bytes":88,"put_count":1}} -{"i":36,"series":1,"stats":{"get_bytes":245417,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":37,"series":1,"stats":{"get_bytes":269058,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":38,"series":1,"stats":{"get_bytes":264274,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":39,"series":1,"stats":{"get_bytes":253377,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":40,"series":1,"stats":{"get_bytes":258093,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":41,"series":1,"stats":{"get_bytes":243449,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":42,"series":1,"stats":{"get_bytes":249143,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":43,"series":1,"stats":{"get_bytes":251890,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":44,"series":1,"stats":{"get_bytes":264176,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":45,"series":1,"stats":{"get_bytes":258123,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":46,"series":1,"stats":{"get_bytes":241574,"get_count":80,"put_bytes":88,"put_count":1}} -{"i":47,"series":1,"stats":{"get_bytes":254364,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":48,"series":1,"stats":{"get_bytes":246998,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":49,"series":1,"stats":{"get_bytes":261940,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":50,"series":1,"stats":{"get_bytes":269983,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":51,"series":1,"stats":{"get_bytes":258754,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":52,"series":1,"stats":{"get_bytes":259425,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":53,"series":1,"stats":{"get_bytes":263539,"get_count":90,"put_bytes":88,"put_count":1}} -{"i":54,"series":1,"stats":{"get_bytes":257617,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":55,"series":1,"stats":{"get_bytes":260751,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":56,"series":1,"stats":{"get_bytes":255600,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":57,"series":1,"stats":{"get_bytes":278599,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":58,"series":1,"stats":{"get_bytes":253719,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":59,"series":1,"stats":{"get_bytes":269695,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":60,"series":1,"stats":{"get_bytes":258102,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":61,"series":1,"stats":{"get_bytes":251761,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":62,"series":1,"stats":{"get_bytes":278234,"get_count":94,"put_bytes":88,"put_count":1}} -{"i":63,"series":1,"stats":{"get_bytes":260533,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":64,"series":1,"stats":{"get_bytes":261870,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":65,"series":1,"stats":{"get_bytes":262698,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":66,"series":1,"stats":{"get_bytes":272425,"get_count":90,"put_bytes":88,"put_count":1}} -{"i":67,"series":1,"stats":{"get_bytes":270336,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":68,"series":1,"stats":{"get_bytes":261408,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":69,"series":1,"stats":{"get_bytes":220835,"get_count":75,"put_bytes":88,"put_count":1}} -{"i":70,"series":1,"stats":{"get_bytes":237962,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":71,"series":1,"stats":{"get_bytes":251799,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":72,"series":1,"stats":{"get_bytes":247449,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":73,"series":1,"stats":{"get_bytes":278772,"get_count":93,"put_bytes":88,"put_count":1}} -{"i":74,"series":1,"stats":{"get_bytes":246384,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":75,"series":1,"stats":{"get_bytes":246302,"get_count":82,"put_bytes":88,"put_count":1}} -{"i":76,"series":1,"stats":{"get_bytes":253090,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":77,"series":1,"stats":{"get_bytes":252910,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":78,"series":1,"stats":{"get_bytes":259912,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":79,"series":1,"stats":{"get_bytes":267250,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":80,"series":1,"stats":{"get_bytes":259627,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":81,"series":1,"stats":{"get_bytes":272860,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":82,"series":1,"stats":{"get_bytes":255096,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":83,"series":1,"stats":{"get_bytes":281550,"get_count":94,"put_bytes":88,"put_count":1}} -{"i":84,"series":1,"stats":{"get_bytes":255834,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":85,"series":1,"stats":{"get_bytes":268791,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":86,"series":1,"stats":{"get_bytes":264136,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":87,"series":1,"stats":{"get_bytes":245159,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":88,"series":1,"stats":{"get_bytes":257537,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":89,"series":1,"stats":{"get_bytes":246062,"get_count":84,"put_bytes":88,"put_count":1}} -{"i":90,"series":1,"stats":{"get_bytes":251086,"get_count":86,"put_bytes":88,"put_count":1}} -{"i":91,"series":1,"stats":{"get_bytes":259743,"get_count":87,"put_bytes":88,"put_count":1}} -{"i":92,"series":1,"stats":{"get_bytes":256742,"get_count":85,"put_bytes":88,"put_count":1}} -{"i":93,"series":1,"stats":{"get_bytes":272971,"get_count":91,"put_bytes":88,"put_count":1}} -{"i":94,"series":1,"stats":{"get_bytes":263016,"get_count":89,"put_bytes":88,"put_count":1}} -{"i":95,"series":1,"stats":{"get_bytes":257749,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":96,"series":1,"stats":{"get_bytes":273136,"get_count":92,"put_bytes":88,"put_count":1}} -{"i":97,"series":1,"stats":{"get_bytes":266754,"get_count":88,"put_bytes":88,"put_count":1}} -{"i":98,"series":1,"stats":{"get_bytes":254377,"get_count":83,"put_bytes":88,"put_count":1}} -{"i":99,"series":1,"stats":{"get_bytes":272001,"get_count":91,"put_bytes":88,"put_count":1}} +{"i":0,"series":1,"stats":{"get_bytes":266405,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":257750,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":246442,"get_count":80,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":258645,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":269806,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":277809,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":253602,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":244032,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":261152,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":267164,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":247450,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":260030,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":251356,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":249430,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":266077,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":262109,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":272145,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":265082,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":255770,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":268920,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":250101,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":282665,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":251984,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":260574,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":260074,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":239497,"get_count":78,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":264938,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":266585,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":274356,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":255936,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":277797,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":266896,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":261984,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":256362,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":248936,"get_count":80,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":266790,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":245329,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":268970,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":264186,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":253289,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":258005,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":243361,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":249055,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":251802,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":264088,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":258035,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":241486,"get_count":79,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":254276,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":246910,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":261852,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":269895,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":258666,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":259337,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":263451,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":257529,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":260663,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":255512,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":278511,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":253631,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":269607,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":258014,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":251673,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":278146,"get_count":93,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":260445,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":261782,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":262610,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":272337,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":270248,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":261320,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":220747,"get_count":74,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":237874,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":251711,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":247361,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":278684,"get_count":92,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":246296,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":246214,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":253002,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":252822,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":259824,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":267162,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":259539,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":272772,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":255008,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":281462,"get_count":93,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":255746,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":268703,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":264048,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":245071,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":257449,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":245974,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":250998,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":259655,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":256654,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":272883,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":262928,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":257661,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":273048,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":266666,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":254289,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":271913,"get_count":90,"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 index 80d0414f77ffed7bf95f24e4c265e6819bae5385..3c6bcb3aa0ae001ea3d63a6e20ced2d27abf1f82 100644 GIT binary patch literal 19391 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 UA>X4{d8yj=2ORLf=^I=8Gk|}pcK`qY From cc090a19ca6ca7abd3c5294a931fd9644c8f37fd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Oct 2022 11:06:11 -0700 Subject: [PATCH 083/339] EVM: allow evm/f4 addresses everywhere (#770) - call - callactor - balance - selfdestruct - origin - address - get_evm_bytecode Also remove some of the "from ID" functions to reduce the chances that we'll mess this up. --- actors/evm/src/interpreter/address.rs | 51 +++---- .../evm/src/interpreter/instructions/call.rs | 11 +- .../src/interpreter/instructions/context.rs | 12 +- .../evm/src/interpreter/instructions/ext.rs | 14 +- .../src/interpreter/instructions/lifecycle.rs | 7 +- .../evm/src/interpreter/instructions/state.rs | 8 +- actors/evm/tests/call.rs | 14 +- actors/evm/tests/misc.rs | 127 +++++++++++++++++- actors/evm/tests/storage_footprint.rs | 6 +- actors/evm/tests/util.rs | 8 +- 10 files changed, 194 insertions(+), 64 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index c60865159..c3047ee43 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -33,18 +33,25 @@ impl std::fmt::Debug for EthAddress { } } +impl TryFrom for Address { + type Error = StatusCode; + fn try_from(addr: EthAddress) -> Result { + TryFrom::try_from(&addr) + } +} + impl TryFrom<&EthAddress> for Address { - type Error = anyhow::Error; + type Error = StatusCode; fn try_from(addr: &EthAddress) -> Result { if addr.0[..19] == [0; 19] { - return Err(anyhow::anyhow!( - "Cannot convert a precompile address {:X?} to an f4 address.", - addr - )); + return Err(StatusCode::BadAddress(format!( + "cannot convert precompile {} to an f4 address", + addr.0[19] + ))); } - let f4_addr = if let Some(addr) = addr.as_id_address() { - addr + let f4_addr = if let Some(id) = addr.as_id() { + Address::new_id(id) } else { Address::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap() }; @@ -54,11 +61,6 @@ impl TryFrom<&EthAddress> for Address { } impl EthAddress { - /// Expect a Filecoin address type containing an ID address, and return an address in EVM-form. - pub fn from_id_address(addr: &Address) -> Option { - addr.id().ok().map(EthAddress::from_id) - } - /// Returns an EVM-form ID address from actor ID. pub fn from_id(id: u64) -> EthAddress { let mut bytes = [0u8; 20]; @@ -75,16 +77,11 @@ impl EthAddress { /// /// 0 1-11 12 /// 0xff \[0x00...] [id address...] - pub fn as_id_address(&self) -> Option

{ + pub fn as_id(&self) -> Option { if (self.0[0] != 0xff) || !self.0[1..12].iter().all(|&byte| byte == 0) { return None; } - Some(Address::new_id(u64::from_be_bytes(self.0[12..].try_into().unwrap()))) - } - - /// Same as as_id_address, but go the extra mile and return the ActorID. - pub fn as_id(&self) -> Option { - self.as_id_address().and_then(|id| id.id().ok()) + Some(u64::from_be_bytes(self.0[12..].try_into().unwrap())) } /// Returns this Address as an EVM word. @@ -103,7 +100,6 @@ impl AsRef<[u8]> for EthAddress { mod tests { use crate::interpreter::address::EthAddress; use crate::U256; - use fvm_shared::address::Address as FilecoinAddress; const TYPE_PADDING: &[u8] = &[0; 12]; // padding (12 bytes) const ID_ADDRESS_MARKER: &[u8] = &[0xff]; // ID address marker (1 byte) @@ -117,19 +113,14 @@ mod tests { fn $name() { let evm_bytes = $input.concat(); let evm_addr = EthAddress::try_from(U256::from(evm_bytes.as_slice())).unwrap(); - assert_eq!( - evm_addr.as_id_address(), - $expectation - ); - assert_eq!( evm_addr.as_id(), - $expectation.map(|addr: FilecoinAddress| addr.id().unwrap()) + $expectation ); // test inverse conversion, if a valid ID address was supplied - if let Some(fil_addr) = $expectation { - assert_eq!(EthAddress::from_id_address(&fil_addr), Some(evm_addr)); + if let Some(fil_id) = $expectation { + assert_eq!(EthAddress::from_id(fil_id), evm_addr); } } )* @@ -142,14 +133,14 @@ mod tests { 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(FilecoinAddress::new_id(1)), + ] => 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(FilecoinAddress::new_id(u16::MAX as u64)), + ] => Some(u16::MAX as u64), bad_marker: [ TYPE_PADDING, diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index aa95369fb..7edc185b5 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,4 +1,5 @@ use fvm_ipld_encoding::{BytesDe, BytesSer}; +use fvm_shared::address::Address; use { super::memory::{copy_to_memory, get_memory_region}, @@ -153,9 +154,8 @@ pub fn call>( precompiles::Precompiles::call_precompile(platform.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? } else { - let dst_addr = EthAddress::try_from(dst)? - .as_id_address() - .ok_or_else(|| StatusCode::BadAddress("not an actor id address".to_string()))?; + let dst_addr: EthAddress = dst.try_into()?; + let dst_addr: Address = dst_addr.try_into()?; let call_result = match kind { CallKind::Call => platform.send( @@ -225,9 +225,8 @@ pub fn callactor>( .map_err(|_| StatusCode::InvalidMemoryAccess)?; let result = { - let dst_addr = EthAddress::try_from(dst)? - .as_id_address() - .ok_or_else(|| StatusCode::BadAddress(format!("not an actor id address: {}", dst)))?; + let dst_addr: EthAddress = dst.try_into()?; + let dst_addr: Address = dst_addr.try_into()?; if method.bits() > 64 { return Err(StatusCode::ArgumentOutOfRange(format!("bad method number: {}", method))); diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 5f1dee2b7..5ab2051f7 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,5 +1,4 @@ use { - crate::interpreter::address::EthAddress, crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, @@ -39,10 +38,9 @@ pub fn caller<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn address<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + _platform: &'r System<'r, BS, RT>, ) { - let id = platform.rt.message().receiver().id().unwrap(); - state.stack.push(EthAddress::from_id(id).as_evm_word()) + state.stack.push(state.receiver.as_evm_word()) } #[inline] @@ -50,8 +48,10 @@ pub fn origin<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, platform: &'r System<'r, BS, RT>, ) { - let id = platform.rt.message().origin().id().unwrap(); - state.stack.push(EthAddress::from_id(id).as_evm_word()) + let origin_addr = platform + .resolve_ethereum_address(&platform.rt.message().origin()) + .expect("failed to resolve origin address"); + state.stack.push(origin_addr.as_evm_word()) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index da909a72a..481c415a5 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -4,7 +4,7 @@ use crate::U256; use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::ActorError; -use fvm_shared::econ::TokenAmount; +use fvm_shared::{address::Address, econ::TokenAmount}; use num_traits::Zero; use { crate::interpreter::{ExecutionState, StatusCode, System}, @@ -61,12 +61,16 @@ fn get_evm_bytecode_cid>( rt: &RT, addr: U256, ) -> Result { - let addr = EthAddress::try_from(addr)? - .as_id_address() - .ok_or_else(|| StatusCode::BadAddress("no support for non-ID addresses yet".to_string()))?; + let addr: EthAddress = addr.try_into()?; + let addr: Address = addr.try_into()?; + // TODO: just return none in most of these cases? + let actor_id = rt.resolve_address(&addr).ok_or_else(|| { + StatusCode::InvalidArgument("failed to resolve address".to_string()) + // TODO better error code + })?; let evm_cid = rt.get_code_cid_for_type(Type::EVM); - let target_cid = rt.get_actor_code_cid(&addr.id().expect("not an ID address")); + let target_cid = rt.get_actor_code_cid(&actor_id); if Some(evm_cid) != target_cid { return Err(StatusCode::InvalidArgument( diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index d9e97d037..7048ed6c9 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -145,8 +145,9 @@ pub fn selfdestruct>( state: &mut ExecutionState, _system: &mut System, ) -> Result<(), StatusCode> { - let beneficiary_addr = EthAddress::try_from(state.stack.pop())?; - let id_addr = beneficiary_addr.as_id_address().expect("no support for non-ID addresses yet"); - state.selfdestroyed = Some(id_addr); + let beneficiary_addr = state.stack.pop(); + // TODO: how do we handle errors here? Just ignore them? + state.selfdestroyed = + beneficiary_addr.try_into().and_then(|addr: EthAddress| addr.try_into()).ok(); Ok(()) } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index dba197243..05089322b 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -1,3 +1,5 @@ +use fvm_shared::address::Address; + use crate::U256; use { crate::interpreter::address::EthAddress, @@ -13,9 +15,11 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( ) -> Result<(), StatusCode> { let actor = state.stack.pop(); - let balance = EthAddress::try_from(actor) + let balance = actor + .try_into() + .and_then(|addr: EthAddress| addr.try_into()) .ok() - .and_then(|addr| addr.as_id()) + .and_then(|addr: Address| platform.rt.resolve_address(&addr)) .and_then(|id| platform.rt.actor_balance(id).as_ref().map(U256::from)) .unwrap_or_default(); diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 008ce5dde..3a626199f 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -67,10 +67,13 @@ fn test_call() { MockRuntime::default(); // create a mock target and proxy a call through the proxy - let target = FILAddress::new_id(0x100); + 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.add_delegated_address(target, f4_target); - let evm_target = EthAddress::from_id_address(&target).unwrap(); let evm_target_word = evm_target.as_evm_word(); // dest + method 0 with no data @@ -86,7 +89,7 @@ fn test_call() { return_data[31] = 0x42; rt.expect_send( - target, + f4_target, evm::Method::InvokeContract as u64, proxy_call_input_data, TokenAmount::zero(), @@ -183,10 +186,11 @@ fn test_callactor() { let mut rt = util::construct_and_verify(contract); // create a mock target and proxy a call through the proxy - let target = FILAddress::new_id(0x100); + let target_id = 0x100; + let target = FILAddress::new_id(target_id); rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); - let evm_target = EthAddress::from_id_address(&target).unwrap(); + let evm_target = EthAddress::from_id(target_id); let evm_target_word = evm_target.as_evm_word(); // dest + method 0x42 with no data diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index 721f960c4..b795e377b 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -2,9 +2,9 @@ mod asm; mod util; use cid::Cid; -use evm::interpreter::U256; +use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; -use fvm_shared::econ::TokenAmount; +use fvm_shared::{address::Address, econ::TokenAmount}; #[test] fn test_timestamp() { @@ -222,3 +222,126 @@ return 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/storage_footprint.rs b/actors/evm/tests/storage_footprint.rs index 4802d9cc0..ce49aa8b3 100644 --- a/actors/evm/tests/storage_footprint.rs +++ b/actors/evm/tests/storage_footprint.rs @@ -8,6 +8,7 @@ use ethers::providers::{MockProvider, Provider}; use fil_actor_evm::interpreter::address::EthAddress; use fvm_ipld_blockstore::tracking::BSStats as BlockstoreStats; use fvm_shared::address::Address; +use fvm_shared::ActorID; mod env; @@ -28,12 +29,13 @@ abigen!(StorageFootprint, "./tests/contracts/StorageFootprint.abi"); // ``` // The owner doesn't matter in these tests, so just using the same value that the other tests use, for everything. -const OWNER: Address = Address::new_id(100); +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_address(&OWNER).unwrap(); + 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(); diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 5dddb722b..ebb3d2dde 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,6 +1,6 @@ use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; -use fil_actors_runtime::{runtime::builtins::Type, test_utils::*, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{runtime::builtins::Type, test_utils::*, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; use fvm_shared::address::Address; @@ -9,6 +9,9 @@ pub fn construct_and_verify(initcode: Vec) -> MockRuntime { init_construct_and_verify(initcode, |_| {}) } +pub const CONTRACT_ADDRESS: [u8; 20] = + hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"); + pub fn init_construct_and_verify( initcode: Vec, initrt: F, @@ -23,8 +26,7 @@ pub fn init_construct_and_verify( // first actor created is 0 rt.add_delegated_address( Address::new_id(0), - Address::new_delegated(10, &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000")) - .unwrap(), + Address::new_delegated(EAM_ACTOR_ID, &CONTRACT_ADDRESS).unwrap(), ); let params = evm::ConstructorParams { From 19bfc09691a9ae233d88e902de2076908dacf399 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 21 Oct 2022 00:32:47 +0300 Subject: [PATCH 084/339] EVM: implement call variants: STATICCALL and DELEGATECALL (#768) * implement STATICCALL Also, consistently rename platform argument to system. * implement DELEGATECALL * rustfmt * move readonly check in flush * shortcut reload in read only mode * dont store the delegate bytecode * input parameters for delegate call are raw bytes * fix caller validation check * dedup bytecode loading code * cliptarded * WIP: callvariant tests * callvariant tests * assert actor type * fix tests * fix test contract: uncomment accidentally commented dispatch code * fix contract bugz * STATICCALL tests * fix contract bugz * delegate call tests * rustfmt * fix: handle empty bytecode on delegatecall and refactor a bit * chore: rename delegate -> with_code * fix clippy Co-authored-by: Steven Allen --- .../evm/src/interpreter/instructions/call.rs | 57 ++- .../src/interpreter/instructions/context.rs | 44 +-- .../evm/src/interpreter/instructions/ext.rs | 18 +- .../src/interpreter/instructions/lifecycle.rs | 29 +- .../evm/src/interpreter/instructions/state.rs | 10 +- .../src/interpreter/instructions/storage.rs | 12 +- actors/evm/src/interpreter/system.rs | 44 ++- actors/evm/src/lib.rs | 64 ++- actors/evm/tests/contracts/callvariants.eas | 19 + actors/evm/tests/contracts/callvariants.hex | 1 + .../evm/tests/contracts/callvariants_body.eas | 312 +++++++++++++++ test_vm/tests/evm_test.rs | 372 +++++++++++++++++- 12 files changed, 895 insertions(+), 87 deletions(-) create mode 100644 actors/evm/tests/contracts/callvariants.eas create mode 100644 actors/evm/tests/contracts/callvariants.hex create mode 100644 actors/evm/tests/contracts/callvariants_body.eas diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 7edc185b5..a372ff752 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -12,7 +12,7 @@ use { crate::interpreter::System, crate::interpreter::U256, crate::RawBytes, - crate::{Method, EVM_CONTRACT_REVERTED}, + crate::{DelegateCallParams, Method, EVM_CONTRACT_REVERTED}, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, fvm_shared::econ::TokenAmount, @@ -110,7 +110,7 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod pub fn call>( state: &mut ExecutionState, - platform: &mut System, + system: &mut System, kind: CallKind, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; @@ -151,26 +151,54 @@ pub fn call>( }; if precompiles::Precompiles::::is_precompile(&dst) { - precompiles::Precompiles::call_precompile(platform.rt, dst, input_data) + precompiles::Precompiles::call_precompile(system.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? } else { let dst_addr: EthAddress = dst.try_into()?; let dst_addr: Address = dst_addr.try_into()?; let call_result = match kind { - CallKind::Call => platform.send( + CallKind::Call => system.send( &dst_addr, - Method::InvokeContract as u64, + // readonly is sticky + if system.readonly { + Method::InvokeContractReadOnly + } else { + Method::InvokeContract + } as u64, // TODO: support IPLD codecs #758 RawBytes::serialize(BytesSer(input_data))?, TokenAmount::from(&value), ), + CallKind::DelegateCall => { - todo!() - } - CallKind::StaticCall => { - todo!() + // first invoke GetBytecode to get the code CID from the target + let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( + system.rt, dst, + )?; + + // and then invoke self with delegate; readonly context is sticky + let params = DelegateCallParams { + code, + input: input_data.to_vec(), + readonly: system.readonly, + }; + system.send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + RawBytes::serialize(¶ms)?, + TokenAmount::from(&value), + ) } + + CallKind::StaticCall => system.send( + &dst_addr, + Method::InvokeContractReadOnly as u64, + // TODO: support IPLD codecs #758 + RawBytes::serialize(BytesSer(input_data))?, + TokenAmount::from(&value), + ), + CallKind::CallCode => { todo!() } @@ -205,10 +233,17 @@ pub fn call>( pub fn callactor>( state: &mut ExecutionState, - platform: &System, + system: &System, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; - let rt = &*platform.rt; // as immutable reference + let rt = &*system.rt; // as immutable reference + + // TODO Until we support readonly (static) calls at the fvm level, we disallow callactor + // when in static mode as it is sticky and there are no guarantee of preserving the + // static invariant + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } // stack: GAS DEST VALUE METHODNUM INPUT-OFFSET INPUT-SIZE // NOTE: we don't need output-offset/output-size (which the CALL instructions have) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 5ab2051f7..bb13b4f6a 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -8,14 +8,14 @@ use { #[inline] pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let bn = state.stack.pop(); if bn.bits() > 8 { return Err(StatusCode::ArgumentOutOfRange(format!("invalid epoch lookback: {}", bn))); } let epoch = bn.as_u64() as i64; - if let Some(cid) = platform.rt.tipset_cid(epoch) { + if let Some(cid) = system.rt.tipset_cid(epoch) { let mut hash = cid.hash().digest(); if hash.len() > 32 { hash = &hash[..32] @@ -38,7 +38,7 @@ pub fn caller<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn address<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, ) { state.stack.push(state.receiver.as_evm_word()) } @@ -46,10 +46,10 @@ pub fn address<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn origin<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - let origin_addr = platform - .resolve_ethereum_address(&platform.rt.message().origin()) + let origin_addr = system + .resolve_ethereum_address(&system.rt.message().origin()) .expect("failed to resolve origin address"); state.stack.push(origin_addr.as_evm_word()) } @@ -57,15 +57,15 @@ pub fn origin<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn call_value<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(&platform.rt.message().value_received())); + state.stack.push(U256::from(&system.rt.message().value_received())); } #[inline] pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, ) { // TODO do we want to return the zero ID address, or just a plain 0? state.stack.push(U256::zero()) @@ -74,40 +74,40 @@ pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn gas_price<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - let effective_price = platform.rt.base_fee() + platform.rt.message().gas_premium(); + let effective_price = system.rt.base_fee() + system.rt.message().gas_premium(); state.stack.push(U256::from(&effective_price)); } #[inline] pub fn gas<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.gas_available())); + state.stack.push(U256::from(system.rt.gas_available())); } #[inline] pub fn timestamp<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.tipset_timestamp())); + state.stack.push(U256::from(system.rt.tipset_timestamp())); } #[inline] pub fn block_number<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(platform.rt.curr_epoch())); + state.stack.push(U256::from(system.rt.curr_epoch())); } #[inline] pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, ) { state.stack.push(U256::zero()); } @@ -115,7 +115,7 @@ pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, ) { const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; state.stack.push(U256::from(BLOCK_GAS_LIMIT)); @@ -124,7 +124,7 @@ pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, ) { state.stack.push(U256::from(chainid::CHAINID)); } @@ -132,7 +132,7 @@ pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn base_fee<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { - state.stack.push(U256::from(&platform.rt.base_fee())) + state.stack.push(U256::from(&system.rt.base_fee())) } diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 481c415a5..49564a709 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -15,14 +15,14 @@ use { #[inline] pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let addr = state.stack.pop(); // TODO 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 = get_evm_bytecode_cid(platform.rt, addr) - .and_then(|cid| get_evm_bytecode(platform.rt, &cid)) + let len = get_evm_bytecode_cid(system.rt, addr) + .and_then(|cid| get_evm_bytecode(system.rt, &cid)) .map(|bytecode| bytecode.len())?; state.stack.push(len.into()); @@ -31,10 +31,10 @@ pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let addr = state.stack.pop(); - let cid = get_evm_bytecode_cid(platform.rt, addr)?; + let cid = get_evm_bytecode_cid(system.rt, addr)?; let digest = cid.hash().digest(); // Take the first 32 bytes of the Multihash let digest_len = digest.len().min(32); @@ -44,20 +44,20 @@ pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let ExecutionState { stack, .. } = state; let (addr, dest_offset, data_offset, size) = (stack.pop(), stack.pop(), stack.pop(), stack.pop()); - let bytecode = get_evm_bytecode_cid(platform.rt, addr) - .and_then(|cid| get_evm_bytecode(platform.rt, &cid))?; + let bytecode = + get_evm_bytecode_cid(system.rt, addr).and_then(|cid| get_evm_bytecode(system.rt, &cid))?; copy_to_memory(&mut state.memory, dest_offset, size, data_offset, bytecode.as_slice())?; Ok(()) } -fn get_evm_bytecode_cid>( +pub fn get_evm_bytecode_cid>( rt: &RT, addr: U256, ) -> Result { diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 7048ed6c9..21efdcd12 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -42,8 +42,12 @@ pub struct EamReturn { #[inline] pub fn create>( state: &mut ExecutionState, - platform: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } + let ExecutionState { stack, memory, .. } = state; let value = stack.pop(); @@ -61,15 +65,19 @@ pub fn create>( ))); }; - let nonce = platform.increment_nonce(); + let nonce = system.increment_nonce(); let params = CreateParams { code: input_data.to_vec(), nonce }; - create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) + create_init(stack, system, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) } pub fn create2>( state: &mut ExecutionState, - platform: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } + let ExecutionState { stack, memory, .. } = state; // see `create()` overall TODOs @@ -94,20 +102,20 @@ pub fn create2>( }; let params = Create2Params { code: input_data.to_vec(), salt }; - platform.increment_nonce(); - create_init(stack, platform, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) + system.increment_nonce(); + create_init(stack, system, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) } /// call into Ethereum Address Manager to make the new account fn create_init>( stack: &mut Stack, - platform: &mut System, + system: &mut System, params: RawBytes, method: MethodNum, value: TokenAmount, ) -> Result<(), StatusCode> { // send bytecode & params to EAM to generate the address and contract - let ret = platform.send(&EAM_ACTOR_ADDR, method, params, value); + let ret = system.send(&EAM_ACTOR_ADDR, method, params, value); // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 // Normally EVM will do some checks here to ensure that a contract has the capability @@ -143,8 +151,11 @@ fn create_init>( #[inline] pub fn selfdestruct>( state: &mut ExecutionState, - _system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } let beneficiary_addr = state.stack.pop(); // TODO: how do we handle errors here? Just ignore them? state.selfdestroyed = diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 05089322b..e30559dbb 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -11,7 +11,7 @@ use { #[inline] pub fn balance<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let actor = state.stack.pop(); @@ -19,8 +19,8 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( .try_into() .and_then(|addr: EthAddress| addr.try_into()) .ok() - .and_then(|addr: Address| platform.rt.resolve_address(&addr)) - .and_then(|id| platform.rt.actor_balance(id).as_ref().map(U256::from)) + .and_then(|addr: Address| system.rt.resolve_address(&addr)) + .and_then(|id| system.rt.actor_balance(id).as_ref().map(U256::from)) .unwrap_or_default(); state.stack.push(balance); @@ -30,9 +30,9 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( #[inline] pub fn selfbalance<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, - platform: &'r System<'r, BS, RT>, + system: &'r System<'r, BS, RT>, ) { // Returns native FIL balance of the receiver. Value precision is identical to Ethereum, so // no conversion needed (atto, 1e18). - state.stack.push(U256::from(&platform.rt.current_balance())) + state.stack.push(U256::from(&system.rt.current_balance())) } diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 0ea3e1b61..371eaae60 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -7,13 +7,13 @@ use { #[inline] pub fn sload>( state: &mut ExecutionState, - platform: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { // where? let location = state.stack.pop(); // get from storage and place on stack - let value = match platform.get_storage(location)? { + let value = match system.get_storage(location)? { Some(val) => val, None => U256::zero(), }; @@ -24,12 +24,16 @@ pub fn sload>( #[inline] pub fn sstore>( state: &mut ExecutionState, - platform: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } + let location = state.stack.pop(); let value = state.stack.pop(); let opt_value = if value == U256::zero() { None } else { Some(value) }; - platform.set_storage(location, opt_value)?; + system.set_storage(location, opt_value)?; Ok(()) } diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 9cac14539..e97f4f69d 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -50,6 +50,8 @@ pub struct System<'r, BS: Blockstore, RT: Runtime> { 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, } impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { @@ -69,11 +71,12 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { nonce: 1, saved_state_root: None, bytecode: None, + readonly: false, }) } /// Load the actor from state. - pub fn load(rt: &'r mut RT) -> Result + pub fn load(rt: &'r mut RT, readonly: bool) -> Result where BS: Clone, { @@ -91,6 +94,7 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { nonce: state.nonce, saved_state_root: Some(state_root), bytecode: Some(state.bytecode), + readonly, }) } @@ -120,6 +124,11 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { if self.saved_state_root.is_some() { return Ok(()); } + + if self.readonly { + return Err(ActorError::forbidden("contract invocation is read only".to_string())); + } + let bytecode_cid = match self.bytecode { Some(cid) => cid, None => self.set_bytecode(&[])?, @@ -147,10 +156,15 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { /// 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() @@ -169,21 +183,7 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { /// Load the bytecode. pub fn load_bytecode(&self) -> Result, ActorError> { - match &self.bytecode { - Some(cid) => { - let bytecode = self - .rt - .store() - .get(cid) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to read state")? - .expect("bytecode not in state tree"); - if bytecode.is_empty() { - return Ok(None); - } - Ok(Some(Bytecode::new(bytecode))) - } - None => Ok(None), - } + Ok(self.bytecode.as_ref().map(|k| load_bytecode(self.rt.store(), k)).transpose()?.flatten()) } /// Set the bytecode. @@ -278,3 +278,15 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { } } } + +pub fn load_bytecode(bs: &BS, 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))) + } +} diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 91bbb2d97..8ed98717e 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,9 +1,9 @@ use std::iter; use fil_actors_runtime::{runtime::builtins::Type, EAM_ACTOR_ID}; -use fvm_ipld_encoding::{BytesDe, BytesSer}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; use fvm_shared::address::{Address, Payload}; -use interpreter::address::EthAddress; +use interpreter::{address::EthAddress, system::load_bytecode}; pub mod interpreter; mod state; @@ -43,6 +43,8 @@ pub enum Method { InvokeContract = 2, GetBytecode = 3, GetStorageAt = 4, + InvokeContractReadOnly = 5, + InvokeContractDelegate = 6, } pub struct EvmContractActor; @@ -142,20 +144,30 @@ impl EvmContractActor { rt: &mut RT, method: u64, input_data: &[u8], + readonly: bool, + with_code: Option, ) -> Result, ActorError> where BS: Blockstore + Clone, RT: Runtime, { - rt.validate_immediate_caller_accept_any()?; + if with_code.is_some() { + rt.validate_immediate_caller_is(&[rt.message().receiver()])?; + } else { + rt.validate_immediate_caller_accept_any()?; + } - let mut system = System::load(rt).map_err(|e| { + let mut system = System::load(rt, readonly).map_err(|e| { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; - let bytecode = match system.load_bytecode()? { + let bytecode = match match with_code { + Some(cid) => load_bytecode(system.rt.store(), &cid), + None => system.load_bytecode(), + }? { Some(bytecode) => bytecode, - None => return Ok(Vec::new()), // an EVM contract with no code returns immediately + // an EVM contract with no code returns immediately + None => return Ok(Vec::new()), }; // Resolve the caller's ethereum address. If the caller doesn't have one, the caller's ID is used instead. @@ -224,7 +236,7 @@ impl EvmContractActor { // access arbitrary storage keys from a contract. rt.validate_immediate_caller_is([&Address::new_id(0)])?; - System::load(rt)? + System::load(rt, true)? .get_storage(params.storage_key) .map_err(|st| ActorError::unspecified(format!("failed to get storage key: {}", &st)))? .ok_or_else(|| ActorError::not_found(String::from("storage key not found"))) @@ -248,7 +260,8 @@ impl ActorCode for EvmContractActor { } Some(Method::InvokeContract) => { let BytesDe(params) = params.deserialize()?; - let value = Self::invoke_contract(rt, Method::InvokeContract as u64, ¶ms)?; + let value = + Self::invoke_contract(rt, Method::InvokeContract as u64, ¶ms, false, None)?; Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::GetBytecode) => { @@ -259,8 +272,31 @@ impl ActorCode for EvmContractActor { let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(value)?) } + Some(Method::InvokeContractReadOnly) => { + let BytesDe(params) = params.deserialize()?; + let value = Self::invoke_contract( + rt, + Method::InvokeContractReadOnly as u64, + ¶ms, + true, + None, + )?; + Ok(RawBytes::serialize(BytesSer(&value))?) + } + Some(Method::InvokeContractDelegate) => { + let params: DelegateCallParams = cbor::deserialize_params(params)?; + let value = Self::invoke_contract( + rt, + Method::InvokeContractDelegate as u64, + ¶ms.input, + params.readonly, + Some(params.code), + )?; + Ok(RawBytes::serialize(BytesSer(&value))?) + } + // Otherwise, we take the bytes as CBOR. - None => Self::invoke_contract(rt, method, params).map(RawBytes::new), + None => Self::invoke_contract(rt, method, params, false, None).map(RawBytes::new), } } } @@ -273,6 +309,16 @@ pub struct ConstructorParams { pub initcode: RawBytes, } +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct DelegateCallParams { + pub code: Cid, + /// The contract invocation parameters + #[serde(with = "strict_bytes")] + pub input: Vec, + /// Whether the call is within a read only (static) call context + pub readonly: bool, +} + #[derive(Serialize_tuple, Deserialize_tuple)] pub struct GetStorageAtParams { pub storage_key: U256, 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..d364b864a --- /dev/null +++ b/actors/evm/tests/contracts/callvariants.hex @@ -0,0 +1 @@ +306000556101cf8060106000396000f360003560e01c80600114607557806002146101af5780600314609157806004146101bb578060051460ad578060061460cf578060071460ed578060081461010f578060091461012d5780600a146101495780600b1461016b5780600c1461012d5780600d1461018d5780600e1461014957600080fd5b60206000600260e01b600052600460006004356000fa60206000f35b60206000600460e01b600052600460006004356000fa60206000f35b60206000600660e01b600052602435600452602460006004356000fa60206000f35b60206000600260e01b6000526004600060006004356000f160206000f35b60206000600860e01b600052602435600452602460006004356000fa60206000f35b60206000600460e01b6000526004600060006004356000f160206000f35b60206000600260e01b600052600460006004356000f460206000f35b60206000600460e01b600052600460006004356000f460005460005260206000f35b60206000600c60e01b600052602435600452602460006004356000fa60206000f35b60206000600e60e01b600052602435600452602460006004356000fa60206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f3 \ 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..5862b9a57 --- /dev/null +++ b/actors/evm/tests/contracts/callvariants_body.eas @@ -0,0 +1,312 @@ +# 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 + +# 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) + +%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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 +push1 0x00 # call gas (ignored) +call # do it! +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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 +push1 0x00 # call gas (ignored) +call # do it! +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_delegatecall10(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 +push1 0x00 # delegate gas (ignored) +delegatecall # do it! +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 +push1 0x00 # delegate gas (ignored) +delegatecall # do it! +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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 +push1 0x00 # staticcall gas (ignored) +staticcall # do it! +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 diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 0e937ddc7..b1ed844bc 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use ethers::core::types::Address as EthAddress; use ethers::prelude::abigen; use ethers::providers::Provider; -use fil_actors_runtime::EAM_ACTOR_ADDR; +use fil_actors_runtime::{test_utils::EVM_ACTOR_CODE_ID, EAM_ACTOR_ADDR}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor}; -use fvm_shared::econ::TokenAmount; use fvm_shared::ActorID; +use fvm_shared::{address::Address, econ::TokenAmount}; use num_traits::Zero; use serde::{Deserialize, Serialize}; use test_vm::{util::create_accounts, VM}; @@ -86,3 +86,371 @@ fn test_evm_lifecycle() { .expect("failed to decode return"); assert_eq!(0, evm_ret, "expected contract to return 0 on success"); } + +#[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::Create2 as u64, + fil_actor_eam::Create2Params { initcode: bytecode.clone(), salt: [0u8; 32] }, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.error_message.unwrap() + ); + + let create_return: fil_actor_eam::Create2Return = + create_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!(!call_result.code.is_success(), "static call mutation succeeded"); + } + + // A -> staticcall -> B -> call -> C (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!(!call_result.code.is_success(), "static call mutation succeeded",); + } +} + +#[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::Create2 as u64, + fil_actor_eam::Create2Params { initcode: bytecode.clone(), salt: [0u8; 32] }, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.error_message.unwrap() + ); + + let create_return: fil_actor_eam::Create2Return = + create_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value[28..], &[0xff, 0xff, 0xff, 0x42]); + } +} + +#[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::Create2 as u64, + fil_actor_eam::Create2Params { initcode: bytecode.clone(), salt: [0u8; 32] }, + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.error_message.unwrap() + ); + + let create_return: fil_actor_eam::Create2Return = + create_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.error_message.unwrap() + ); + let BytesDe(return_value) = + call_result.ret.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; + 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, + ContractParams(params.to_vec()), + ) + .unwrap(); + assert!(!call_result.code.is_success(), "static call mutation succeeded",); + } +} From 0c64a7820eee7f29e0fad837066e6a6910c67dbe Mon Sep 17 00:00:00 2001 From: raulk Date: Sat, 22 Oct 2022 00:41:44 +0100 Subject: [PATCH 085/339] upgrade fvm_shared and fvm_sdk. (#774) --- Cargo.lock | 197 ++++++++++++++++++------------------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 18 files changed, 117 insertions(+), 118 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca157b66e..f44c95ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "arrayref" @@ -157,9 +157,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" dependencies = [ "proc-macro2", "quote", @@ -248,9 +248,9 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64ct" @@ -412,9 +412,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byte-slice-cast" @@ -698,9 +698,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2b443d17d49dad5ef0ede301c3179cc923b8822f3393b4d2c28c269dd4a122" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array 0.14.6", "rand_core", @@ -739,9 +739,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn", @@ -908,15 +908,15 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dunce" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" [[package]] name = "ecdsa" -version = "0.14.7" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85789ce7dfbd0f0624c07ef653a08bb2ebf43d3e16531361f46d36dd54334fed" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der", "elliptic-curve", @@ -1191,7 +1191,7 @@ checksum = "e46482e4d1e79b20c338fd9db9e166184eb387f0a4e7c05c5b5c0aa2e8c8900c" dependencies = [ "async-trait", "auto_impl", - "base64 0.13.0", + "base64 0.13.1", "ethers-core", "futures-core", "futures-timer", @@ -1297,9 +1297,9 @@ dependencies = [ [[package]] name = "fastrlp-derive" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fa41ebc231af281098b11ad4a4f6182ec9096902afffe948034a20d4e1385a" +checksum = "d9e9158c1d8f0a7a716c9191562eaabba70268ba64972ef4871ce8d66fd08872" dependencies = [ "bytes", "proc-macro2", @@ -1734,9 +1734,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -1749,9 +1749,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -1759,15 +1759,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -1776,9 +1776,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-lite" @@ -1808,9 +1808,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", @@ -1819,15 +1819,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-timer" @@ -1837,9 +1837,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -1946,9 +1946,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.5" +version = "3.0.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddbad8bddf3fcc961e8f2884d8eb5610485606f808b50955945dcd3cc5c31eb" +checksum = "2bceb744ba412c16af1040379e435f8a775dc93911f65d26ca4a7c67b6e3afb1" dependencies = [ "cid", "fvm_ipld_encoding", @@ -1961,9 +1961,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.5" +version = "3.0.0-alpha.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2a195a55012c943894f71f8ed505dd3efe73b13f2cab70fb9e4d335c63d0f3" +checksum = "c8bd40254c259463c977799f0e5d5b030d27d7b615c05941175e5ab4e72b67a6" dependencies = [ "anyhow", "blake2b_simd", @@ -2017,9 +2017,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "js-sys", @@ -2042,9 +2042,9 @@ dependencies = [ [[package]] name = "group" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7391856def869c1c81063a03457c676fbcd419709c3dfb33d8d319de484b154d" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", "rand_core", @@ -2302,9 +2302,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" @@ -2317,9 +2317,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3636d281d46c3b64182eb3a0a42b7b483191a2ecc3f05301fa67403f7c9bc949" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", "ecdsa", @@ -2354,9 +2354,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.134" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libipld-core" @@ -2395,7 +2395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64 0.13.0", + "base64 0.13.1", "digest 0.9.0", "libsecp256k1-core", "libsecp256k1-gen-ecmult", @@ -2768,9 +2768,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ "thiserror", "ucd-trie", @@ -2778,9 +2778,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1" +checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" dependencies = [ "pest", "pest_generator", @@ -2788,9 +2788,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c" +checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" dependencies = [ "pest", "pest_meta", @@ -2801,9 +2801,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6" +checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" dependencies = [ "once_cell", "pest", @@ -2932,9 +2932,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -3022,7 +3022,7 @@ version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bytes", "encoding_rs", "futures-core", @@ -3092,9 +3092,9 @@ dependencies = [ [[package]] name = "rlp" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", "rustc-hex", @@ -3139,9 +3139,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" dependencies = [ "log", "ring", @@ -3155,7 +3155,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] @@ -3248,9 +3248,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] @@ -3285,9 +3285,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", @@ -3308,9 +3308,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa", "ryu", @@ -3410,9 +3410,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2904bea16a1ae962b483322a1c7b81d976029203aea1f461e51cd7705db7ba9" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ "digest 0.10.5", "keccak", @@ -3420,9 +3420,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.6.3" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb766570a2825fa972bceff0d195727876a9cdf2460ab2e52d455dc2de47fd9" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ "digest 0.10.5", "rand_core", @@ -3439,15 +3439,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snafu" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" +checksum = "a152ba99b054b22972ee794cf04e5ef572da1229e33b65f3c57abbff0525a454" dependencies = [ "doc-comment", "snafu-derive", @@ -3455,9 +3455,9 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" +checksum = "d5e79cdebbabaebb06a9bdbaedc7f159b410461f63611d4d0e3fb0fab8fed850" dependencies = [ "heck", "proc-macro2", @@ -3546,9 +3546,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -3678,9 +3678,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", "bytes", @@ -3688,7 +3688,6 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "pin-project-lite", "socket2", "winapi", @@ -3736,9 +3735,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -3748,9 +3747,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -3759,9 +3758,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] @@ -3814,9 +3813,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" @@ -4018,9 +4017,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.4" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki", ] diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 7b128d901..32ddf46b8 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index f31448b75..72dbc10d3 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index e2dc4c23e..92e3ee82d 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 095e89b17..b3121a77e 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.5", optional = true } -fvm_shared = { version = "3.0.0-alpha.5", optional = true } +fvm_sdk = { version = "3.0.0-alpha.7", optional = true } +fvm_shared = { version = "3.0.0-alpha.6", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 13d3d667e..d57f550b9 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index d12105b72..c35495859 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 39af159b7..1daf002dc 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_bitfield = "0.5.4" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 142658f88..f028d6b08 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.0" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 360cdaf09..ed0598516 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index a7aa1a666..45bdfe155 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 5cd684f63..a87d2b1f8 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 0ea16cbc8..1dcf7ce76 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 9bfc688be..481396915 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 13f29fbbf..cab207a63 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 72a889c0b..5d9911159 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.0" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.5", optional = true } +fvm_sdk = { version = "3.0.0-alpha.7", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index 6f033e13e..1aaaa8c36 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 676271035..0d38b4623 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner" } fil_actor_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } fil_actor_eam = { version = "10.0.0-alpha.1", path = "../actors/eam" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.5", default-features = false } +fvm_shared = { version = "3.0.0-alpha.6", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.4" From b5ee962d3fd412d801801ed45cd37e353139509c Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 22 Oct 2022 02:53:51 +0300 Subject: [PATCH 086/339] Transfer value in embryonic actor calls (#773) --- .../evm/src/interpreter/instructions/call.rs | 110 ++++++++++++------ runtime/src/test_utils.rs | 9 ++ 2 files changed, 81 insertions(+), 38 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index a372ff752..f9d15b294 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,5 +1,6 @@ use fvm_ipld_encoding::{BytesDe, BytesSer}; use fvm_shared::address::Address; +use fvm_shared::address::Protocol as AddressProtocol; use { super::memory::{copy_to_memory, get_memory_region}, @@ -13,6 +14,7 @@ use { crate::interpreter::U256, crate::RawBytes, crate::{DelegateCallParams, Method, EVM_CONTRACT_REVERTED}, + fil_actors_runtime::runtime::builtins::Type, fil_actors_runtime::runtime::Runtime, fvm_ipld_blockstore::Blockstore, fvm_shared::econ::TokenAmount, @@ -139,6 +141,11 @@ pub fn call>( ), }; + if system.readonly && value > U256::zero() { + // non-zero sends are side-effects and hence a static mode violation + return Err(StatusCode::StaticModeViolation); + } + let input_region = get_memory_region(memory, input_offset, input_size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -157,50 +164,77 @@ pub fn call>( let dst_addr: EthAddress = dst.try_into()?; let dst_addr: Address = dst_addr.try_into()?; - let call_result = match kind { - CallKind::Call => system.send( - &dst_addr, - // readonly is sticky - if system.readonly { - Method::InvokeContractReadOnly - } else { - Method::InvokeContract - } as u64, - // TODO: support IPLD codecs #758 - RawBytes::serialize(BytesSer(input_data))?, - TokenAmount::from(&value), - ), - - CallKind::DelegateCall => { - // first invoke GetBytecode to get the code CID from the target - let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( - system.rt, dst, - )?; - - // and then invoke self with delegate; readonly context is sticky - let params = DelegateCallParams { - code, - input: input_data.to_vec(), - readonly: system.readonly, - }; - system.send( - &system.rt.message().receiver(), - Method::InvokeContractDelegate as u64, - RawBytes::serialize(¶ms)?, - TokenAmount::from(&value), - ) + // Special casing for embryo/non-existent actors: we just do a SEND (method 0) + // which allows us to transfer funds (and create embryos) + let is_embryonic = if dst_addr.protocol() == AddressProtocol::ID { + // sanity check: this shouldn't be an ID address, as you can't predict + // what actor is gonna sit there. + false + } else if let Some(actor_id) = system.rt.resolve_address(&dst_addr) { + if let Some(cid) = system.rt.get_actor_code_cid(&actor_id) { + system.rt.resolve_builtin_actor_type(&cid) == Some(Type::Embryo) + } else { + true } + } else { + true + }; - CallKind::StaticCall => system.send( + let call_result = if is_embryonic { + system.send( &dst_addr, - Method::InvokeContractReadOnly as u64, - // TODO: support IPLD codecs #758 + 0, + // we still send the input, even thought it will be ignored, for debugging + // purposes RawBytes::serialize(BytesSer(input_data))?, TokenAmount::from(&value), - ), + ) + } else { + match kind { + CallKind::Call => system.send( + &dst_addr, + // readonly is sticky + if system.readonly { + Method::InvokeContractReadOnly + } else { + Method::InvokeContract + } as u64, + // TODO: support IPLD codecs #758 + RawBytes::serialize(BytesSer(input_data))?, + TokenAmount::from(&value), + ), + + CallKind::DelegateCall => { + // first invoke GetBytecode to get the code CID from the target + let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( + system.rt, dst, + )?; + + // and then invoke self with delegate; readonly context is sticky + let params = DelegateCallParams { + code, + input: input_data.to_vec(), + readonly: system.readonly, + }; + system.send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + RawBytes::serialize(¶ms)?, + TokenAmount::from(&value), + ) + } + + CallKind::StaticCall => system.send( + &dst_addr, + Method::InvokeContractReadOnly as u64, + // TODO: support IPLD codecs #758 + RawBytes::serialize(BytesSer(input_data))?, + TokenAmount::from(&value), + ), - CallKind::CallCode => { - todo!() + CallKind::CallCode => { + todo!() + } } }; match call_result { diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 8ebc6a93a..2d9cb78d6 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -120,6 +120,7 @@ pub struct MockRuntime { pub base_fee: TokenAmount, pub id_addresses: HashMap, pub delegated_addresses: HashMap, + pub delegated_addresses_source: HashMap, pub actor_code_cids: HashMap, pub new_actor_addr: Option
, pub receiver: Address, @@ -300,6 +301,7 @@ impl MockRuntime { base_fee: Default::default(), id_addresses: Default::default(), delegated_addresses: Default::default(), + delegated_addresses_source: Default::default(), actor_code_cids: Default::default(), new_actor_addr: Default::default(), receiver: Address::new_id(0), @@ -506,6 +508,7 @@ impl MockRuntime { ); assert_eq!(source.protocol(), Protocol::ID, "source must use ID address protocol"); self.delegated_addresses.insert(source, target); + self.delegated_addresses_source.insert(target, source); } pub fn call( @@ -910,6 +913,12 @@ impl Runtime> for MockRuntime { if let &Payload::ID(id) = address.payload() { return Some(id); } + if Protocol::Delegated == address.protocol() { + return self + .delegated_addresses_source + .get(address) + .and_then(|a| self.resolve_address(a)); + } match self.get_id_address(address) { None => None, From cac19816ccef335d7ab2c7b876964c21d53d2681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sat, 22 Oct 2022 16:04:45 +0100 Subject: [PATCH 087/339] upgrade to fvm_shared@3.0.0-alpha.8. --- Cargo.lock | 4 ++-- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f44c95ae9..0ac176dc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1961,9 +1961,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.7" +version = "3.0.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8bd40254c259463c977799f0e5d5b030d27d7b615c05941175e5ab4e72b67a6" +checksum = "d9f0722cf399cfde0e2c01f9069d74da758ed01aa859c2f8be0e153bd557ecc9" dependencies = [ "anyhow", "blake2b_simd", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 32ddf46b8..239e40d79 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 72dbc10d3..73b928f5a 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 92e3ee82d..fc8978d80 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index b3121a77e..669d3cabe 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fvm_sdk = { version = "3.0.0-alpha.7", optional = true } -fvm_shared = { version = "3.0.0-alpha.6", optional = true } +fvm_shared = { version = "3.0.0-alpha.8", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index d57f550b9..b3083ce92 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index c35495859..fcabd3d0a 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 1daf002dc..e5b7458cb 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_bitfield = "0.5.4" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index f028d6b08..1bcc164ec 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.0" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index ed0598516..4c982d500 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 45bdfe155..3e0b06a46 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index a87d2b1f8..313a17a34 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 1dcf7ce76..e52cbc02e 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 481396915..493a3d037 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index cab207a63..6590c293d 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["fil-actor"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5d9911159..fb1f33d92 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.0" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/state/Cargo.toml b/state/Cargo.toml index 1aaaa8c36..018b6fdc0 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -26,7 +26,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 0d38b4623..3a71333b3 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner" } fil_actor_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } fil_actor_eam = { version = "10.0.0-alpha.1", path = "../actors/eam" } lazy_static = "1.4.0" -fvm_shared = { version = "3.0.0-alpha.6", default-features = false } +fvm_shared = { version = "3.0.0-alpha.8", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_bitfield = "0.5.4" From 138cc917f86fdaff117715803ab34451b3c250d0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Oct 2022 11:48:25 +0100 Subject: [PATCH 088/339] fix: allow calling more non-contract actors (#775) * fix: allow calling more non-contract actors - Allow calling accounts. - Allow calling embryos by ID. And oxidize it a bit. * fix: really fix contract calling rules 1. Don't do any extra work for dynamic calls. 2. Avoid creating new actors unless we're sending value (EVM rules). 3. Correctly identify "non-existent" actors. 4. Correctly handle "empty" returns. * test: check send/invoke logic --- .../evm/src/interpreter/instructions/call.rs | 142 +++++++++--------- actors/evm/tests/call.rs | 72 ++++++++- 2 files changed, 142 insertions(+), 72 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index f9d15b294..415b92869 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,6 +1,5 @@ use fvm_ipld_encoding::{BytesDe, BytesSer}; -use fvm_shared::address::Address; -use fvm_shared::address::Protocol as AddressProtocol; +use fvm_shared::{address::Address, METHOD_SEND}; use { super::memory::{copy_to_memory, get_memory_region}, @@ -161,87 +160,88 @@ pub fn call>( precompiles::Precompiles::call_precompile(system.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? } else { - let dst_addr: EthAddress = dst.try_into()?; - let dst_addr: Address = dst_addr.try_into()?; - - // Special casing for embryo/non-existent actors: we just do a SEND (method 0) - // which allows us to transfer funds (and create embryos) - let is_embryonic = if dst_addr.protocol() == AddressProtocol::ID { - // sanity check: this shouldn't be an ID address, as you can't predict - // what actor is gonna sit there. - false - } else if let Some(actor_id) = system.rt.resolve_address(&dst_addr) { - if let Some(cid) = system.rt.get_actor_code_cid(&actor_id) { - system.rt.resolve_builtin_actor_type(&cid) == Some(Type::Embryo) - } else { - true - } - } else { - true - }; - - let call_result = if is_embryonic { - system.send( - &dst_addr, - 0, - // we still send the input, even thought it will be ignored, for debugging - // purposes - RawBytes::serialize(BytesSer(input_data))?, - TokenAmount::from(&value), - ) - } else { - match kind { - CallKind::Call => system.send( - &dst_addr, - // readonly is sticky - if system.readonly { - Method::InvokeContractReadOnly + let call_result = match kind { + CallKind::Call | CallKind::StaticCall => { + let dst_addr: EthAddress = dst.try_into()?; + let dst_addr: Address = dst_addr.try_into()?; + + // Special casing for account/embryo/non-existent actors: we just do a SEND (method 0) + // which allows us to transfer funds (and create embryos) + let target_actor_code = system + .rt + .resolve_address(&dst_addr) + .and_then(|actor_id| system.rt.get_actor_code_cid(&actor_id)); + let target_actor_type = target_actor_code + .as_ref() + .and_then(|cid| system.rt.resolve_builtin_actor_type(cid)); + let actor_exists = target_actor_code.is_some(); + + if !actor_exists && value.is_zero() { + // If the actor doesn't exist and we're not sending value, return with + // "success". The EVM only auto-creates actors when sending value. + // + // NOTE: this will also apply if we're in read-only mode, because we can't + // send value in read-only mode anyways. + Ok(RawBytes::default()) + } else { + let method = if !actor_exists + || matches!(target_actor_type, Some(Type::Embryo | Type::Account)) + { + // If the target actor doesn't exist or is an account or an embryo, + // switch to a basic "send" so the call will still work even if the + // target actor would reject a normal ethereum call. + METHOD_SEND + } else if system.readonly || kind == CallKind::StaticCall { + // Invoke, preserving read-only mode. + Method::InvokeContractReadOnly as u64 } else { - Method::InvokeContract - } as u64, - // TODO: support IPLD codecs #758 - RawBytes::serialize(BytesSer(input_data))?, - TokenAmount::from(&value), - ), - - CallKind::DelegateCall => { - // first invoke GetBytecode to get the code CID from the target - let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( - system.rt, dst, - )?; - - // and then invoke self with delegate; readonly context is sticky - let params = DelegateCallParams { - code, - input: input_data.to_vec(), - readonly: system.readonly, + // Otherwise, invoke normally. + Method::InvokeContract as u64 }; system.send( - &system.rt.message().receiver(), - Method::InvokeContractDelegate as u64, - RawBytes::serialize(¶ms)?, + &dst_addr, + method, + // TODO: support IPLD codecs #758 + RawBytes::serialize(BytesSer(input_data))?, TokenAmount::from(&value), ) } - - CallKind::StaticCall => system.send( - &dst_addr, - Method::InvokeContractReadOnly as u64, - // TODO: support IPLD codecs #758 - RawBytes::serialize(BytesSer(input_data))?, + } + CallKind::DelegateCall => { + // first invoke GetBytecode to get the code CID from the target + let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( + system.rt, dst, + )?; + + // and then invoke self with delegate; readonly context is sticky + let params = DelegateCallParams { + code, + input: input_data.to_vec(), + readonly: system.readonly, + }; + system.send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + RawBytes::serialize(¶ms)?, TokenAmount::from(&value), - ), + ) + } - CallKind::CallCode => { - todo!() - } + CallKind::CallCode => { + todo!() } }; match call_result { Ok(result) => { - // TODO: support IPLD codecs #758 - let BytesDe(result) = result.deserialize()?; - result + // Support the "empty" result. We often use this to mean "returned nothing" and + // it's important to support, e.g., sending to accounts. + if result.is_empty() { + Vec::new() + } else { + // TODO: support IPLD codecs #758 + let BytesDe(result) = result.deserialize()?; + result + } } Err(ae) => { return if ae.exit_code() == EVM_CONTRACT_REVERTED { diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 3a626199f..f1e4efe53 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -9,6 +9,7 @@ use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; mod util; @@ -64,7 +65,6 @@ fn test_call() { // construct the proxy let mut rt = util::construct_and_verify(contract); - MockRuntime::default(); // create a mock target and proxy a call through the proxy let target_id = 0x100; @@ -101,6 +101,76 @@ fn test_call() { assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } +// Test that a zero-value call to an actor that doesn't exist doesn't actually create the actor. +#[test] +fn test_empty_call_no_side_effects() { + 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 evm_target = EthAddress(hex_literal::hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + + 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]); + + // expected return data + let mut return_data = vec![0u8; 32]; + return_data[31] = 0x42; + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + // Expect no calls + rt.verify(); +} + +// Make sure we do bare sends when calling accounts/embryo, and make sure it works. +#[test] +fn test_call_convert_to_send() { + let contract = call_proxy_contract(); + + for code in [*ACCOUNT_ACTOR_CODE_ID, *EMBRYO_ACTOR_CODE_ID] { + // construct the proxy + let mut rt = util::construct_and_verify(contract.clone()); + + // 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, code); + + let evm_target_word = EthAddress::from_id(target_id).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 = RawBytes::serialize(BytesSer(&proxy_call_contract_params)) + .expect("failed to serialize input data"); + + // expected return data + let mut return_data = vec![0u8; 32]; + return_data[31] = 0x42; + + rt.expect_send( + target, + METHOD_SEND, + proxy_call_input_data, + TokenAmount::zero(), + RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); + rt.verify(); + } +} + #[allow(dead_code)] pub fn methodnum_contract() -> Vec { // a simple contract that just returns the invocation methodnum From 66ecb668577269236b420fe1507a3c6895bd1791 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Oct 2022 19:27:56 +0100 Subject: [PATCH 089/339] fix: truncate addresses in instructions (#777) This is what the EVM does. They're defined to take the low 20 bytes. --- actors/evm/src/interpreter/address.rs | 17 ++++++----------- actors/evm/src/interpreter/instructions/call.rs | 8 +++++--- actors/evm/src/interpreter/instructions/ext.rs | 2 +- .../src/interpreter/instructions/lifecycle.rs | 5 ++--- .../evm/src/interpreter/instructions/state.rs | 3 +-- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index c3047ee43..1e5cc2195 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -11,19 +11,14 @@ use fvm_shared::ActorID; #[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone, Copy)] pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); -impl TryFrom for EthAddress { - type Error = StatusCode; - - fn try_from(v: U256) -> Result { - // top 12 bytes must be 0s; - // enforce that constraint so that we validate that the word is a valid address +/// 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); - if !bytes[..12].iter().all(|&byte| byte == 0) { - Err(StatusCode::BadAddress(format!("invalid address: {}", hex::encode(bytes)))) - } else { - Ok(Self(bytes[12..].try_into().unwrap())) - } + Self(bytes[12..].try_into().unwrap()) } } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 415b92869..44312430f 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -157,13 +157,14 @@ pub fn call>( }; if precompiles::Precompiles::::is_precompile(&dst) { + // TODO: DO NOT FAIL!!! precompiles::Precompiles::call_precompile(system.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? } else { let call_result = match kind { CallKind::Call | CallKind::StaticCall => { - let dst_addr: EthAddress = dst.try_into()?; - let dst_addr: Address = dst_addr.try_into()?; + let dst_addr: EthAddress = dst.into(); + let dst_addr: Address = dst_addr.try_into().expect("address is a precompile"); // Special casing for account/embryo/non-existent actors: we just do a SEND (method 0) // which allows us to transfer funds (and create embryos) @@ -294,7 +295,8 @@ pub fn callactor>( .map_err(|_| StatusCode::InvalidMemoryAccess)?; let result = { - let dst_addr: EthAddress = dst.try_into()?; + // TODO: this is wrong https://github.com/filecoin-project/ref-fvm/issues/1018 + let dst_addr: EthAddress = dst.into(); let dst_addr: Address = dst_addr.try_into()?; if method.bits() > 64 { diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 49564a709..892ababd3 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -61,7 +61,7 @@ pub fn get_evm_bytecode_cid>( rt: &RT, addr: U256, ) -> Result { - let addr: EthAddress = addr.try_into()?; + let addr: EthAddress = addr.into(); let addr: Address = addr.try_into()?; // TODO: just return none in most of these cases? let actor_id = rt.resolve_address(&addr).ok_or_else(|| { diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 21efdcd12..b8655b268 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -156,9 +156,8 @@ pub fn selfdestruct>( if system.readonly { return Err(StatusCode::StaticModeViolation); } - let beneficiary_addr = state.stack.pop(); + let beneficiary_addr: EthAddress = state.stack.pop().into(); // TODO: how do we handle errors here? Just ignore them? - state.selfdestroyed = - beneficiary_addr.try_into().and_then(|addr: EthAddress| addr.try_into()).ok(); + state.selfdestroyed = beneficiary_addr.try_into().ok(); Ok(()) } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index e30559dbb..b37a80734 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -13,11 +13,10 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( state: &mut ExecutionState, system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { - let actor = state.stack.pop(); + let actor: EthAddress = state.stack.pop().into(); let balance = actor .try_into() - .and_then(|addr: EthAddress| addr.try_into()) .ok() .and_then(|addr: Address| system.rt.resolve_address(&addr)) .and_then(|id| system.rt.actor_balance(id).as_ref().map(U256::from)) From b787886c806f502db487407bf4c0a5fe0931de1e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 10:57:07 +0100 Subject: [PATCH 090/339] fix: blockhash takes a height, not an offset (#778) --- .../src/interpreter/instructions/context.rs | 38 ++++++++++++------- actors/evm/tests/misc.rs | 38 ++++++++++++++++--- runtime/src/test_utils.rs | 11 ++---- 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index bb13b4f6a..08252b9c2 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,3 +1,5 @@ +use fvm_shared::clock::ChainEpoch; + use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::chainid, @@ -11,20 +13,28 @@ pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( system: &'r System<'r, BS, RT>, ) -> Result<(), StatusCode> { let bn = state.stack.pop(); - if bn.bits() > 8 { - return Err(StatusCode::ArgumentOutOfRange(format!("invalid epoch lookback: {}", bn))); - } - let epoch = bn.as_u64() as i64; - if let Some(cid) = system.rt.tipset_cid(epoch) { - let mut hash = cid.hash().digest(); - if hash.len() > 32 { - hash = &hash[..32] - } - state.stack.push(U256::from_big_endian(hash)); - Ok(()) - } else { - Err(StatusCode::InvalidArgument(format!("no tipset for epoch lookback at: {}", epoch))) - } + let result = bn + .try_into() + .ok() + .filter(|&height: &ChainEpoch| { + // The EVM allows fetching blockhashes from the 256 _previous_ blocks. + // TODO: we can consider extending this to allow the full range. + // Also relates to https://github.com/filecoin-project/ref-fvm/issues/1023 (we might + // want to keep some of these restrictions). + let curr_epoch = system.rt.curr_epoch(); + height >= curr_epoch - 256 && height < curr_epoch + }) + .and_then(|height| system.rt.tipset_cid(height)) + .map(|cid| { + let mut hash = cid.hash().digest(); + if hash.len() > 32 { + hash = &hash[..32] + } + U256::from_big_endian(hash) + }) + .unwrap_or_default(); + state.stack.push(result); + Ok(()) } #[inline] diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index b795e377b..90888c214 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -4,7 +4,9 @@ mod util; use cid::Cid; use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; +use fvm_ipld_encoding::DAG_CBOR; use fvm_shared::{address::Address, econ::TokenAmount}; +use multihash::Multihash; #[test] fn test_timestamp() { @@ -34,7 +36,7 @@ fn test_blockhash() { "blockhash", "", r#" -push1 0x00 +push2 0xffff blockhash push1 0x00 mstore @@ -47,11 +49,37 @@ return let mut rt = util::construct_and_verify(contract); - let test_cid = - Cid::try_from("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); - rt.tipset_cids = vec![test_cid]; + rt.tipset_cids = (0..900) + .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[2].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[256].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.to_vec(), test_cid.hash().digest()); + assert_eq!(&result, &[0u8; 32]); } #[test] diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 2d9cb78d6..1dd34ae90 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1195,15 +1195,10 @@ impl Runtime> for MockRuntime { } fn tipset_cid(&self, epoch: i64) -> Option { - if !(0..900).contains(&epoch) { - panic!("ivalidn epoch {}", epoch); + if epoch > self.epoch || epoch < 0 { + return None; } - - let epoch_index = epoch as usize; - if epoch_index < self.tipset_cids.len() { - return Some(self.tipset_cids[epoch_index]); - } - None + self.tipset_cids.get((self.epoch - epoch) as usize).copied() } } From deb8bdfef7c354c28cdd3864962be3a6d8cc703a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 17:22:30 +0100 Subject: [PATCH 091/339] feat: allow anyone to call EAM::create (#779) This will allow lotus to call this from the fake embryo accounts. This isn't behind a feature gate because it should be secure regardless. --- actors/eam/src/lib.rs | 11 +++++++++-- actors/eam/tests/create.rs | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 5051a3b15..462c16ee6 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -165,13 +165,20 @@ impl EamActor { /// Create a new contract per the EVM's CREATE rules. /// - /// Permissions: May only be called by EVM contracts. + /// Permissions: May be called by any actor. pub fn create(rt: &mut RT, params: CreateParams) -> Result where BS: Blockstore + Clone, RT: Runtime, { - rt.validate_immediate_caller_type(iter::once(&Type::EVM))?; + // TODO: this accepts a nonce from the user, so we _may_ want to limit it to specific + // actors. However, we won't deploy over another actor anyways (those constraints are + // enforced by the init actor and the FVM itself), so it shouldn't really be an issue in + // practice. + // + // This allows _any_ actor to behave like an Ethereum account, so we'd prefer to keep it + // open. + rt.validate_immediate_caller_accept_any()?; let caller_id = rt.message().caller().id().unwrap(); let caller_addr = match rt.lookup_address(caller_id).unwrap().payload() { diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 9c07f8cac..cc557bd20 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -25,7 +25,7 @@ fn call_create() { rt.add_delegated_address(id_addr, f4_eth_addr); rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); - rt.expect_validate_caller_type(vec![Type::EVM]); + rt.expect_validate_caller_any(); let initcode = vec![0xff]; From f5c855bafe40892f49793009e53ff8b1f563d36f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 17:45:17 +0100 Subject: [PATCH 092/339] chore: update sdk for address lookup fix --- actors/embryo/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 669d3cabe..374dc9407 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.7", optional = true } +fvm_sdk = { version = "3.0.0-alpha.8", optional = true } fvm_shared = { version = "3.0.0-alpha.8", optional = true } [features] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index fb1f33d92..94b5405f4 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.7", optional = true } +fvm_sdk = { version = "3.0.0-alpha.8", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } From 27df7c4f7f4b88a6f475015d54de7b926e9fcac2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 21:25:51 +0100 Subject: [PATCH 093/339] fix: refactor mathops (#780) --- .../interpreter/instructions/arithmetic.rs | 188 +++++++++----- .../src/interpreter/instructions/bitwise.rs | 57 +++-- .../src/interpreter/instructions/boolean.rs | 12 +- actors/evm/src/interpreter/uints.rs | 237 +++++++++--------- 4 files changed, 272 insertions(+), 222 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 53874144a..34a59f04f 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -25,9 +25,7 @@ pub fn sub(stack: &mut Stack) { pub fn div(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - if *b == U256::zero() { - *b = U256::zero() - } else { + if !b.is_zero() { *b = a / *b } } @@ -44,7 +42,9 @@ pub fn sdiv(stack: &mut Stack) { pub fn modulo(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if *b == U256::zero() { U256::zero() } else { a % *b }; + if !b.is_zero() { + *b = a % *b; + } } #[inline] @@ -52,71 +52,36 @@ pub fn smod(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - if *b == U256::zero() { - *b = U256::zero() - } else { - *b = i256_mod(a, *b); - }; + *b = i256_mod(a, *b); } #[inline] pub fn addmod(stack: &mut Stack) { let a = stack.pop(); let b = stack.pop(); - let c = stack.pop(); - - let v = if c == U256::zero() { - U256::zero() - } else { - let mut a_be = [0u8; 32]; - let mut b_be = [0u8; 32]; - let mut c_be = [0u8; 32]; - - a.to_big_endian(&mut a_be); - b.to_big_endian(&mut b_be); - c.to_big_endian(&mut c_be); + let c = stack.get_mut(0); - let a = U512::from_big_endian(&a_be); - let b = U512::from_big_endian(&b_be); - let c = U512::from_big_endian(&c_be); + if !c.is_zero() { + let al: U512 = a.into(); + let bl: U512 = b.into(); + let cl: U512 = (*c).into(); - let v = a + b % c; - let mut v_be = [0u8; 64]; - v.to_big_endian(&mut v_be); - U256::from_big_endian(&v_be) - }; - - stack.push(v); + *c = (al + bl % cl).low_u256(); + } } #[inline] pub fn mulmod(stack: &mut Stack) { let a = stack.pop(); let b = stack.pop(); - let c = stack.pop(); + let c = stack.get_mut(0); - let v = if c == U256::zero() { - U256::zero() - } else { - let mut a_be = [0u8; 32]; - let mut b_be = [0u8; 32]; - let mut c_be = [0u8; 32]; - - a.to_big_endian(&mut a_be); - b.to_big_endian(&mut b_be); - c.to_big_endian(&mut c_be); - - let a = U512::from_big_endian(&a_be); - let b = U512::from_big_endian(&b_be); - let c = U512::from_big_endian(&c_be); - - let v = a * b % c; - let mut v_be = [0u8; 64]; - v.to_big_endian(&mut v_be); - U256::from_big_endian(&v_be) - }; - - stack.push(v); + if !c.is_zero() { + let al: U512 = a.into(); + let bl: U512 = b.into(); + let cl: U512 = (*c).into(); + *c = (al * bl % cl).low_u256(); + } } #[inline] @@ -124,30 +89,115 @@ pub fn signextend(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - if a < U256::from(32) { - let bit_index = (8 * u256_low(a) as u8 + 7) as u16; - let hi = u256_high(*b); - let lo = u256_low(*b); - let bit = if bit_index > 0x7f { hi } else { lo } & (1 << (bit_index % 128)) != 0; - let mask = (U256::from(1) << bit_index) - U256::from(1); - *b = if bit { *b | !mask } else { *b & mask } + if a < 32 { + let bit_index = 8 * a.low_u32() + 7; + let mask = U256::MAX >> (U256::BITS - bit_index); + *b = if b.bit(bit_index as usize) { *b | !mask } else { *b & mask } } } #[inline] pub fn exp(stack: &mut Stack) { let mut base = stack.pop(); - let mut power = stack.pop(); + let power = stack.pop(); + + 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); + } - let mut v = U256::from(1); + stack.push(v); +} - while power > U256::zero() { - if (power & U256::from(1)) != U256::zero() { - v = v.overflowing_mul(base).0; +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_signextend() { + macro_rules! assert_exp { + ($num:expr, $byte:expr, $result:expr) => { + let mut stack = Stack::new(); + stack.push(($num).into()); + stack.push(($byte).into()); + signextend(&mut stack); + let res: U256 = ($result).into(); + assert_eq!(res, stack.pop()); + }; } - power >>= 1; - base = base.overflowing_mul(base).0; + 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 mut stack = Stack::new(); + stack.push(($exp).into()); + stack.push(($base).into()); + exp(&mut stack); + let res: U256 = ($result).into(); + assert_eq!(res, stack.pop()); + }; + } - stack.push(v); + // 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 index 53ceeb424..3f0458842 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -1,25 +1,22 @@ -use { - crate::interpreter::stack::Stack, crate::interpreter::uints::Sign, - crate::interpreter::uints::*, crate::interpreter::U256, -}; +use {crate::interpreter::stack::Stack, crate::interpreter::U256}; #[inline] pub fn byte(stack: &mut Stack) { let i = stack.pop(); let x = stack.get_mut(0); - if i >= U256::from(32) { - *x = U256::zero(); + if i >= U256::from_u64(32) { + *x = U256::ZERO; return; } - let mut i = u256_low(i); + let mut i = i.low_u128(); let x_word = if i >= 16 { i -= 16; - u256_low(*x) + x.low_u128() } else { - u256_high(*x) + x.high_u128() }; *x = U256::from((x_word >> (120 - i * 8)) & 0xFF); @@ -30,8 +27,8 @@ pub fn shl(stack: &mut Stack) { let shift = stack.pop(); let value = stack.get_mut(0); - if *value == U256::zero() || shift >= U256::from(256) { - *value = U256::zero(); + if value.is_zero() || shift >= 256 { + *value = U256::ZERO; } else { *value <<= shift }; @@ -42,8 +39,8 @@ pub fn shr(stack: &mut Stack) { let shift = stack.pop(); let value = stack.get_mut(0); - if *value == U256::zero() || shift >= U256::from(256) { - *value = U256::zero() + if value.is_zero() || shift >= 256 { + *value = U256::ZERO; } else { *value >>= shift }; @@ -54,33 +51,35 @@ pub fn sar(stack: &mut Stack) { let shift = stack.pop(); let mut value = stack.pop(); - let value_sign = i256_sign::(&mut value); + let negative = value.i256_is_negative(); + if negative { + value = value.i256_neg(); + } - stack.push(if value == U256::zero() || shift >= U256::from(256) { - match value_sign { - // value is 0 or >=1, pushing 0 - Sign::Plus | Sign::Zero => U256::zero(), + stack.push(if value.is_zero() || shift >= 256 { + if negative { // value is <0, pushing -1 - Sign::Minus => two_compl(U256::from(1)), + U256::ONE.i256_neg() + } else { + // value is 0 or >=1, pushing 0 + U256::ONE } } else { let shift = shift.as_u128(); - match value_sign { - Sign::Plus | Sign::Zero => value >> shift, - Sign::Minus => { - let shifted = ((value.overflowing_sub(U256::from(1)).0) >> shift) - .overflowing_add(U256::from(1)) - .0; - two_compl(shifted) - } + 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::*, crate::interpreter::uints::u128_words_to_u256}; + use super::*; #[test] fn test_instruction_byte() { @@ -107,7 +106,7 @@ mod tests { let mut stack = Stack::new(); stack.push(value); - stack.push(u128_words_to_u256(1, 0)); + stack.push(U256::from_u128_words(1, 0)); byte(&mut stack); let result = stack.pop(); diff --git a/actors/evm/src/interpreter/instructions/boolean.rs b/actors/evm/src/interpreter/instructions/boolean.rs index 6fb0958b7..50963ced7 100644 --- a/actors/evm/src/interpreter/instructions/boolean.rs +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -8,7 +8,7 @@ pub fn lt(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if a.lt(b) { U256::from(1) } else { U256::zero() } + *b = U256::from_u64((a < *b).into()); } #[inline] @@ -16,7 +16,7 @@ pub fn gt(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if a.gt(b) { U256::from(1) } else { U256::zero() } + *b = U256::from_u64((a > *b).into()); } #[inline] @@ -24,7 +24,7 @@ pub(crate) fn slt(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if i256_cmp(a, *b) == Ordering::Less { U256::from(1) } else { U256::zero() } + *b = U256::from_u64((i256_cmp(a, *b) == Ordering::Less).into()); } #[inline] @@ -32,7 +32,7 @@ pub(crate) fn sgt(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if i256_cmp(a, *b) == Ordering::Greater { U256::from(1) } else { U256::zero() } + *b = U256::from_u64((i256_cmp(a, *b) == Ordering::Greater).into()); } #[inline] @@ -40,13 +40,13 @@ pub fn eq(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); - *b = if a.eq(b) { U256::from(1) } else { U256::zero() } + *b = U256::from_u64((a == *b).into()); } #[inline] pub fn iszero(stack: &mut Stack) { let a = stack.get_mut(0); - *a = if *a == U256::zero() { U256::from(1) } else { U256::zero() } + *a = U256::from_u64(a.is_zero().into()); } #[inline] diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index cac7538ab..0f56a6f2f 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -13,6 +13,62 @@ use { 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 high_u128(&self) -> u128 { + ((self.0[3] as u128) << u64::BITS) | (self.0[2] as u128) + } + + #[inline(always)] + pub const fn i256_is_negative(&self) -> bool { + (self.0[3] as i64) < 0 + } + + #[inline(always)] + pub fn i256_neg(&self) -> U256 { + !*self + U256::ONE + } +} + +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(); @@ -26,6 +82,13 @@ impl From for arith::U256 { } } +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]; @@ -83,43 +146,11 @@ impl_hamt_hash!(U512); impl_rlp_codec_uint!(U256, 32); impl_rlp_codec_uint!(U512, 64); -#[inline(always)] -pub fn u256_high(val: U256) -> u128 { - let mut bytes = [0u8; 32]; - val.to_big_endian(&mut bytes); - u128::from_be_bytes(bytes[0..16].try_into().unwrap()) -} - -#[inline(always)] -pub fn u256_low(val: U256) -> u128 { - let mut bytes = [0u8; 32]; - val.to_big_endian(&mut bytes); - u128::from_be_bytes(bytes[16..32].try_into().unwrap()) -} - -#[inline(always)] -pub fn u128_words_to_u256(high: u128, low: u128) -> U256 { - let high = high.to_be_bytes(); - let low = low.to_be_bytes(); - let bytes = high.into_iter().chain(low.into_iter()).collect::>(); - U256::from_big_endian(&bytes) -} - -const SIGN_BITMASK_U128: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; -const FLIPH_BITMASK_U128: u128 = 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum Sign { - Plus, - Minus, - Zero, -} - #[inline] pub fn log2floor(value: U256) -> u64 { debug_assert!(value != U256::zero()); let mut l: u64 = 256; - for v in [u256_high(value), u256_low(value)] { + for v in [value.high_u128(), value.low_u128()] { if v == 0 { l -= 128; } else { @@ -134,137 +165,107 @@ pub fn log2floor(value: U256) -> u64 { l } -#[inline(always)] -pub fn two_compl(op: U256) -> U256 { - !op + U256::from(1) -} - -#[inline(always)] -fn two_compl_mut(op: &mut U256) { - *op = two_compl(*op); -} - -#[inline(always)] -pub fn i256_sign(val: &mut U256) -> Sign { - if u256_high(*val) & SIGN_BITMASK_U128 == 0 { - if *val == U256::zero() { - Sign::Zero - } else { - Sign::Plus - } - } else { - if DO_TWO_COMPL { - two_compl_mut(val); - } - Sign::Minus - } -} - #[inline(always)] pub fn i256_div(mut first: U256, mut second: U256) -> U256 { - let min_negative_value: U256 = u128_words_to_u256(SIGN_BITMASK_U128, 0); - let second_sign = i256_sign::(&mut second); - if second_sign == Sign::Zero { - return U256::zero(); + if first.is_zero() || second.is_zero() { + // EVM defines X/0 to be 0. + return U256::ZERO; } - let first_sign = i256_sign::(&mut first); - if first_sign == Sign::Minus && first == min_negative_value && second == U256::from(1) { - return two_compl(min_negative_value); + + // 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 (first, second) == (U256::I128_MIN, U256::ONE) { + return U256::I128_MIN; } - let mut d = first / second; + // 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(); - u256_remove_sign(&mut d); + if first_neg { + first = first.i256_neg() + } - if d == U256::zero() { - return U256::zero(); + if second_neg { + second = second.i256_neg() } - match (first_sign, second_sign) { - (Sign::Zero, Sign::Plus) - | (Sign::Plus, Sign::Zero) - | (Sign::Zero, Sign::Zero) - | (Sign::Plus, Sign::Plus) - | (Sign::Minus, Sign::Minus) => d, - (Sign::Zero, Sign::Minus) - | (Sign::Plus, Sign::Minus) - | (Sign::Minus, Sign::Zero) - | (Sign::Minus, Sign::Plus) => two_compl(d), + let d = first / second; + + // Flip the sign back if necessary. + if d.is_zero() || first_neg == second_neg { + d + } else { + d.i256_neg() } } #[inline(always)] pub fn i256_mod(mut first: U256, mut second: U256) -> U256 { - let first_sign = i256_sign::(&mut first); - if first_sign == Sign::Zero { - return U256::zero(); + if first.is_zero() || second.is_zero() { + // X % 0 or 0 % X is always 0. + return U256::ZERO; + } + + // Record and strip the sign. + let negative = first.i256_is_negative(); + if negative { + first = first.i256_neg(); } - let _ = i256_sign::(&mut second); - let mut r = first % second; - u256_remove_sign(&mut r); - if r == U256::zero() { - return U256::zero(); + if second.i256_is_negative() { + second = second.i256_neg() } - if first_sign == Sign::Minus { - two_compl(r) + + let r = first % second; + + // Restore the sign. + if negative && !r.is_zero() { + r.i256_neg() } else { r } } #[inline(always)] -pub fn i256_cmp(mut first: U256, mut second: U256) -> Ordering { - let first_sign = i256_sign::(&mut first); - let second_sign = i256_sign::(&mut second); - match (first_sign, second_sign) { - (Sign::Zero, Sign::Zero) => Ordering::Equal, - (Sign::Zero, Sign::Plus) => Ordering::Less, - (Sign::Zero, Sign::Minus) => Ordering::Greater, - (Sign::Minus, Sign::Zero) => Ordering::Less, - (Sign::Minus, Sign::Plus) => Ordering::Less, - (Sign::Minus, Sign::Minus) => first.cmp(&second), - (Sign::Plus, Sign::Minus) => Ordering::Greater, - (Sign::Plus, Sign::Zero) => Ordering::Greater, - (Sign::Plus, Sign::Plus) => first.cmp(&second), +pub fn i256_cmp(first: U256, second: U256) -> Ordering { + // true > false: + // - true < positive: + match second.i256_is_negative().cmp(&first.i256_is_negative()) { + Ordering::Equal => first.cmp(&second), + sign_cmp => sign_cmp, } } -#[inline(always)] -fn u256_remove_sign(val: &mut U256) { - let low = u256_low(*val); - let mut high = u256_high(*val); - high &= FLIPH_BITMASK_U128; - *val = u128_words_to_u256(high, low) -} - #[cfg(test)] mod tests { use {super::*, core::num::Wrapping}; #[test] fn div_i256() { - let min_negative_value: U256 = u128_words_to_u256(SIGN_BITMASK_U128, 0); - assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN)); assert_eq!(i8::MAX / -1, -i8::MAX); - let one = U256::from(1); + let zero = U256::ZERO; + let one = U256::ONE; let one_hundred = U256::from(100); let fifty = U256::from(50); - let _fifty_sign = Sign::Plus; let two = U256::from(2); let neg_one_hundred = U256::from(100); - let _neg_one_hundred_sign = Sign::Minus; 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!(i256_div(min_negative_value, minus_one), min_negative_value); - assert_eq!(i256_div(min_negative_value, one), min_negative_value); + assert_eq!(i256_div(U256::I128_MIN, minus_one), U256::I128_MIN); + assert_eq!(i256_div(U256::I128_MIN, one), U256::I128_MIN); + assert_eq!(i256_div(one, U256::I128_MIN), zero); assert_eq!(i256_div(max_value, one), max_value); assert_eq!(i256_div(max_value, minus_one), neg_max_value); assert_eq!(i256_div(one_hundred, minus_one), neg_one_hundred); assert_eq!(i256_div(one_hundred, two), fifty); + + assert_eq!(i256_div(zero, zero), zero); + assert_eq!(i256_div(one, zero), zero); + assert_eq!(i256_div(zero, one), zero); } } From b43d9540735d5fc68e23382ca51dfca4dd8f8a95 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 23:16:56 +0100 Subject: [PATCH 094/339] feat: improve a few more math ops (#781) - byte: use the built-in byte and don't touch u128s. - sar: simplify a bit - remove dead code --- .../src/interpreter/instructions/bitwise.rs | 24 ++++++------------- actors/evm/src/interpreter/uints.rs | 24 ------------------- 2 files changed, 7 insertions(+), 41 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs index 3f0458842..167807c40 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -5,21 +5,11 @@ pub fn byte(stack: &mut Stack) { let i = stack.pop(); let x = stack.get_mut(0); - if i >= U256::from_u64(32) { + if i >= 32 { *x = U256::ZERO; - return; - } - - let mut i = i.low_u128(); - - let x_word = if i >= 16 { - i -= 16; - x.low_u128() } else { - x.high_u128() - }; - - *x = U256::from((x_word >> (120 - i * 8)) & 0xFF); + *x = U256::from_u64(x.byte(31 - i.low_u64() as usize) as u64); + } } #[inline] @@ -58,14 +48,14 @@ pub fn sar(stack: &mut Stack) { stack.push(if value.is_zero() || shift >= 256 { if negative { - // value is <0, pushing -1 - U256::ONE.i256_neg() + // value is < 0, pushing U256::MAX (== -1) + U256::MAX } else { - // value is 0 or >=1, pushing 0 + // value is >= 0, pushing 0 U256::ONE } } else { - let shift = shift.as_u128(); + let shift = shift.low_u32(); if negative { let shifted = diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 0f56a6f2f..62897448d 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -46,11 +46,6 @@ impl U256 { U256([value, 0, 0, 0]) } - #[inline(always)] - pub const fn high_u128(&self) -> u128 { - ((self.0[3] as u128) << u64::BITS) | (self.0[2] as u128) - } - #[inline(always)] pub const fn i256_is_negative(&self) -> bool { (self.0[3] as i64) < 0 @@ -146,25 +141,6 @@ impl_hamt_hash!(U512); impl_rlp_codec_uint!(U256, 32); impl_rlp_codec_uint!(U512, 64); -#[inline] -pub fn log2floor(value: U256) -> u64 { - debug_assert!(value != U256::zero()); - let mut l: u64 = 256; - for v in [value.high_u128(), value.low_u128()] { - if v == 0 { - l -= 128; - } else { - l -= v.leading_zeros() as u64; - if l == 0 { - return l; - } else { - return l - 1; - } - } - } - l -} - #[inline(always)] pub fn i256_div(mut first: U256, mut second: U256) -> U256 { if first.is_zero() || second.is_zero() { From f0282ab4e5adedff8d89f340afff4b5fd7e8f4b4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 23:17:12 +0100 Subject: [PATCH 095/339] feat: do sha3 hashing with the runtime (#782) This should significantly speedup smart contracts. --- Cargo.lock | 1 - actors/evm/Cargo.toml | 1 - actors/evm/src/interpreter/execution.rs | 2 +- .../evm/src/interpreter/instructions/hash.rs | 28 ++++++++++++------- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ac176dc7..e41fd1ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1422,7 +1422,6 @@ dependencies = [ "serde", "serde_json", "serde_tuple", - "sha3", "strum", "strum_macros", "substrate-bn", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index b3083ce92..19c97f950 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -26,7 +26,6 @@ anyhow = "1.0.56" log = "0.4.14" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" -sha3 = { version = "0.10", default-features = false } rlp = { version = "0.5.1", default-features = false } bytes = { version = "1.1.0", features = ["serde"], default-features = false } strum = "0.24" diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 0fada1765..c6feddef6 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -267,7 +267,7 @@ impl<'r, 'a, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, 'a, BS, RT> } KECCAK256(m) { - hash::keccak256(m.runtime)?; + hash::keccak256(m.system, m.runtime)?; Ok(ControlFlow::Continue) } diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 732bc6d35..77c88cd2c 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -1,23 +1,31 @@ use { super::memory::get_memory_region, - crate::interpreter::ExecutionState, - crate::interpreter::StatusCode, - crate::interpreter::U256, - sha3::{Digest, Keccak256}, + crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::Runtime, + fvm_ipld_blockstore::Blockstore, + fvm_shared::crypto::hash::SupportedHashes, }; -pub fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> { +pub fn keccak256<'r, BS: Blockstore, RT: Runtime>( + system: &System<'r, BS, RT>, + state: &mut ExecutionState, +) -> Result<(), StatusCode> { let index = state.stack.pop(); let size = state.stack.pop(); let region = get_memory_region(&mut state.memory, index, size) // .map_err(|_| StatusCode::InvalidMemoryAccess)?; - state.stack.push(U256::from_big_endian(&*Keccak256::digest(if let Some(region) = region { - &state.memory[region.offset..region.offset + region.size.get()] - } else { - &[] - }))); + let (buf, size) = system.rt.hash_64( + SupportedHashes::Keccak256, + if let Some(region) = region { + &state.memory[region.offset..region.offset + region.size.get()] + } else { + &[] + }, + ); + + state.stack.push(U256::from_big_endian(&buf[..size])); Ok(()) } From a7f261d9f6e1e9da6e7a89f07a96d4abf00d1e73 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Oct 2022 23:28:51 +0100 Subject: [PATCH 096/339] fix: implement logging as a no-op in production (#783) For a better testnet experience. --- .../evm/src/interpreter/instructions/log.rs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs index da1212dc6..d6df382a7 100644 --- a/actors/evm/src/interpreter/instructions/log.rs +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -4,11 +4,26 @@ use { fvm_ipld_blockstore::Blockstore, }; -#[inline] +#[cfg(debug_assertions)] pub fn log<'r, BS: Blockstore, RT: Runtime>( _state: &mut ExecutionState, - _platform: &'r System<'r, BS, RT>, + _system: &'r System<'r, BS, RT>, _num_topics: usize, ) -> Result<(), StatusCode> { - todo!() + todo!("unimplemented"); +} + +#[cfg(not(debug_assertions))] +#[inline] +pub fn log<'r, BS: Blockstore, RT: Runtime>( + state: &mut ExecutionState, + _system: &'r System<'r, BS, RT>, + num_topics: usize, +) -> Result<(), StatusCode> { + // TODO: Right now, we just drop everything. But we implement this in production anyways so + // things work. + for _ in 0..num_topics { + state.stack.pop(); + } + Ok(()) } From a5bf14e2447cab732b58b35931ca87384c715f0b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 31 Oct 2022 15:36:55 +0000 Subject: [PATCH 097/339] ci: bump cache version --- .github/actions/rust-cargo-run/action.yml | 4 ++-- .github/workflows/ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml index ee2aa8cff..643b872d8 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: v4-${{ 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 46b577aa2..f27745098 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,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 From 7418f629a0704170b950717d1e46c4d8eef08fe0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 1 Nov 2022 16:41:50 +0000 Subject: [PATCH 098/339] feat: fix eam/evm for changes on master --- actors/eam/src/lib.rs | 53 ++++---------- actors/evm/src/interpreter/execution.rs | 22 +++--- .../evm/src/interpreter/instructions/call.rs | 11 ++- .../src/interpreter/instructions/context.rs | 70 ++++--------------- .../evm/src/interpreter/instructions/ext.rs | 24 +++---- .../evm/src/interpreter/instructions/hash.rs | 5 +- .../src/interpreter/instructions/lifecycle.rs | 17 +++-- .../evm/src/interpreter/instructions/log.rs | 9 ++- .../evm/src/interpreter/instructions/state.rs | 10 +-- .../src/interpreter/instructions/storage.rs | 9 ++- actors/evm/src/interpreter/precompiles.rs | 5 +- actors/evm/src/interpreter/system.rs | 10 +-- actors/evm/src/lib.rs | 31 ++++---- 13 files changed, 95 insertions(+), 181 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 462c16ee6..aef0e39c6 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -13,7 +13,6 @@ use { runtime::{ActorCode, Runtime}, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, }, - fvm_ipld_blockstore::Blockstore, fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}, fvm_shared::{ address::{Address, Payload, SECP_PUB_LEN}, @@ -108,24 +107,16 @@ pub struct EvmConstructorParams { } /// hash of data with Keccack256, with first 12 bytes cropped -fn hash_20(rt: &RT, data: &[u8]) -> [u8; 20] -where - BS: Blockstore + Clone, - RT: Runtime, -{ +fn hash_20(rt: &impl Runtime, data: &[u8]) -> [u8; 20] { rt.hash(SupportedHashes::Keccak256, data)[12..32].try_into().unwrap() } -fn create_actor( - rt: &mut RT, +fn create_actor( + rt: &mut impl Runtime, creator: EthAddress, new_addr: EthAddress, initcode: Vec, -) -> Result -where - BS: Blockstore + Clone, - RT: Runtime, -{ +) -> Result { let constructor_params = RawBytes::serialize(EvmConstructorParams { creator, initcode: initcode.into() })?; @@ -149,11 +140,7 @@ where pub struct EamActor; impl EamActor { - pub fn constructor(rt: &mut RT) -> Result<(), ActorError> - where - BS: Blockstore + Clone, - RT: Runtime, - { + pub fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { let actor_id = rt.resolve_address(&rt.message().receiver()).unwrap(); if actor_id != EAM_ACTOR_ID { return Err(ActorError::forbidden(format!( @@ -166,11 +153,7 @@ impl EamActor { /// Create a new contract per the EVM's CREATE rules. /// /// Permissions: May be called by any actor. - pub fn create(rt: &mut RT, params: CreateParams) -> Result - where - BS: Blockstore + Clone, - RT: Runtime, - { + pub fn create(rt: &mut impl Runtime, params: CreateParams) -> Result { // TODO: this accepts a nonce from the user, so we _may_ want to limit it to specific // actors. However, we won't deploy over another actor anyways (those constraints are // enforced by the init actor and the FVM itself), so it shouldn't really be an issue in @@ -197,11 +180,10 @@ impl EamActor { /// Create a new contract per the EVM's CREATE2 rules. /// /// Permissions: May be called by any actor. - pub fn create2(rt: &mut RT, params: Create2Params) -> Result - where - BS: Blockstore + Clone, - RT: Runtime, - { + pub fn create2( + rt: &mut impl Runtime, + params: Create2Params, + ) -> Result { rt.validate_immediate_caller_accept_any()?; // CREATE2 logic @@ -230,14 +212,10 @@ impl EamActor { create_actor(rt, caller_addr, eth_addr, params.initcode) } - pub fn create_account( - rt: &mut RT, + pub fn create_account( + rt: &mut impl Runtime, params: InitAccountParams, - ) -> Result - where - BS: Blockstore + Clone, - RT: Runtime, - { + ) -> Result { // First, validate that we're receiving this message from the filecoin account that maps to // this ethereum account. // @@ -263,14 +241,13 @@ impl EamActor { } impl ActorCode for EamActor { - fn invoke_method( + fn invoke_method( rt: &mut RT, method: MethodNum, params: &RawBytes, ) -> Result where - BS: Blockstore + Clone, - RT: Runtime, + RT: Runtime, { match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index c6feddef6..fe96d266a 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -15,7 +15,6 @@ use { crate::interpreter::{Bytecode, Output, System}, bytes::Bytes, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; /// EVM execution runtime. @@ -51,8 +50,8 @@ impl ExecutionState { } } -pub struct Machine<'r, 'a, BS: Blockstore, RT: Runtime> { - system: &'r mut System<'a, BS, RT>, +pub struct Machine<'r, 'a, RT: Runtime + 'a> { + system: &'r mut System<'a, RT>, runtime: &'r mut ExecutionState, bytecode: &'r Bytecode, pc: usize, @@ -69,9 +68,9 @@ type Instruction = fn(*mut M) -> Result; macro_rules! def_jmptable { ($($op:ident)*) => { - const fn jmptable() -> [Instruction>; 256] { - let mut table: [Instruction>; 256] = [Machine::<'r, 'a, BS, RT>::UNDEFINED; 256]; - $(table[OpCode::$op as usize] = Machine::<'r, 'a, BS, RT>::$op;)* + const fn jmptable() -> [Instruction>; 256] { + let mut table: [Instruction>; 256] = [Machine::<'r, 'a, RT>::UNDEFINED; 256]; + $(table[OpCode::$op as usize] = Machine::<'r, 'a, RT>::$op;)* table } } @@ -102,9 +101,9 @@ macro_rules! def_ins { } } -impl<'r, 'a, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, 'a, BS, RT> { +impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { pub fn new( - system: &'r mut System<'a, BS, RT>, + system: &'r mut System<'a, RT>, runtime: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { @@ -867,14 +866,13 @@ impl<'r, 'a, BS: Blockstore + 'r, RT: Runtime + 'r> Machine<'r, 'a, BS, RT> } } - const JMPTABLE: [Instruction>; 256] = - Machine::<'r, 'a, BS, RT>::jmptable(); + const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); } -pub fn execute>( +pub fn execute( bytecode: &Bytecode, runtime: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result { let mut m = Machine::new(system, runtime, bytecode); m.execute()?; diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 44312430f..a9a771359 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -15,7 +15,6 @@ use { crate::{DelegateCallParams, Method, EVM_CONTRACT_REVERTED}, fil_actors_runtime::runtime::builtins::Type, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, fvm_shared::econ::TokenAmount, }; @@ -109,9 +108,9 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod Ok(()) } -pub fn call>( +pub fn call( state: &mut ExecutionState, - system: &mut System, + system: &mut System, kind: CallKind, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; @@ -156,7 +155,7 @@ pub fn call>( &[] }; - if precompiles::Precompiles::::is_precompile(&dst) { + if precompiles::Precompiles::::is_precompile(&dst) { // TODO: DO NOT FAIL!!! precompiles::Precompiles::call_precompile(system.rt, dst, input_data) .map_err(|_| StatusCode::PrecompileFailure)? @@ -266,9 +265,9 @@ pub fn call>( Ok(()) } -pub fn callactor>( +pub fn callactor( state: &mut ExecutionState, - system: &System, + system: &System, ) -> Result<(), StatusCode> { let ExecutionState { stack, memory, .. } = state; let rt = &*system.rt; // as immutable reference diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 08252b9c2..8874a30a6 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -4,13 +4,12 @@ use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; #[inline] -pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( +pub fn blockhash( state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &System, ) -> Result<(), StatusCode> { let bn = state.stack.pop(); let result = bn @@ -38,26 +37,17 @@ pub fn blockhash<'r, BS: Blockstore, RT: Runtime>( } #[inline] -pub fn caller<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _: &'r System<'r, BS, RT>, -) { +pub fn caller(state: &mut ExecutionState, _: &System) { state.stack.push(state.caller.as_evm_word()) } #[inline] -pub fn address<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, -) { +pub fn address(state: &mut ExecutionState, _system: &System) { state.stack.push(state.receiver.as_evm_word()) } #[inline] -pub fn origin<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn origin(state: &mut ExecutionState, system: &System) { let origin_addr = system .resolve_ethereum_address(&system.rt.message().origin()) .expect("failed to resolve origin address"); @@ -65,84 +55,54 @@ pub fn origin<'r, BS: Blockstore, RT: Runtime>( } #[inline] -pub fn call_value<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn call_value(state: &mut ExecutionState, system: &System) { state.stack.push(U256::from(&system.rt.message().value_received())); } #[inline] -pub fn coinbase<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, -) { +pub fn coinbase(state: &mut ExecutionState, _system: &System) { // TODO do we want to return the zero ID address, or just a plain 0? state.stack.push(U256::zero()) } #[inline] -pub fn gas_price<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn gas_price(state: &mut ExecutionState, system: &System) { let effective_price = system.rt.base_fee() + system.rt.message().gas_premium(); state.stack.push(U256::from(&effective_price)); } #[inline] -pub fn gas<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn gas(state: &mut ExecutionState, system: &System) { state.stack.push(U256::from(system.rt.gas_available())); } #[inline] -pub fn timestamp<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn timestamp(state: &mut ExecutionState, system: &System) { state.stack.push(U256::from(system.rt.tipset_timestamp())); } #[inline] -pub fn block_number<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn block_number(state: &mut ExecutionState, system: &System) { state.stack.push(U256::from(system.rt.curr_epoch())); } #[inline] -pub fn difficulty<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, -) { +pub fn difficulty(state: &mut ExecutionState, _system: &System) { state.stack.push(U256::zero()); } #[inline] -pub fn gas_limit<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, -) { +pub fn gas_limit(state: &mut ExecutionState, _system: &System) { const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; state.stack.push(U256::from(BLOCK_GAS_LIMIT)); } #[inline] -pub fn chain_id<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, -) { +pub fn chain_id(state: &mut ExecutionState, _system: &System) { state.stack.push(U256::from(chainid::CHAINID)); } #[inline] -pub fn base_fee<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn base_fee(state: &mut ExecutionState, system: &System) { state.stack.push(U256::from(&system.rt.base_fee())) } diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 892ababd3..c0290fe79 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -4,18 +4,18 @@ use crate::U256; use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::ActorError; +use fvm_ipld_blockstore::Blockstore; use fvm_shared::{address::Address, econ::TokenAmount}; use num_traits::Zero; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; #[inline] -pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( +pub fn extcodesize( state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &System, ) -> Result<(), StatusCode> { let addr = state.stack.pop(); // TODO we're fetching the entire block here just to get its size. We should instead use @@ -29,9 +29,9 @@ pub fn extcodesize<'r, BS: Blockstore, RT: Runtime>( Ok(()) } -pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( +pub fn extcodehash( state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &System, ) -> Result<(), StatusCode> { let addr = state.stack.pop(); let cid = get_evm_bytecode_cid(system.rt, addr)?; @@ -42,9 +42,9 @@ pub fn extcodehash<'r, BS: Blockstore, RT: Runtime>( Ok(()) } -pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( +pub fn extcodecopy( state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &System, ) -> Result<(), StatusCode> { let ExecutionState { stack, .. } = state; let (addr, dest_offset, data_offset, size) = @@ -57,10 +57,7 @@ pub fn extcodecopy<'r, BS: Blockstore, RT: Runtime>( Ok(()) } -pub fn get_evm_bytecode_cid>( - rt: &RT, - addr: U256, -) -> Result { +pub fn get_evm_bytecode_cid(rt: &impl Runtime, addr: U256) -> Result { let addr: EthAddress = addr.into(); let addr: Address = addr.try_into()?; // TODO: just return none in most of these cases? @@ -84,10 +81,7 @@ pub fn get_evm_bytecode_cid>( Ok(cid) } -pub fn get_evm_bytecode>( - rt: &RT, - cid: &Cid, -) -> Result, StatusCode> { +pub fn get_evm_bytecode(rt: &impl Runtime, cid: &Cid) -> Result, StatusCode> { let raw_bytecode = rt .store() .get(cid) // TODO this is inefficient; should call stat here. diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 77c88cd2c..0895f1a72 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -2,12 +2,11 @@ use { super::memory::get_memory_region, crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, fvm_shared::crypto::hash::SupportedHashes, }; -pub fn keccak256<'r, BS: Blockstore, RT: Runtime>( - system: &System<'r, BS, RT>, +pub fn keccak256( + system: &System, state: &mut ExecutionState, ) -> Result<(), StatusCode> { let index = state.stack.pop(); diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index b8655b268..fe2957156 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -12,7 +12,6 @@ use super::memory::{get_memory_region, MemoryRegion}; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; pub const CREATE_METHOD_NUM: u64 = 2; @@ -40,9 +39,9 @@ pub struct EamReturn { } #[inline] -pub fn create>( +pub fn create( state: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); @@ -70,9 +69,9 @@ pub fn create>( create_init(stack, system, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) } -pub fn create2>( +pub fn create2( state: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); @@ -107,9 +106,9 @@ pub fn create2>( } /// call into Ethereum Address Manager to make the new account -fn create_init>( +fn create_init( stack: &mut Stack, - system: &mut System, + system: &mut System, params: RawBytes, method: MethodNum, value: TokenAmount, @@ -149,9 +148,9 @@ fn create_init>( } #[inline] -pub fn selfdestruct>( +pub fn selfdestruct( state: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs index d6df382a7..efa3c60d6 100644 --- a/actors/evm/src/interpreter/instructions/log.rs +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -1,13 +1,12 @@ use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; #[cfg(debug_assertions)] -pub fn log<'r, BS: Blockstore, RT: Runtime>( +pub fn log( _state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, + _system: &System, _num_topics: usize, ) -> Result<(), StatusCode> { todo!("unimplemented"); @@ -15,9 +14,9 @@ pub fn log<'r, BS: Blockstore, RT: Runtime>( #[cfg(not(debug_assertions))] #[inline] -pub fn log<'r, BS: Blockstore, RT: Runtime>( +pub fn log( state: &mut ExecutionState, - _system: &'r System<'r, BS, RT>, + _system: &System, num_topics: usize, ) -> Result<(), StatusCode> { // TODO: Right now, we just drop everything. But we implement this in production anyways so diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index b37a80734..fa8d7dc51 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -5,13 +5,12 @@ use { crate::interpreter::address::EthAddress, crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; #[inline] -pub fn balance<'r, BS: Blockstore, RT: Runtime>( +pub fn balance( state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, + system: &System, ) -> Result<(), StatusCode> { let actor: EthAddress = state.stack.pop().into(); @@ -27,10 +26,7 @@ pub fn balance<'r, BS: Blockstore, RT: Runtime>( } #[inline] -pub fn selfbalance<'r, BS: Blockstore, RT: Runtime>( - state: &mut ExecutionState, - system: &'r System<'r, BS, RT>, -) { +pub fn selfbalance(state: &mut ExecutionState, system: &System) { // Returns native FIL balance of the receiver. Value precision is identical to Ethereum, so // no conversion needed (atto, 1e18). state.stack.push(U256::from(&system.rt.current_balance())) diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 371eaae60..2614a1a0d 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -1,13 +1,12 @@ use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, - fvm_ipld_blockstore::Blockstore, }; #[inline] -pub fn sload>( +pub fn sload( state: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { // where? let location = state.stack.pop(); @@ -22,9 +21,9 @@ pub fn sload>( } #[inline] -pub fn sstore>( +pub fn sstore( state: &mut ExecutionState, - system: &mut System, + system: &mut System, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index f6d3fd19e..2ae3771fb 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -2,7 +2,6 @@ use std::{borrow::Cow, convert::TryInto, marker::PhantomData}; use super::U256; use fil_actors_runtime::runtime::{Primitives, Runtime}; -use fvm_ipld_blockstore::Blockstore; use fvm_shared::{ bigint::BigUint, crypto::{ @@ -64,9 +63,9 @@ const fn gen_precompiles() -> [PrecompileFn; 9] { ] } -pub struct Precompiles(PhantomData, PhantomData); +pub struct Precompiles(PhantomData); -impl> Precompiles { +impl Precompiles { const PRECOMPILES: [PrecompileFn; 9] = gen_precompiles(); const MAX_PRECOMPILE: U256 = { let mut limbs = [0u64; 4]; diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index e97f4f69d..3d134876f 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -39,13 +39,13 @@ pub enum StorageStatus { /// Platform Abstraction Layer /// that bridges the FVM world to EVM world -pub struct System<'r, BS: Blockstore, RT: Runtime> { +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. bytecode: Option, /// The contract's EVM storage slots. - slots: Hamt, + slots: Hamt, /// The contracts "nonce" (incremented when creating new actors). nonce: u64, /// The last saved state root. None if the current state hasn't been saved yet. @@ -54,11 +54,11 @@ pub struct System<'r, BS: Blockstore, RT: Runtime> { pub readonly: bool, } -impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { +impl<'r, RT: Runtime> System<'r, RT> { /// Create the actor. pub fn create(rt: &'r mut RT) -> Result where - BS: Clone, + RT::Blockstore: Clone, { let state_root = rt.get_state_root()?; if state_root != EMPTY_ARR_CID { @@ -78,7 +78,7 @@ impl<'r, BS: Blockstore, RT: Runtime> System<'r, BS, RT> { /// Load the actor from state. pub fn load(rt: &'r mut RT, readonly: bool) -> Result where - BS: Clone, + RT::Blockstore: Clone, { let store = rt.store().clone(); let state_root = rt.get_state_root()?; diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 8ed98717e..e838e1fc2 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -18,7 +18,6 @@ use { runtime::{ActorCode, Runtime}, ActorError, }, - fvm_ipld_blockstore::Blockstore, fvm_ipld_encoding::tuple::*, fvm_ipld_encoding::RawBytes, fvm_shared::error::*, @@ -49,10 +48,10 @@ pub enum Method { pub struct EvmContractActor; impl EvmContractActor { - pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> + pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> where - BS: Blockstore + Clone, - RT: Runtime, + RT: Runtime, + RT::Blockstore: Clone, { // TODO ideally we would be checking that we are constructed by the EAM actor, // but instead we check for init and then assert that we have a delegated address. @@ -140,7 +139,7 @@ impl EvmContractActor { } } - pub fn invoke_contract( + pub fn invoke_contract( rt: &mut RT, method: u64, input_data: &[u8], @@ -148,8 +147,8 @@ impl EvmContractActor { with_code: Option, ) -> Result, ActorError> where - BS: Blockstore + Clone, - RT: Runtime, + RT: Runtime, + RT::Blockstore: Clone, { if with_code.is_some() { rt.validate_immediate_caller_is(&[rt.message().receiver()])?; @@ -215,11 +214,7 @@ impl EvmContractActor { Ok(exec_status.output_data.to_vec()) } - pub fn bytecode(rt: &mut RT) -> Result - where - BS: Blockstore + Clone, - RT: Runtime, - { + pub fn bytecode(rt: &mut impl Runtime) -> Result { // Any caller can fetch the bytecode of a contract; this is now EXT* opcodes work. rt.validate_immediate_caller_accept_any()?; @@ -227,10 +222,10 @@ impl EvmContractActor { Ok(state.bytecode) } - pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result + pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result where - BS: Blockstore + Clone, - RT: Runtime, + 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. @@ -244,14 +239,14 @@ impl EvmContractActor { } impl ActorCode for EvmContractActor { - fn invoke_method( + fn invoke_method( rt: &mut RT, method: MethodNum, params: &RawBytes, ) -> Result where - BS: Blockstore + Clone, - RT: Runtime, + RT: Runtime, + RT::Blockstore: Clone, { match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { From 2acb19fa8544c8074e8ec3cd7d4bf758290d0388 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 8 Nov 2022 21:40:12 +0200 Subject: [PATCH 099/339] M2: drop m2-native feature from wallaby build (#815) * drop m2-native from wallaby build * rustfmt --- build.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/build.rs b/build.rs index d3f46b666..d232d4aa7 100644 --- a/build.rs +++ b/build.rs @@ -35,11 +35,7 @@ const ACTORS: &[(&Package, &ID)] = &[ 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"]), - /*("devnet-evm", &["m2-fevm"]),*/ - ("wallaby", &["m2-native"]), -]; +const EXTRA_CARGO_FEATURES: &[(&str, &[&str])] = &[("devnet-wasm", &["m2-native"])]; const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; From 379ef073db6fadce94adeb9acef9ccf0b514a52a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 9 Nov 2022 11:35:33 -0800 Subject: [PATCH 100/339] fix: really allow anyone to call EAM::create (#820) We use the ETH address derived from the user's ID address. --- actors/eam/src/lib.rs | 39 ++++++------ actors/eam/tests/create.rs | 126 ++++++++++++++++++++++--------------- 2 files changed, 95 insertions(+), 70 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index aef0e39c6..9ff897676 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -1,7 +1,9 @@ use std::iter; use ext::init::{Exec4Params, Exec4Return}; +use fil_actors_runtime::AsActorError; use fvm_ipld_encoding::Cbor; +use fvm_shared::error::ExitCode; use rlp::Encodable; pub mod ext; @@ -138,6 +140,23 @@ fn create_actor( Ok(Return::from_exec4(ret, new_addr)) } +fn resolve_caller(rt: &mut impl Runtime) -> Result { + let caller_id = rt.message().caller().id().unwrap(); + Ok(match rt.lookup_address(caller_id).map(|a| *a.payload()) { + Some(Payload::Delegated(addr)) if addr.namespace() == EAM_ACTOR_ID => EthAddress( + addr.subaddress() + .try_into() + .context_code(ExitCode::USR_FORBIDDEN, "caller's eth address isn't valid")?, + ), + _ => { + let mut bytes = [0u8; 20]; + bytes[0] = 0xff; + bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); + EthAddress(bytes) + } + }) +} + pub struct EamActor; impl EamActor { pub fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { @@ -162,12 +181,7 @@ impl EamActor { // This allows _any_ actor to behave like an Ethereum account, so we'd prefer to keep it // open. rt.validate_immediate_caller_accept_any()?; - - let caller_id = rt.message().caller().id().unwrap(); - let caller_addr = match rt.lookup_address(caller_id).unwrap().payload() { - Payload::Delegated(addr) => EthAddress(addr.subaddress().try_into().unwrap()), - _ => unreachable!(), - }; + let caller_addr = resolve_caller(rt)?; // CREATE logic let rlp = RlpCreateAddress { address: caller_addr, nonce: params.nonce }; @@ -190,18 +204,7 @@ impl EamActor { let inithash = rt.hash(SupportedHashes::Keccak256, ¶ms.initcode); // Try to lookup the caller's EVM address, but otherwise derive one from the ID address. - let caller_id = rt.message().caller().id().unwrap(); - let caller_addr = match rt.lookup_address(caller_id).map(|a| *a.payload()) { - Some(Payload::Delegated(addr)) if addr.namespace() == EAM_ACTOR_ID => { - EthAddress(addr.subaddress().try_into().expect("eth address has an invalid size")) - } - _ => { - let mut bytes = [0u8; 20]; - bytes[0] = 0xff; - bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); - EthAddress(bytes) - } - }; + let caller_addr = resolve_caller(rt)?; let eth_addr = EthAddress(hash_20( rt, diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index cc557bd20..f8d2c483d 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -6,7 +6,7 @@ use fil_actor_eam as eam; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ - expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, + expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; use fil_actors_runtime::INIT_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; @@ -17,64 +17,85 @@ use rlp::Encodable; #[test] fn call_create() { - let mut rt = construct_and_verify(); - - let id_addr = Address::new_id(110); - let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); - let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); - rt.add_delegated_address(id_addr, f4_eth_addr); - - rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); - rt.expect_validate_caller_any(); - - let initcode = vec![0xff]; + fn test_create(rt: &mut MockRuntime, eth_addr: eam::EthAddress) { + rt.expect_validate_caller_any(); - let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; + let initcode = vec![0xff]; - let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; + let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; - let rlp_params = RlpCreateAddress { address: eth_addr, nonce: 0 }; - let mut subaddress = - rt.hash(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &rlp_params.rlp_bytes()); - subaddress.drain(..12); + let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; - let params = Exec4Params { - code_cid: *EVM_ACTOR_CODE_ID, - constructor_params: RawBytes::serialize(evm_params).unwrap(), - subaddress: subaddress.clone().into(), - }; + let rlp_params = RlpCreateAddress { address: eth_addr, nonce: 0 }; + let mut subaddress = + rt.hash(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &rlp_params.rlp_bytes()); + subaddress.drain(..12); - let send_return = RawBytes::serialize(Exec4Return { - id_address: Address::new_id(111), - robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks - }) - .unwrap(); + let params = Exec4Params { + code_cid: *EVM_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(evm_params).unwrap(), + subaddress: subaddress.clone().into(), + }; - rt.expect_send( - INIT_ACTOR_ADDR, - EXEC4_METHOD, - RawBytes::serialize(params).unwrap(), - TokenAmount::from_atto(0), - send_return, - ExitCode::OK, - ); - - let result = rt - .call::( - eam::Method::Create as u64, - &RawBytes::serialize(create_params).unwrap(), - ) - .unwrap() - .deserialize::() + let send_return = RawBytes::serialize(Exec4Return { + id_address: Address::new_id(111), + robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks + }) .unwrap(); - let expected_return = Return { - actor_id: 111, - robust_address: Address::new_id(0), - eth_address: EthAddress(subaddress.try_into().unwrap()), - }; - - assert_eq!(result, expected_return) + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC4_METHOD, + RawBytes::serialize(params).unwrap(), + TokenAmount::from_atto(0), + send_return, + ExitCode::OK, + ); + + let result = rt + .call::( + eam::Method::Create as u64, + &RawBytes::serialize(create_params).unwrap(), + ) + .unwrap() + .deserialize::() + .unwrap(); + + let expected_return = Return { + actor_id: 111, + robust_address: Address::new_id(0), + eth_address: EthAddress(subaddress.try_into().unwrap()), + }; + + assert_eq!(result, expected_return); + rt.verify(); + } + + // From an EVM actor + { + let mut rt = construct_and_verify(); + + let id_addr = Address::new_id(110); + let eth_addr = + eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); + rt.add_delegated_address(id_addr, f4_eth_addr); + + rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); + test_create(&mut rt, eth_addr); + } + + // From a non-evm actor. + { + let mut rt = construct_and_verify(); + + let id_addr = Address::new_id(110); + let eth_addr = + eam::EthAddress(hex_literal::hex!("FF0000000000000000000000000000000000006E")); + + rt.set_caller(*MULTISIG_ACTOR_CODE_ID, id_addr); + test_create(&mut rt, eth_addr); + } } #[test] @@ -138,7 +159,8 @@ fn call_create2() { eth_address: EthAddress(subaddress.try_into().unwrap()), }; - assert_eq!(result, expected_return) + assert_eq!(result, expected_return); + rt.verify(); } pub fn construct_and_verify() -> MockRuntime { From d67f60cfc4ca572da3d080bd5980182465448ab0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 9 Nov 2022 12:48:40 -0800 Subject: [PATCH 101/339] fix: explicitly reject "reserved" eth addresses in the EAM (#822) Users shouldn't _naturally_ try to create actors with these addresses, but it's not theoretically impossible to _make_ this happen. --- actors/eam/src/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 9ff897676..7b4b34715 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -54,6 +54,26 @@ impl rlp::Encodable for RlpCreateAddress { #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); +impl EthAddress { + /// Returns true if the EthAddress refers to a precompile. + #[inline] + fn is_precompile(&self) -> bool { + self.0[..19].iter().all(|&i| i == 0) + } + + /// Returns true if the EthAddress is an actor ID embedded in an eth address. + #[inline] + fn is_id(&self) -> bool { + self.0[0] == 0xff && self.0[1..12].iter().all(|&i| i == 0) + } + + /// Returns true if the EthAddress is "reserved" (cannot be assigned by the EAM). + #[inline] + fn is_reserved(&self) -> bool { + self.is_precompile() || self.is_id() + } +} + #[derive(Serialize_tuple, Deserialize_tuple)] pub struct CreateParams { #[serde(with = "strict_bytes")] @@ -119,6 +139,12 @@ fn create_actor( new_addr: EthAddress, initcode: Vec, ) -> Result { + // If the new address is reserved (an ID address, or a precompile), reject it. An attacker would + // need to brute-force 96bits of a cryptographic hash and convince the target to use an attacker + // chosen salt, but we might as well be safe. + if new_addr.is_reserved() { + return Err(ActorError::forbidden("cannot create address with a reserved prefix".into())); + } let constructor_params = RawBytes::serialize(EvmConstructorParams { creator, initcode: initcode.into() })?; @@ -270,3 +296,31 @@ impl ActorCode for EamActor { } } } + +#[test] +fn test_create_actor_rejects() { + use fil_actors_runtime::test_utils::MockRuntime; + use fvm_shared::error::ExitCode; + let mut rt = MockRuntime::default(); + let mut creator = EthAddress([0; 20]); + creator.0[0] = 0xff; + creator.0[19] = 0x1; + + // Reject ID. + let mut new_addr = EthAddress([0; 20]); + new_addr.0[0] = 0xff; + new_addr.0[18] = 0x20; + new_addr.0[19] = 0x20; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); + + // Reject Precompile. + let mut new_addr = EthAddress([0; 20]); + new_addr.0[19] = 0x20; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); +} From a56108cc62ba5d4ab67e13b74ff54711ec59704a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 10 Nov 2022 16:13:01 -0800 Subject: [PATCH 102/339] fix u256 encoding (#828) Always encode to a big-endian byte array. fixes https://github.com/filecoin-project/ref-fvm/issues/912 --- actors/evm/src/interpreter/uints.rs | 71 ++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 62897448d..1b178787c 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -3,11 +3,12 @@ // see https://github.com/paritytech/parity-common/issues/660 #![allow(clippy::ptr_offset_with_cast, clippy::assign_op_pattern)] +use serde::{Deserialize, Serialize}; use substrate_bn::arith; use { - fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, impl_serde::impl_uint_serde, - std::cmp::Ordering, uint::construct_uint, + 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 @@ -92,11 +93,43 @@ impl From<&U256> for TokenAmount { } } -// make ETH uints serde serializable, -// so it can work with Hamt and other -// IPLD structures seamlessly -impl_uint_serde!(U256, 4); -impl_uint_serde!(U512, 8); +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) + } +} macro_rules! impl_hamt_hash { ($type:ident) => { @@ -215,6 +248,8 @@ pub fn i256_cmp(first: U256, second: U256) -> Ordering { #[cfg(test)] mod tests { + use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; + use {super::*, core::num::Wrapping}; #[test] @@ -244,4 +279,26 @@ mod tests { assert_eq!(i256_div(one, zero), zero); assert_eq!(i256_div(zero, one), zero); } + + #[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"); + } } From 137c4478ee21ff555da1c73e9a9b28c516b67abc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 10 Nov 2022 17:48:13 -0800 Subject: [PATCH 103/339] EVM: simplify and fix evm storage (#831) * EVM: simplify and fix evm storage 1. The EVM doesn't distinguish between "exists" and "doesn't exist". 2. There was some interesting dead code. 3. Only mark the state-tree dirty if something has changed. * nit: make vyzo happy --- .../src/interpreter/instructions/storage.rs | 13 ++--- actors/evm/src/interpreter/system.rs | 58 +++++-------------- actors/evm/src/lib.rs | 5 +- actors/evm/tests/basic.rs | 19 +++--- 4 files changed, 33 insertions(+), 62 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 2614a1a0d..6d2296794 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -1,5 +1,5 @@ use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, }; @@ -12,11 +12,7 @@ pub fn sload( let location = state.stack.pop(); // get from storage and place on stack - let value = match system.get_storage(location)? { - Some(val) => val, - None => U256::zero(), - }; - state.stack.push(value); + state.stack.push(system.get_storage(location)?); Ok(()) } @@ -29,10 +25,9 @@ pub fn sstore( return Err(StatusCode::StaticModeViolation); } - let location = state.stack.pop(); + let key = state.stack.pop(); let value = state.stack.pop(); - let opt_value = if value == U256::zero() { None } else { Some(value) }; - system.set_storage(location, opt_value)?; + system.set_storage(key, value)?; Ok(()) } diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 3d134876f..4e1593e46 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -23,20 +23,6 @@ use { fvm_ipld_hamt::Hamt, }; -#[derive(Clone, Copy, Debug)] -pub enum StorageStatus { - /// The value of a storage item has been left unchanged: 0 -> 0 and X -> X. - Unchanged, - /// The value of a storage item has been modified: X -> Y. - Modified, - /// A storage item has been modified after being modified before: X -> Y -> Z. - ModifiedAgain, - /// A new storage item has been added: 0 -> X. - Added, - /// A storage item has been deleted: X -> 0. - Deleted, -} - /// Platform Abstraction Layer /// that bridges the FVM world to EVM world pub struct System<'r, RT: Runtime> { @@ -205,40 +191,28 @@ impl<'r, RT: Runtime> System<'r, RT> { } /// Get value of a storage key. - pub fn get_storage(&mut self, key: U256) -> Result, StatusCode> { - let mut key_bytes = [0u8; 32]; - key.to_big_endian(&mut key_bytes); - - Ok(self.slots.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned()) + pub fn get_storage(&mut self, key: U256) -> Result { + Ok(self + .slots + .get(&key) + .map_err(|e| StatusCode::InternalError(e.to_string()))? + .cloned() + .unwrap_or_default()) } /// Set value of a storage key. - pub fn set_storage( - &mut self, - key: U256, - value: Option, - ) -> Result { - self.saved_state_root = None; // dirty. - - let mut key_bytes = [0u8; 32]; - key.to_big_endian(&mut key_bytes); - - let prev_value = - self.slots.get(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?.cloned(); - - let mut storage_status = - if prev_value == value { StorageStatus::Unchanged } else { StorageStatus::Modified }; - - if value.is_none() { - self.slots.delete(&key).map_err(|e| StatusCode::InternalError(e.to_string()))?; - storage_status = StorageStatus::Deleted; + pub fn set_storage(&mut self, key: U256, value: U256) -> Result<(), StatusCode> { + let changed = if value.is_zero() { + self.slots.delete(&key).map(|v| v.is_some()) } else { - self.slots - .set(key, value.unwrap()) - .map_err(|e| StatusCode::InternalError(e.to_string()))?; + self.slots.set(key, value).map(|v| v != Some(value)) } + .map_err(|e| StatusCode::InternalError(e.to_string()))?; - Ok(storage_status) + if changed { + self.saved_state_root = None; // dirty. + }; + Ok(()) } /// Resolve the address to the ethereum equivalent, if possible. diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index e838e1fc2..d47a4bff7 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,6 @@ use std::iter; -use fil_actors_runtime::{runtime::builtins::Type, EAM_ACTOR_ID}; +use fil_actors_runtime::{runtime::builtins::Type, AsActorError, EAM_ACTOR_ID}; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; use fvm_shared::address::{Address, Payload}; use interpreter::{address::EthAddress, system::load_bytecode}; @@ -233,8 +233,7 @@ impl EvmContractActor { System::load(rt, true)? .get_storage(params.storage_key) - .map_err(|st| ActorError::unspecified(format!("failed to get storage key: {}", &st)))? - .ok_or_else(|| ActorError::not_found(String::from("storage key not found"))) + .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to get storage key") } } diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index f09bf159b..5dc0766e4 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -4,7 +4,6 @@ use cid::Cid; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::ActorError; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -129,16 +128,20 @@ sstore"; assert_eq!(U256::from(0xfffa), value); // - // Get a storage key that doesn't exist. + // 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 ret = rt.call::( - evm::Method::GetStorageAt as u64, - &RawBytes::serialize(params).unwrap(), - ); - rt.verify(); + let value: U256 = rt + .call::( + evm::Method::GetStorageAt as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); - assert_eq!(ActorError::not_found("storage key not found".to_string()), ret.err().unwrap()); + assert_eq!(U256::from(0), value); + rt.verify(); } From 38e800ee40bab3a15835291526f9e190c3a6727f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 10 Nov 2022 17:59:29 -0800 Subject: [PATCH 104/339] EVM: remove impossible result (#833) --- actors/evm/src/interpreter/execution.rs | 2 +- actors/evm/src/interpreter/instructions/context.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index fe96d266a..714e618a9 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -351,7 +351,7 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } BLOCKHASH(m) { - context::blockhash(m.runtime, m.system)?; + context::blockhash(m.runtime, m.system); Ok(ControlFlow::Continue) } diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 8874a30a6..8e6542e42 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,16 +1,13 @@ use fvm_shared::clock::ChainEpoch; use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, }; #[inline] -pub fn blockhash( - state: &mut ExecutionState, - system: &System, -) -> Result<(), StatusCode> { +pub fn blockhash(state: &mut ExecutionState, system: &System) { let bn = state.stack.pop(); let result = bn .try_into() @@ -33,7 +30,6 @@ pub fn blockhash( }) .unwrap_or_default(); state.stack.push(result); - Ok(()) } #[inline] From 47a1d648344fbb007dfc3b9fe5bb30c4c22f2490 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 11 Nov 2022 11:31:02 -0800 Subject: [PATCH 105/339] EVM: reserve methods < 1024 for EVM internals (#823) We only delegate methods >= 1024 to the contract. This is more than sufficient to cover any methods exported by FRC0042. part of https://github.com/filecoin-project/ref-fvm/issues/1056 --- actors/evm/src/lib.rs | 14 ++++++++++---- actors/evm/tests/call.rs | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index d47a4bff7..c8d5f7dd7 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,6 @@ use std::iter; -use fil_actors_runtime::{runtime::builtins::Type, AsActorError, EAM_ACTOR_ID}; +use fil_actors_runtime::{actor_error, runtime::builtins::Type, AsActorError, EAM_ACTOR_ID}; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; use fvm_shared::address::{Address, Payload}; use interpreter::{address::EthAddress, system::load_bytecode}; @@ -35,6 +35,8 @@ const MAX_CODE_SIZE: usize = 24 << 10; pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(27); +const EVM_MAX_RESERVED_METHOD: u64 = 1023; + #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -247,6 +249,12 @@ impl ActorCode for EvmContractActor { RT: Runtime, RT::Blockstore: Clone, { + // We reserve all methods below EVM_MAX_RESERVED (<= 1023) method. This is a _subset_ of + // those reserved by FRC0042. + if method > EVM_MAX_RESERVED_METHOD { + return Self::invoke_contract(rt, method, params, false, None).map(RawBytes::new); + } + match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -288,9 +296,7 @@ impl ActorCode for EvmContractActor { )?; Ok(RawBytes::serialize(BytesSer(&value))?) } - - // Otherwise, we take the bytes as CBOR. - None => Self::invoke_contract(rt, method, params, false, None).map(RawBytes::new), + None => Err(actor_error!(unhandled_message; "Invalid method")), } } } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index f1e4efe53..00d13238d 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -188,6 +188,20 @@ return asm::new_contract("methodnum", init, body).unwrap() } +#[test] +fn test_reserved_method() { + let contract = methodnum_contract(); + + let mut rt = util::construct_and_verify(contract); + + // invoke the contract + rt.expect_validate_caller_any(); + + let code = + rt.call::(0x42, &RawBytes::default()).unwrap_err().exit_code(); + assert_eq!(ExitCode::USR_UNHANDLED_MESSAGE, code); +} + #[test] fn test_methodnum() { let contract = methodnum_contract(); @@ -197,8 +211,8 @@ fn test_methodnum() { // invoke the contract rt.expect_validate_caller_any(); - let result = rt.call::(0x42, &RawBytes::default()).unwrap(); - assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); + let result = rt.call::(1024, &RawBytes::default()).unwrap(); + assert_eq!(U256::from_big_endian(&result), U256::from(1024)); } #[allow(dead_code)] From 46a59b54906c1e6dcda85a3a99fe99bc8a6bbab0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 11 Nov 2022 13:31:49 -0800 Subject: [PATCH 106/339] EVM: on unhandled methods, call handle_filecoin_method with solidity ABI (#834) * on unhandled methods, call filecoin_native_method with solidity ABI * address feedback * comment * fix compile otherwise, we conflict with ActorCode::invoke_method * fix tests * fix: rename method number --- actors/evm/src/interpreter/execution.rs | 9 +- .../evm/src/interpreter/instructions/call.rs | 4 - actors/evm/src/lib.rs | 87 ++++++++++++------- actors/evm/tests/asm.rs | 4 +- actors/evm/tests/call.rs | 24 ++--- .../evm/tests/contracts/FilecoinFallback.abi | 1 + .../evm/tests/contracts/FilecoinFallback.bin | 1 + .../evm/tests/contracts/FilecoinFallback.hex | 1 + .../contracts/FilecoinFallback.signatures | 2 + .../evm/tests/contracts/FilecoinFallback.sol | 9 ++ .../contracts/FilecoinFallback_storage.json | 1 + actors/evm/tests/opcodes/methodnum.hex | 1 - 12 files changed, 78 insertions(+), 66 deletions(-) create mode 100644 actors/evm/tests/contracts/FilecoinFallback.abi create mode 100644 actors/evm/tests/contracts/FilecoinFallback.bin create mode 100644 actors/evm/tests/contracts/FilecoinFallback.hex create mode 100644 actors/evm/tests/contracts/FilecoinFallback.signatures create mode 100644 actors/evm/tests/contracts/FilecoinFallback.sol create mode 100644 actors/evm/tests/contracts/FilecoinFallback_storage.json delete mode 100644 actors/evm/tests/opcodes/methodnum.hex diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 714e618a9..f9788728b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -22,7 +22,6 @@ use { pub struct ExecutionState { pub stack: Stack, pub memory: Memory, - pub method: u64, pub input_data: Bytes, pub return_data: Bytes, pub output_data: Bytes, @@ -35,11 +34,10 @@ pub struct ExecutionState { } impl ExecutionState { - pub fn new(caller: EthAddress, receiver: EthAddress, method: u64, input_data: Bytes) -> Self { + pub fn new(caller: EthAddress, receiver: EthAddress, input_data: Bytes) -> Self { Self { stack: Stack::default(), memory: Memory::default(), - method, input_data, return_data: Default::default(), output_data: Bytes::new(), @@ -859,11 +857,6 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { call::callactor(m.runtime, m.system)?; Ok(ControlFlow::Continue) } - - METHODNUM(m) { - call::methodnum(m.runtime); - Ok(ControlFlow::Continue) - } } const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index a9a771359..5b9b7eb05 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -329,7 +329,3 @@ pub fn callactor( stack.push(U256::from(1)); Ok(()) } - -pub fn methodnum(state: &mut ExecutionState) { - state.stack.push(U256::from(state.method)); -} diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index c8d5f7dd7..446809afd 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,7 +1,7 @@ use std::iter; use fil_actors_runtime::{actor_error, runtime::builtins::Type, AsActorError, EAM_ACTOR_ID}; -use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; use fvm_shared::address::{Address, Payload}; use interpreter::{address::EthAddress, system::load_bytecode}; @@ -36,6 +36,17 @@ const MAX_CODE_SIZE: usize = 24 << 10; pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(27); 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]; + +#[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)] @@ -102,12 +113,7 @@ impl EvmContractActor { let mut system = System::create(rt)?; // create a new execution context - let mut exec_state = ExecutionState::new( - params.creator, - receiver_eth_addr, - Method::Constructor as u64, - Bytes::new(), - ); + let mut exec_state = ExecutionState::new(params.creator, receiver_eth_addr, Bytes::new()); // identify bytecode valid jump destinations let initcode = Bytecode::new(params.initcode.into()); @@ -143,7 +149,6 @@ impl EvmContractActor { pub fn invoke_contract( rt: &mut RT, - method: u64, input_data: &[u8], readonly: bool, with_code: Option, @@ -179,12 +184,8 @@ impl EvmContractActor { 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_eth_addr, - receiver_eth_addr, - method, - input_data.to_vec().into(), - ); + let mut exec_state = + ExecutionState::new(caller_eth_addr, receiver_eth_addr, input_data.to_vec().into()); let exec_status = execute(&bytecode, &mut exec_state, &mut system).map_err(|e| match e { @@ -216,6 +217,20 @@ impl EvmContractActor { Ok(exec_status.output_data.to_vec()) } + pub fn handle_filecoin_method( + rt: &mut RT, + method: u64, + codec: u64, + params: &[u8], + ) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + let input = handle_filecoin_method_input(method, codec, params); + Self::invoke_contract(rt, &input, false, None) + } + pub fn bytecode(rt: &mut impl Runtime) -> Result { // Any caller can fetch the bytecode of a contract; this is now EXT* opcodes work. rt.validate_immediate_caller_accept_any()?; @@ -239,6 +254,25 @@ impl EvmContractActor { } } +/// Format "filecoin_native_method" input parameters. +fn handle_filecoin_method_input(method: u64, codec: u64, params: &[u8]) -> Vec { + let static_args = [method, codec, 32 * 3 /* start of params */, params.len() as u64]; + let total_words = static_args.len() + (params.len() / 32) + (params.len() % 32 > 0) as usize; + let len = 4 + total_words * 32; + 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() + (32 - 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 +} + impl ActorCode for EvmContractActor { fn invoke_method( rt: &mut RT, @@ -252,7 +286,10 @@ impl ActorCode for EvmContractActor { // We reserve all methods below EVM_MAX_RESERVED (<= 1023) method. This is a _subset_ of // those reserved by FRC0042. if method > EVM_MAX_RESERVED_METHOD { - return Self::invoke_contract(rt, method, params, false, None).map(RawBytes::new); + // FIXME: we need the actual codec. + // See https://github.com/filecoin-project/ref-fvm/issues/987 + let codec = if params.is_empty() { 0 } else { DAG_CBOR }; + return Self::handle_filecoin_method(rt, method, codec, params).map(RawBytes::new); } match FromPrimitive::from_u64(method) { @@ -262,8 +299,7 @@ impl ActorCode for EvmContractActor { } Some(Method::InvokeContract) => { let BytesDe(params) = params.deserialize()?; - let value = - Self::invoke_contract(rt, Method::InvokeContract as u64, ¶ms, false, None)?; + let value = Self::invoke_contract(rt, ¶ms, false, None)?; Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::GetBytecode) => { @@ -276,24 +312,13 @@ impl ActorCode for EvmContractActor { } Some(Method::InvokeContractReadOnly) => { let BytesDe(params) = params.deserialize()?; - let value = Self::invoke_contract( - rt, - Method::InvokeContractReadOnly as u64, - ¶ms, - true, - None, - )?; + let value = Self::invoke_contract(rt, ¶ms, true, None)?; Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::InvokeContractDelegate) => { let params: DelegateCallParams = cbor::deserialize_params(params)?; - let value = Self::invoke_contract( - rt, - Method::InvokeContractDelegate as u64, - ¶ms.input, - params.readonly, - Some(params.code), - )?; + let value = + Self::invoke_contract(rt, ¶ms.input, params.readonly, Some(params.code))?; Ok(RawBytes::serialize(BytesSer(&value))?) } None => Err(actor_error!(unhandled_message; "Invalid method")), diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index 561672374..b661b525b 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -95,7 +95,5 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a // So f*ck it, we'll just hack this until there is support. // See also https://github.com/quilt/etk/issues/110 fn with_fevm_extensions(body: &str) -> String { - body.to_owned() - .replace("@callactor", "%include_hex(\"tests/opcodes/callactor.hex\")") - .replace("@methodnum", "%include_hex(\"tests/opcodes/methodnum.hex\")") + body.to_owned().replace("@callactor", "%include_hex(\"tests/opcodes/callactor.hex\")") } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 00d13238d..bc5a5619b 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -172,26 +172,13 @@ fn test_call_convert_to_send() { } #[allow(dead_code)] -pub fn methodnum_contract() -> Vec { - // a simple contract that just returns the invocation methodnum - let init = ""; - let body = r#" -@methodnum -push1 0x00 -mstore - -push1 0x20 -push1 0x00 -return -"#; - - asm::new_contract("methodnum", init, body).unwrap() +pub fn filecoin_fallback_contract() -> Vec { + hex::decode(include_str!("contracts/FilecoinFallback.hex")).unwrap() } #[test] fn test_reserved_method() { - let contract = methodnum_contract(); - + let contract = filecoin_fallback_contract(); let mut rt = util::construct_and_verify(contract); // invoke the contract @@ -203,9 +190,8 @@ fn test_reserved_method() { } #[test] -fn test_methodnum() { - let contract = methodnum_contract(); - +fn test_native_call() { + let contract = filecoin_fallback_contract(); let mut rt = util::construct_and_verify(contract); // invoke the contract diff --git a/actors/evm/tests/contracts/FilecoinFallback.abi b/actors/evm/tests/contracts/FilecoinFallback.abi new file mode 100644 index 000000000..43c775c90 --- /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":"uint64","name":"","type":"uint64"}],"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..25c8b5aaf --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610212806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061013e565b610060565b60405161005791906101c1565b60405180910390f35b6000808383905014151560008567ffffffffffffffff161415151461008457600080fd5b849050949350505050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6100b681610099565b81146100c157600080fd5b50565b6000813590506100d3816100ad565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126100fe576100fd6100d9565b5b8235905067ffffffffffffffff81111561011b5761011a6100de565b5b602083019150836001820283011115610137576101366100e3565b5b9250929050565b600080600080606085870312156101585761015761008f565b5b6000610166878288016100c4565b9450506020610177878288016100c4565b935050604085013567ffffffffffffffff81111561019857610197610094565b5b6101a4878288016100e8565b925092505092959194509250565b6101bb81610099565b82525050565b60006020820190506101d660008301846101b2565b9291505056fea2646970667358221220ca0553fc34ed0b668b807e982f4781af89f25219fe41d96f851f2acfb721a83764736f6c63430008110033 \ 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..25c8b5aaf --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610212806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061013e565b610060565b60405161005791906101c1565b60405180910390f35b6000808383905014151560008567ffffffffffffffff161415151461008457600080fd5b849050949350505050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6100b681610099565b81146100c157600080fd5b50565b6000813590506100d3816100ad565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126100fe576100fd6100d9565b5b8235905067ffffffffffffffff81111561011b5761011a6100de565b5b602083019150836001820283011115610137576101366100e3565b5b9250929050565b600080600080606085870312156101585761015761008f565b5b6000610166878288016100c4565b9450506020610177878288016100c4565b935050604085013567ffffffffffffffff81111561019857610197610094565b5b6101a4878288016100e8565b925092505092959194509250565b6101bb81610099565b82525050565b60006020820190506101d660008301846101b2565b9291505056fea2646970667358221220ca0553fc34ed0b668b807e982f4781af89f25219fe41d96f851f2acfb721a83764736f6c63430008110033 \ 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..7a564ec41 --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.sol @@ -0,0 +1,9 @@ +// 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 (uint64) { + require((codec == 0) == (params.length == 0)); + return method; + } +} 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/opcodes/methodnum.hex b/actors/evm/tests/opcodes/methodnum.hex deleted file mode 100644 index 611d98703..000000000 --- a/actors/evm/tests/opcodes/methodnum.hex +++ /dev/null @@ -1 +0,0 @@ -b1 \ No newline at end of file From 59e8796d5a72f25bfa7ffb5fae75e6824b8cf3ab Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 11 Nov 2022 14:43:26 -0800 Subject: [PATCH 107/339] chore: update storage plots (#832) --- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 15926 -> 15633 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 22390 -> 22640 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 14782 -> 14691 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 18131 -> 18437 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 16842 -> 17092 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 15291 -> 14527 bytes .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 16168 -> 16081 bytes .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 23236 -> 23485 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22925 -> 23561 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 17700 -> 18077 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 19391 -> 19729 bytes 22 files changed, 1620 insertions(+), 1620 deletions(-) diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index ba364a1bc..c3d42fa00 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2288,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2197,"get_count":3,"put_bytes":197,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2205,"get_count":3,"put_bytes":205,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2213,"get_count":3,"put_bytes":214,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2222,"get_count":3,"put_bytes":222,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2230,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":238,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":246,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2254,"get_count":3,"put_bytes":334,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2342,"get_count":3,"put_bytes":342,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2350,"get_count":3,"put_bytes":350,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2358,"get_count":3,"put_bytes":359,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2367,"get_count":3,"put_bytes":367,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2375,"get_count":3,"put_bytes":375,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2383,"get_count":3,"put_bytes":383,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":2391,"get_count":3,"put_bytes":392,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":2400,"get_count":3,"put_bytes":466,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":2474,"get_count":3,"put_bytes":474,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2482,"get_count":3,"put_bytes":482,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2490,"get_count":3,"put_bytes":491,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":2499,"get_count":3,"put_bytes":499,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2507,"get_count":3,"put_bytes":507,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2515,"get_count":3,"put_bytes":515,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2523,"get_count":3,"put_bytes":523,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":2531,"get_count":3,"put_bytes":597,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":2605,"get_count":3,"put_bytes":605,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":2613,"get_count":3,"put_bytes":613,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":2621,"get_count":3,"put_bytes":622,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":2630,"get_count":3,"put_bytes":630,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":2638,"get_count":3,"put_bytes":638,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":2646,"get_count":3,"put_bytes":646,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":2654,"get_count":3,"put_bytes":654,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":2662,"get_count":3,"put_bytes":727,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":2735,"get_count":3,"put_bytes":735,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":2743,"get_count":3,"put_bytes":743,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":2751,"get_count":3,"put_bytes":752,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":2760,"get_count":3,"put_bytes":760,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":2768,"get_count":3,"put_bytes":768,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":2776,"get_count":3,"put_bytes":776,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":2784,"get_count":3,"put_bytes":784,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":2792,"get_count":3,"put_bytes":858,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":2866,"get_count":3,"put_bytes":866,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":2874,"get_count":3,"put_bytes":874,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":2882,"get_count":3,"put_bytes":883,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":2891,"get_count":3,"put_bytes":891,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":2899,"get_count":3,"put_bytes":899,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":2907,"get_count":3,"put_bytes":907,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":2915,"get_count":3,"put_bytes":915,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":2923,"get_count":3,"put_bytes":990,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":2998,"get_count":3,"put_bytes":998,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":3006,"get_count":3,"put_bytes":1006,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3014,"get_count":3,"put_bytes":1015,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":3023,"get_count":3,"put_bytes":1023,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3031,"get_count":3,"put_bytes":1031,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1039,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":3047,"get_count":3,"put_bytes":1047,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":3055,"get_count":3,"put_bytes":1121,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3129,"get_count":3,"put_bytes":1129,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3137,"get_count":3,"put_bytes":1137,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":3145,"get_count":3,"put_bytes":1146,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":3154,"get_count":3,"put_bytes":1154,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":3162,"get_count":3,"put_bytes":1162,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":3170,"get_count":3,"put_bytes":1170,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3178,"get_count":3,"put_bytes":1178,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":3186,"get_count":3,"put_bytes":1252,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1260,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":3268,"get_count":3,"put_bytes":1268,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":3276,"get_count":3,"put_bytes":1277,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":3285,"get_count":3,"put_bytes":1285,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":3293,"get_count":3,"put_bytes":1293,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":3301,"get_count":3,"put_bytes":1301,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":3309,"get_count":3,"put_bytes":1309,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":3317,"get_count":3,"put_bytes":1383,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":3391,"get_count":3,"put_bytes":1391,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":3399,"get_count":3,"put_bytes":1399,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":3407,"get_count":3,"put_bytes":1408,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":3416,"get_count":3,"put_bytes":1416,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":3424,"get_count":3,"put_bytes":1424,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":3432,"get_count":3,"put_bytes":1432,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":3440,"get_count":3,"put_bytes":1440,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":3448,"get_count":3,"put_bytes":1514,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":3522,"get_count":3,"put_bytes":1522,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1530,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":3538,"get_count":3,"put_bytes":1539,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":3547,"get_count":3,"put_bytes":1547,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":3555,"get_count":3,"put_bytes":1555,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":3563,"get_count":3,"put_bytes":1563,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":3571,"get_count":3,"put_bytes":1571,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":3579,"get_count":3,"put_bytes":1645,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":3653,"get_count":3,"put_bytes":1653,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":3661,"get_count":3,"put_bytes":1661,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":3669,"get_count":3,"put_bytes":1670,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":3678,"get_count":3,"put_bytes":1678,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":3686,"get_count":3,"put_bytes":1686,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":3694,"get_count":3,"put_bytes":1694,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":3702,"get_count":3,"put_bytes":1702,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":3710,"get_count":3,"put_bytes":1776,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":3784,"get_count":3,"put_bytes":1784,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":3792,"get_count":3,"put_bytes":1792,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":3800,"get_count":3,"put_bytes":1801,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":3809,"get_count":3,"put_bytes":1885,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":3893,"get_count":3,"put_bytes":1893,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3901,"get_count":3,"put_bytes":1901,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3909,"get_count":3,"put_bytes":1910,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":3918,"get_count":3,"put_bytes":1918,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":3926,"get_count":3,"put_bytes":1926,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":3934,"get_count":3,"put_bytes":1934,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":3942,"get_count":3,"put_bytes":1942,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":3950,"get_count":3,"put_bytes":2016,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":4024,"get_count":3,"put_bytes":2024,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":4032,"get_count":3,"put_bytes":2032,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":4040,"get_count":3,"put_bytes":2041,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":4049,"get_count":3,"put_bytes":2049,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":4057,"get_count":3,"put_bytes":2057,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":4065,"get_count":3,"put_bytes":2065,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":4073,"get_count":3,"put_bytes":2074,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":4082,"get_count":3,"put_bytes":2147,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":4155,"get_count":3,"put_bytes":2155,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":4163,"get_count":3,"put_bytes":2163,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":4171,"get_count":3,"put_bytes":2172,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":4180,"get_count":3,"put_bytes":2180,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":4188,"get_count":3,"put_bytes":2188,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":4196,"get_count":3,"put_bytes":2196,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":4204,"get_count":3,"put_bytes":2204,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":4212,"get_count":3,"put_bytes":2278,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":4286,"get_count":3,"put_bytes":2286,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":4294,"get_count":3,"put_bytes":2294,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":4302,"get_count":3,"put_bytes":2303,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":4311,"get_count":3,"put_bytes":2311,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":4319,"get_count":3,"put_bytes":2319,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":4327,"get_count":3,"put_bytes":2327,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":4335,"get_count":3,"put_bytes":2335,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":4343,"get_count":3,"put_bytes":2409,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":4417,"get_count":3,"put_bytes":2417,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":4425,"get_count":3,"put_bytes":2425,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":4433,"get_count":3,"put_bytes":2434,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":4442,"get_count":3,"put_bytes":2442,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":4450,"get_count":3,"put_bytes":2450,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":4458,"get_count":3,"put_bytes":2458,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":4466,"get_count":3,"put_bytes":2466,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":4474,"get_count":3,"put_bytes":2540,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":4548,"get_count":3,"put_bytes":2548,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":4556,"get_count":3,"put_bytes":2556,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":4564,"get_count":3,"put_bytes":2565,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":4573,"get_count":3,"put_bytes":2573,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":4581,"get_count":3,"put_bytes":2581,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":4589,"get_count":3,"put_bytes":2589,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":4597,"get_count":3,"put_bytes":2597,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":4605,"get_count":3,"put_bytes":2673,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":4681,"get_count":3,"put_bytes":2681,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":4689,"get_count":3,"put_bytes":2689,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":4697,"get_count":3,"put_bytes":2698,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":4706,"get_count":3,"put_bytes":2706,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":4714,"get_count":3,"put_bytes":2714,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":4722,"get_count":3,"put_bytes":2722,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":4730,"get_count":3,"put_bytes":2730,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":4738,"get_count":3,"put_bytes":2804,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":4812,"get_count":3,"put_bytes":2812,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":4820,"get_count":3,"put_bytes":2820,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":4828,"get_count":3,"put_bytes":2829,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":4837,"get_count":3,"put_bytes":2837,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":4845,"get_count":3,"put_bytes":2845,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":4853,"get_count":3,"put_bytes":2853,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":4861,"get_count":3,"put_bytes":2861,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":4869,"get_count":3,"put_bytes":2935,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":4943,"get_count":3,"put_bytes":2943,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":4951,"get_count":3,"put_bytes":2951,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":4959,"get_count":3,"put_bytes":2960,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":4968,"get_count":3,"put_bytes":2968,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":4976,"get_count":3,"put_bytes":2976,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":4984,"get_count":3,"put_bytes":2984,"put_count":2}} -{"i":71,"series":2,"stats":{"get_bytes":4992,"get_count":3,"put_bytes":2992,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":5000,"get_count":3,"put_bytes":3066,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":5074,"get_count":3,"put_bytes":3074,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":5082,"get_count":3,"put_bytes":3082,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":5090,"get_count":3,"put_bytes":3091,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":5099,"get_count":3,"put_bytes":3099,"put_count":2}} -{"i":77,"series":2,"stats":{"get_bytes":5107,"get_count":3,"put_bytes":3107,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":5115,"get_count":3,"put_bytes":3115,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":5123,"get_count":3,"put_bytes":3123,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":5131,"get_count":3,"put_bytes":3198,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":5206,"get_count":3,"put_bytes":3206,"put_count":2}} -{"i":82,"series":2,"stats":{"get_bytes":5214,"get_count":3,"put_bytes":3214,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":5222,"get_count":3,"put_bytes":3223,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":5231,"get_count":3,"put_bytes":3231,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":5239,"get_count":3,"put_bytes":3239,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":5247,"get_count":3,"put_bytes":3247,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":5255,"get_count":3,"put_bytes":3255,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":5263,"get_count":3,"put_bytes":3328,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":5336,"get_count":3,"put_bytes":3336,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":5344,"get_count":3,"put_bytes":3344,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":5352,"get_count":3,"put_bytes":3353,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":5361,"get_count":3,"put_bytes":3361,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":5369,"get_count":3,"put_bytes":3369,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":5377,"get_count":3,"put_bytes":3377,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":5385,"get_count":3,"put_bytes":3385,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":5393,"get_count":3,"put_bytes":3459,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":5467,"get_count":3,"put_bytes":3467,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":5475,"get_count":3,"put_bytes":3475,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":5483,"get_count":3,"put_bytes":3484,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2248,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2157,"get_count":3,"put_bytes":153,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2161,"get_count":3,"put_bytes":157,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2165,"get_count":3,"put_bytes":161,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2169,"get_count":3,"put_bytes":165,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2173,"get_count":3,"put_bytes":169,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2177,"get_count":3,"put_bytes":174,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2182,"get_count":3,"put_bytes":178,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2186,"get_count":3,"put_bytes":230,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":234,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2242,"get_count":3,"put_bytes":238,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":242,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2250,"get_count":3,"put_bytes":246,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2254,"get_count":3,"put_bytes":250,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2258,"get_count":3,"put_bytes":255,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":2263,"get_count":3,"put_bytes":259,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":2267,"get_count":3,"put_bytes":297,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":2305,"get_count":3,"put_bytes":301,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2309,"get_count":3,"put_bytes":305,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2313,"get_count":3,"put_bytes":309,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":2317,"get_count":3,"put_bytes":313,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2321,"get_count":3,"put_bytes":317,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2325,"get_count":3,"put_bytes":322,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2330,"get_count":3,"put_bytes":326,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":2334,"get_count":3,"put_bytes":364,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":2372,"get_count":3,"put_bytes":368,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":2376,"get_count":3,"put_bytes":372,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":2380,"get_count":3,"put_bytes":376,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":2384,"get_count":3,"put_bytes":380,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2388,"get_count":3,"put_bytes":384,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2392,"get_count":3,"put_bytes":389,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":2397,"get_count":3,"put_bytes":393,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":2401,"get_count":3,"put_bytes":430,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":2438,"get_count":3,"put_bytes":434,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":2442,"get_count":3,"put_bytes":438,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":2446,"get_count":3,"put_bytes":442,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":2450,"get_count":3,"put_bytes":446,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":2454,"get_count":3,"put_bytes":450,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":2458,"get_count":3,"put_bytes":455,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":2463,"get_count":3,"put_bytes":459,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":2467,"get_count":3,"put_bytes":497,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":2505,"get_count":3,"put_bytes":501,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":2509,"get_count":3,"put_bytes":505,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":2513,"get_count":3,"put_bytes":509,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":2517,"get_count":3,"put_bytes":513,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":2521,"get_count":3,"put_bytes":517,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":2525,"get_count":3,"put_bytes":522,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":2530,"get_count":3,"put_bytes":526,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":2534,"get_count":3,"put_bytes":565,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":2573,"get_count":3,"put_bytes":569,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":2577,"get_count":3,"put_bytes":573,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":2581,"get_count":3,"put_bytes":577,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":2585,"get_count":3,"put_bytes":581,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":2589,"get_count":3,"put_bytes":585,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":2593,"get_count":3,"put_bytes":590,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":2598,"get_count":3,"put_bytes":594,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":2602,"get_count":3,"put_bytes":632,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":2640,"get_count":3,"put_bytes":636,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":2644,"get_count":3,"put_bytes":640,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":2648,"get_count":3,"put_bytes":644,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":2652,"get_count":3,"put_bytes":648,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":2656,"get_count":3,"put_bytes":652,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":2660,"get_count":3,"put_bytes":657,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":2665,"get_count":3,"put_bytes":661,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":2669,"get_count":3,"put_bytes":699,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":2707,"get_count":3,"put_bytes":703,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":2711,"get_count":3,"put_bytes":707,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":2715,"get_count":3,"put_bytes":711,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":2719,"get_count":3,"put_bytes":715,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":2723,"get_count":3,"put_bytes":719,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":2727,"get_count":3,"put_bytes":724,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":2732,"get_count":3,"put_bytes":728,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":2736,"get_count":3,"put_bytes":766,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":2774,"get_count":3,"put_bytes":770,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":2778,"get_count":3,"put_bytes":774,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":2782,"get_count":3,"put_bytes":778,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":2786,"get_count":3,"put_bytes":782,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":2790,"get_count":3,"put_bytes":786,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":2794,"get_count":3,"put_bytes":791,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":2799,"get_count":3,"put_bytes":795,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":2803,"get_count":3,"put_bytes":833,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":2841,"get_count":3,"put_bytes":837,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":2845,"get_count":3,"put_bytes":841,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":2849,"get_count":3,"put_bytes":845,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":2853,"get_count":3,"put_bytes":849,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":2857,"get_count":3,"put_bytes":853,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":2861,"get_count":3,"put_bytes":858,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":2866,"get_count":3,"put_bytes":862,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":2870,"get_count":3,"put_bytes":900,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":2908,"get_count":3,"put_bytes":904,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":2912,"get_count":3,"put_bytes":908,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":2916,"get_count":3,"put_bytes":912,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":2920,"get_count":3,"put_bytes":916,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":2924,"get_count":3,"put_bytes":920,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":2928,"get_count":3,"put_bytes":925,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":2933,"get_count":3,"put_bytes":929,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":2937,"get_count":3,"put_bytes":967,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":2975,"get_count":3,"put_bytes":971,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":2979,"get_count":3,"put_bytes":975,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":2983,"get_count":3,"put_bytes":979,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":2987,"get_count":3,"put_bytes":1023,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":3031,"get_count":3,"put_bytes":1027,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3035,"get_count":3,"put_bytes":1031,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1035,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3043,"get_count":3,"put_bytes":1039,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3047,"get_count":3,"put_bytes":1043,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3051,"get_count":3,"put_bytes":1048,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":3056,"get_count":3,"put_bytes":1052,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":3060,"get_count":3,"put_bytes":1090,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":3098,"get_count":3,"put_bytes":1094,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":3102,"get_count":3,"put_bytes":1098,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":3106,"get_count":3,"put_bytes":1102,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":3110,"get_count":3,"put_bytes":1106,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":3114,"get_count":3,"put_bytes":1110,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":3118,"get_count":3,"put_bytes":1115,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":3123,"get_count":3,"put_bytes":1119,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1156,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":3164,"get_count":3,"put_bytes":1160,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":3168,"get_count":3,"put_bytes":1164,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":3172,"get_count":3,"put_bytes":1168,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":3176,"get_count":3,"put_bytes":1172,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":3180,"get_count":3,"put_bytes":1176,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":3184,"get_count":3,"put_bytes":1181,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":3189,"get_count":3,"put_bytes":1185,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":3193,"get_count":3,"put_bytes":1223,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":3231,"get_count":3,"put_bytes":1227,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":3235,"get_count":3,"put_bytes":1231,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":3239,"get_count":3,"put_bytes":1235,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":3243,"get_count":3,"put_bytes":1239,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":3247,"get_count":3,"put_bytes":1243,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":3251,"get_count":3,"put_bytes":1248,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":3256,"get_count":3,"put_bytes":1252,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1290,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":3298,"get_count":3,"put_bytes":1294,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":3302,"get_count":3,"put_bytes":1298,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":3306,"get_count":3,"put_bytes":1302,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":3310,"get_count":3,"put_bytes":1306,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":3314,"get_count":3,"put_bytes":1310,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":3318,"get_count":3,"put_bytes":1315,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":3323,"get_count":3,"put_bytes":1319,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":3327,"get_count":3,"put_bytes":1357,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":3365,"get_count":3,"put_bytes":1361,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":3369,"get_count":3,"put_bytes":1365,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":3373,"get_count":3,"put_bytes":1369,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":3377,"get_count":3,"put_bytes":1373,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":3381,"get_count":3,"put_bytes":1377,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":3385,"get_count":3,"put_bytes":1382,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":3390,"get_count":3,"put_bytes":1386,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":3394,"get_count":3,"put_bytes":1426,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":3434,"get_count":3,"put_bytes":1430,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":3438,"get_count":3,"put_bytes":1434,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":3442,"get_count":3,"put_bytes":1438,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":3446,"get_count":3,"put_bytes":1442,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":3450,"get_count":3,"put_bytes":1446,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":3454,"get_count":3,"put_bytes":1451,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":3459,"get_count":3,"put_bytes":1455,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":3463,"get_count":3,"put_bytes":1493,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":3501,"get_count":3,"put_bytes":1497,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":3505,"get_count":3,"put_bytes":1501,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":3509,"get_count":3,"put_bytes":1505,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":3513,"get_count":3,"put_bytes":1509,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":3517,"get_count":3,"put_bytes":1513,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":3521,"get_count":3,"put_bytes":1518,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":3526,"get_count":3,"put_bytes":1522,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1560,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":3568,"get_count":3,"put_bytes":1564,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":3572,"get_count":3,"put_bytes":1568,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":3576,"get_count":3,"put_bytes":1572,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":3580,"get_count":3,"put_bytes":1576,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":3584,"get_count":3,"put_bytes":1580,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":3588,"get_count":3,"put_bytes":1585,"put_count":2}} +{"i":71,"series":2,"stats":{"get_bytes":3593,"get_count":3,"put_bytes":1589,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":3597,"get_count":3,"put_bytes":1627,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":3635,"get_count":3,"put_bytes":1631,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":3639,"get_count":3,"put_bytes":1635,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":3643,"get_count":3,"put_bytes":1639,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":3647,"get_count":3,"put_bytes":1643,"put_count":2}} +{"i":77,"series":2,"stats":{"get_bytes":3651,"get_count":3,"put_bytes":1647,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":3655,"get_count":3,"put_bytes":1652,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":3660,"get_count":3,"put_bytes":1656,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":3664,"get_count":3,"put_bytes":1695,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":3703,"get_count":3,"put_bytes":1699,"put_count":2}} +{"i":82,"series":2,"stats":{"get_bytes":3707,"get_count":3,"put_bytes":1703,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":3711,"get_count":3,"put_bytes":1707,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":3715,"get_count":3,"put_bytes":1711,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":3719,"get_count":3,"put_bytes":1715,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":3723,"get_count":3,"put_bytes":1720,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":3728,"get_count":3,"put_bytes":1724,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":3732,"get_count":3,"put_bytes":1761,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":3769,"get_count":3,"put_bytes":1765,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":3773,"get_count":3,"put_bytes":1769,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":3777,"get_count":3,"put_bytes":1773,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":3781,"get_count":3,"put_bytes":1777,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":3785,"get_count":3,"put_bytes":1781,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":3789,"get_count":3,"put_bytes":1786,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":3794,"get_count":3,"put_bytes":1790,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":3798,"get_count":3,"put_bytes":1828,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":3836,"get_count":3,"put_bytes":1832,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1836,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":3844,"get_count":3,"put_bytes":1840,"put_count":2}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index 4effdef85d5d7d63f55cf0b921c4f5fbdb4e8705..f1a71469b5a0ab72d9be108259650f8224d042c1 100644 GIT binary patch literal 15633 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 VH^NbISmkY-rmC(=g);cf{{oF$sty1E literal 15926 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 Pu_B#&hIeb!p^^U|Rz6Ek diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 7bca6192f..78ca7a2e0 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3912,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":3821,"get_count":3,"put_bytes":3429,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":5437,"get_count":3,"put_bytes":5114,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":7122,"get_count":3,"put_bytes":6724,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":8732,"get_count":3,"put_bytes":8406,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":10414,"get_count":3,"put_bytes":10020,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":12028,"get_count":3,"put_bytes":11698,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":13706,"get_count":3,"put_bytes":13310,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":15318,"get_count":3,"put_bytes":14988,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":16996,"get_count":3,"put_bytes":16743,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":17641,"get_count":3,"put_bytes":17309,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":19317,"get_count":3,"put_bytes":18918,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":20926,"get_count":3,"put_bytes":20595,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":22603,"get_count":3,"put_bytes":22266,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":23729,"get_count":3,"put_bytes":23397,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":25405,"get_count":3,"put_bytes":25005,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":27013,"get_count":3,"put_bytes":26761,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":28211,"get_count":3,"put_bytes":28040,"put_count":5}} -{"i":18,"series":1,"stats":{"get_bytes":28369,"get_count":3,"put_bytes":28036,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":30044,"get_count":3,"put_bytes":29721,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":31730,"get_count":4,"put_bytes":31397,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":32710,"get_count":3,"put_bytes":32374,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":33832,"get_count":3,"put_bytes":33499,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":36065,"get_count":4,"put_bytes":35661,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":36980,"get_count":3,"put_bytes":36718,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":38173,"get_count":3,"put_bytes":37840,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":39297,"get_count":3,"put_bytes":38961,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":41519,"get_count":4,"put_bytes":41283,"put_count":5}} -{"i":28,"series":1,"stats":{"get_bytes":41480,"get_count":3,"put_bytes":41218,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":43786,"get_count":5,"put_bytes":43606,"put_count":7}} -{"i":30,"series":1,"stats":{"get_bytes":43118,"get_count":4,"put_bytes":43018,"put_count":6}} -{"i":31,"series":1,"stats":{"get_bytes":43218,"get_count":4,"put_bytes":43186,"put_count":8}} -{"i":32,"series":1,"stats":{"get_bytes":42426,"get_count":4,"put_bytes":42162,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":44039,"get_count":5,"put_bytes":43764,"put_count":6}} -{"i":34,"series":1,"stats":{"get_bytes":44423,"get_count":5,"put_bytes":44174,"put_count":5}} -{"i":35,"series":1,"stats":{"get_bytes":45353,"get_count":5,"put_bytes":45109,"put_count":6}} -{"i":36,"series":1,"stats":{"get_bytes":45723,"get_count":5,"put_bytes":45463,"put_count":5}} -{"i":37,"series":1,"stats":{"get_bytes":47473,"get_count":6,"put_bytes":47306,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":47642,"get_count":6,"put_bytes":47443,"put_count":7}} -{"i":39,"series":1,"stats":{"get_bytes":46309,"get_count":4,"put_bytes":46072,"put_count":5}} -{"i":40,"series":1,"stats":{"get_bytes":48898,"get_count":7,"put_bytes":48642,"put_count":7}} -{"i":41,"series":1,"stats":{"get_bytes":48038,"get_count":5,"put_bytes":47868,"put_count":7}} -{"i":42,"series":1,"stats":{"get_bytes":48212,"get_count":5,"put_bytes":48102,"put_count":7}} -{"i":43,"series":1,"stats":{"get_bytes":48723,"get_count":6,"put_bytes":48521,"put_count":8}} -{"i":44,"series":1,"stats":{"get_bytes":49292,"get_count":6,"put_bytes":49331,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":49823,"get_count":9,"put_bytes":49424,"put_count":8}} -{"i":46,"series":1,"stats":{"get_bytes":48155,"get_count":5,"put_bytes":47899,"put_count":5}} -{"i":47,"series":1,"stats":{"get_bytes":49474,"get_count":5,"put_bytes":49305,"put_count":7}} -{"i":48,"series":1,"stats":{"get_bytes":49929,"get_count":6,"put_bytes":49896,"put_count":9}} -{"i":49,"series":1,"stats":{"get_bytes":50099,"get_count":7,"put_bytes":49754,"put_count":7}} -{"i":50,"series":1,"stats":{"get_bytes":51110,"get_count":7,"put_bytes":50850,"put_count":7}} -{"i":51,"series":1,"stats":{"get_bytes":52963,"get_count":9,"put_bytes":52725,"put_count":10}} -{"i":52,"series":1,"stats":{"get_bytes":50349,"get_count":5,"put_bytes":50387,"put_count":9}} -{"i":53,"series":1,"stats":{"get_bytes":51410,"get_count":8,"put_bytes":51336,"put_count":11}} -{"i":54,"series":1,"stats":{"get_bytes":50046,"get_count":7,"put_bytes":49875,"put_count":8}} -{"i":55,"series":1,"stats":{"get_bytes":49946,"get_count":7,"put_bytes":49782,"put_count":9}} -{"i":56,"series":1,"stats":{"get_bytes":50845,"get_count":9,"put_bytes":50590,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":52044,"get_count":9,"put_bytes":51806,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":51437,"get_count":8,"put_bytes":51425,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":49586,"get_count":7,"put_bytes":49272,"put_count":7}} -{"i":60,"series":1,"stats":{"get_bytes":52768,"get_count":9,"put_bytes":52444,"put_count":8}} -{"i":61,"series":1,"stats":{"get_bytes":52013,"get_count":8,"put_bytes":51786,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":53939,"get_count":10,"put_bytes":53845,"put_count":12}} -{"i":63,"series":1,"stats":{"get_bytes":52804,"get_count":9,"put_bytes":52642,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":52154,"get_count":8,"put_bytes":51820,"put_count":7}} -{"i":65,"series":1,"stats":{"get_bytes":52614,"get_count":7,"put_bytes":52656,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":50962,"get_count":9,"put_bytes":50705,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":53823,"get_count":11,"put_bytes":53581,"put_count":12}} -{"i":68,"series":1,"stats":{"get_bytes":52595,"get_count":10,"put_bytes":52419,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":51926,"get_count":9,"put_bytes":51755,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":52243,"get_count":9,"put_bytes":52229,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":51630,"get_count":10,"put_bytes":51308,"put_count":10}} -{"i":72,"series":1,"stats":{"get_bytes":53095,"get_count":12,"put_bytes":52992,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":52134,"get_count":12,"put_bytes":51895,"put_count":13}} -{"i":74,"series":1,"stats":{"get_bytes":49757,"get_count":8,"put_bytes":49859,"put_count":13}} -{"i":75,"series":1,"stats":{"get_bytes":51321,"get_count":13,"put_bytes":51005,"put_count":13}} -{"i":76,"series":1,"stats":{"get_bytes":48102,"get_count":9,"put_bytes":48106,"put_count":12}} -{"i":77,"series":1,"stats":{"get_bytes":50778,"get_count":14,"put_bytes":50483,"put_count":14}} -{"i":78,"series":1,"stats":{"get_bytes":48957,"get_count":12,"put_bytes":48854,"put_count":14}} -{"i":79,"series":1,"stats":{"get_bytes":49382,"get_count":13,"put_bytes":49128,"put_count":14}} -{"i":80,"series":1,"stats":{"get_bytes":47420,"get_count":10,"put_bytes":47233,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":47360,"get_count":11,"put_bytes":47204,"put_count":13}} -{"i":82,"series":1,"stats":{"get_bytes":46883,"get_count":11,"put_bytes":46556,"put_count":10}} -{"i":83,"series":1,"stats":{"get_bytes":49108,"get_count":12,"put_bytes":48861,"put_count":13}} -{"i":84,"series":1,"stats":{"get_bytes":49387,"get_count":14,"put_bytes":49309,"put_count":16}} -{"i":85,"series":1,"stats":{"get_bytes":46118,"get_count":11,"put_bytes":45875,"put_count":12}} -{"i":86,"series":1,"stats":{"get_bytes":47610,"get_count":12,"put_bytes":47432,"put_count":13}} -{"i":87,"series":1,"stats":{"get_bytes":45960,"get_count":13,"put_bytes":45799,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":50335,"get_count":17,"put_bytes":50021,"put_count":16}} -{"i":89,"series":1,"stats":{"get_bytes":45108,"get_count":11,"put_bytes":44947,"put_count":13}} -{"i":90,"series":1,"stats":{"get_bytes":45487,"get_count":13,"put_bytes":45398,"put_count":15}} -{"i":91,"series":1,"stats":{"get_bytes":43583,"get_count":12,"put_bytes":43415,"put_count":14}} -{"i":92,"series":1,"stats":{"get_bytes":42670,"get_count":11,"put_bytes":42654,"put_count":14}} -{"i":93,"series":1,"stats":{"get_bytes":41217,"get_count":11,"put_bytes":41122,"put_count":14}} -{"i":94,"series":1,"stats":{"get_bytes":42292,"get_count":14,"put_bytes":42122,"put_count":15}} -{"i":95,"series":1,"stats":{"get_bytes":42384,"get_count":14,"put_bytes":42068,"put_count":14}} -{"i":96,"series":1,"stats":{"get_bytes":42746,"get_count":15,"put_bytes":42433,"put_count":14}} -{"i":97,"series":1,"stats":{"get_bytes":41583,"get_count":13,"put_bytes":41273,"put_count":13}} -{"i":98,"series":1,"stats":{"get_bytes":40335,"get_count":12,"put_bytes":40160,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":40373,"get_count":12,"put_bytes":40203,"put_count":14}} -{"i":0,"series":2,"stats":{"get_bytes":38650,"get_count":11,"put_bytes":38481,"put_count":12}} -{"i":1,"series":2,"stats":{"get_bytes":42294,"get_count":16,"put_bytes":41976,"put_count":16}} -{"i":2,"series":2,"stats":{"get_bytes":40331,"get_count":14,"put_bytes":40089,"put_count":14}} -{"i":3,"series":2,"stats":{"get_bytes":43273,"get_count":15,"put_bytes":42957,"put_count":15}} -{"i":4,"series":2,"stats":{"get_bytes":40372,"get_count":13,"put_bytes":40131,"put_count":13}} -{"i":5,"series":2,"stats":{"get_bytes":36868,"get_count":10,"put_bytes":36631,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":39988,"get_count":13,"put_bytes":39743,"put_count":13}} -{"i":7,"series":2,"stats":{"get_bytes":40029,"get_count":14,"put_bytes":39702,"put_count":14}} -{"i":8,"series":2,"stats":{"get_bytes":38563,"get_count":12,"put_bytes":38318,"put_count":12}} -{"i":9,"series":2,"stats":{"get_bytes":41430,"get_count":14,"put_bytes":41036,"put_count":13}} -{"i":10,"series":2,"stats":{"get_bytes":41482,"get_count":14,"put_bytes":41300,"put_count":15}} -{"i":11,"series":2,"stats":{"get_bytes":37884,"get_count":12,"put_bytes":37647,"put_count":13}} -{"i":12,"series":2,"stats":{"get_bytes":39680,"get_count":14,"put_bytes":39497,"put_count":15}} -{"i":13,"series":2,"stats":{"get_bytes":36853,"get_count":11,"put_bytes":36618,"put_count":12}} -{"i":14,"series":2,"stats":{"get_bytes":42290,"get_count":16,"put_bytes":42048,"put_count":16}} -{"i":15,"series":2,"stats":{"get_bytes":39566,"get_count":13,"put_bytes":39258,"put_count":13}} -{"i":16,"series":2,"stats":{"get_bytes":40741,"get_count":15,"put_bytes":40425,"put_count":14}} -{"i":17,"series":2,"stats":{"get_bytes":41255,"get_count":16,"put_bytes":40959,"put_count":16}} -{"i":18,"series":2,"stats":{"get_bytes":38901,"get_count":14,"put_bytes":38734,"put_count":15}} -{"i":19,"series":2,"stats":{"get_bytes":37376,"get_count":14,"put_bytes":37045,"put_count":14}} -{"i":20,"series":2,"stats":{"get_bytes":41496,"get_count":16,"put_bytes":41170,"put_count":15}} -{"i":21,"series":2,"stats":{"get_bytes":39857,"get_count":16,"put_bytes":39544,"put_count":16}} -{"i":22,"series":2,"stats":{"get_bytes":38527,"get_count":12,"put_bytes":38500,"put_count":15}} -{"i":23,"series":2,"stats":{"get_bytes":35265,"get_count":13,"put_bytes":34950,"put_count":13}} -{"i":24,"series":2,"stats":{"get_bytes":37674,"get_count":17,"put_bytes":37364,"put_count":16}} -{"i":25,"series":2,"stats":{"get_bytes":38042,"get_count":16,"put_bytes":37734,"put_count":16}} -{"i":26,"series":2,"stats":{"get_bytes":35481,"get_count":12,"put_bytes":35382,"put_count":14}} -{"i":27,"series":2,"stats":{"get_bytes":36320,"get_count":15,"put_bytes":36025,"put_count":15}} -{"i":28,"series":2,"stats":{"get_bytes":37197,"get_count":15,"put_bytes":36879,"put_count":14}} -{"i":29,"series":2,"stats":{"get_bytes":35853,"get_count":15,"put_bytes":35606,"put_count":16}} -{"i":30,"series":2,"stats":{"get_bytes":36895,"get_count":15,"put_bytes":36648,"put_count":15}} -{"i":31,"series":2,"stats":{"get_bytes":34065,"get_count":13,"put_bytes":33903,"put_count":15}} -{"i":32,"series":2,"stats":{"get_bytes":33984,"get_count":14,"put_bytes":33808,"put_count":15}} -{"i":33,"series":2,"stats":{"get_bytes":33278,"get_count":15,"put_bytes":32962,"put_count":15}} -{"i":34,"series":2,"stats":{"get_bytes":34709,"get_count":15,"put_bytes":34464,"put_count":15}} -{"i":35,"series":2,"stats":{"get_bytes":36572,"get_count":16,"put_bytes":36254,"put_count":16}} -{"i":36,"series":2,"stats":{"get_bytes":33619,"get_count":15,"put_bytes":33441,"put_count":16}} -{"i":37,"series":2,"stats":{"get_bytes":34097,"get_count":16,"put_bytes":33725,"put_count":15}} -{"i":38,"series":2,"stats":{"get_bytes":33592,"get_count":16,"put_bytes":33339,"put_count":16}} -{"i":39,"series":2,"stats":{"get_bytes":35567,"get_count":16,"put_bytes":35261,"put_count":16}} -{"i":40,"series":2,"stats":{"get_bytes":30579,"get_count":14,"put_bytes":30491,"put_count":16}} -{"i":41,"series":2,"stats":{"get_bytes":27705,"get_count":11,"put_bytes":27532,"put_count":13}} -{"i":42,"series":2,"stats":{"get_bytes":30950,"get_count":17,"put_bytes":30633,"put_count":16}} -{"i":43,"series":2,"stats":{"get_bytes":30317,"get_count":14,"put_bytes":30072,"put_count":15}} -{"i":44,"series":2,"stats":{"get_bytes":29537,"get_count":15,"put_bytes":29370,"put_count":16}} -{"i":45,"series":2,"stats":{"get_bytes":32412,"get_count":17,"put_bytes":32018,"put_count":16}} -{"i":46,"series":2,"stats":{"get_bytes":28608,"get_count":16,"put_bytes":28361,"put_count":16}} -{"i":47,"series":2,"stats":{"get_bytes":27581,"get_count":17,"put_bytes":27197,"put_count":16}} -{"i":48,"series":2,"stats":{"get_bytes":32371,"get_count":17,"put_bytes":32049,"put_count":16}} -{"i":49,"series":2,"stats":{"get_bytes":31526,"get_count":16,"put_bytes":31132,"put_count":15}} -{"i":50,"series":2,"stats":{"get_bytes":31811,"get_count":17,"put_bytes":31494,"put_count":16}} -{"i":51,"series":2,"stats":{"get_bytes":31517,"get_count":16,"put_bytes":31131,"put_count":15}} -{"i":52,"series":2,"stats":{"get_bytes":31725,"get_count":17,"put_bytes":31402,"put_count":16}} -{"i":53,"series":2,"stats":{"get_bytes":31780,"get_count":17,"put_bytes":31395,"put_count":16}} -{"i":54,"series":2,"stats":{"get_bytes":31652,"get_count":16,"put_bytes":31342,"put_count":15}} -{"i":55,"series":2,"stats":{"get_bytes":31921,"get_count":17,"put_bytes":31527,"put_count":16}} -{"i":56,"series":2,"stats":{"get_bytes":31259,"get_count":15,"put_bytes":31018,"put_count":15}} -{"i":57,"series":2,"stats":{"get_bytes":29505,"get_count":14,"put_bytes":29189,"put_count":14}} -{"i":58,"series":2,"stats":{"get_bytes":30962,"get_count":15,"put_bytes":30772,"put_count":16}} -{"i":59,"series":2,"stats":{"get_bytes":30109,"get_count":16,"put_bytes":29714,"put_count":15}} -{"i":60,"series":2,"stats":{"get_bytes":31465,"get_count":17,"put_bytes":31158,"put_count":16}} -{"i":61,"series":2,"stats":{"get_bytes":32093,"get_count":17,"put_bytes":31703,"put_count":16}} -{"i":62,"series":2,"stats":{"get_bytes":31197,"get_count":16,"put_bytes":30874,"put_count":15}} -{"i":63,"series":2,"stats":{"get_bytes":31343,"get_count":17,"put_bytes":30951,"put_count":16}} -{"i":64,"series":2,"stats":{"get_bytes":29072,"get_count":16,"put_bytes":28830,"put_count":16}} -{"i":65,"series":2,"stats":{"get_bytes":30847,"get_count":17,"put_bytes":30455,"put_count":16}} -{"i":66,"series":2,"stats":{"get_bytes":31112,"get_count":16,"put_bytes":30868,"put_count":16}} -{"i":67,"series":2,"stats":{"get_bytes":27550,"get_count":16,"put_bytes":27159,"put_count":15}} -{"i":68,"series":2,"stats":{"get_bytes":31056,"get_count":17,"put_bytes":30738,"put_count":16}} -{"i":69,"series":2,"stats":{"get_bytes":32050,"get_count":16,"put_bytes":31734,"put_count":16}} -{"i":70,"series":2,"stats":{"get_bytes":30046,"get_count":17,"put_bytes":29725,"put_count":16}} -{"i":71,"series":2,"stats":{"get_bytes":31727,"get_count":17,"put_bytes":31332,"put_count":16}} -{"i":72,"series":2,"stats":{"get_bytes":30131,"get_count":16,"put_bytes":29905,"put_count":16}} -{"i":73,"series":2,"stats":{"get_bytes":30966,"get_count":17,"put_bytes":30582,"put_count":16}} -{"i":74,"series":2,"stats":{"get_bytes":28860,"get_count":16,"put_bytes":28618,"put_count":16}} -{"i":75,"series":2,"stats":{"get_bytes":29414,"get_count":16,"put_bytes":29018,"put_count":15}} -{"i":76,"series":2,"stats":{"get_bytes":30144,"get_count":17,"put_bytes":29821,"put_count":16}} -{"i":77,"series":2,"stats":{"get_bytes":30076,"get_count":16,"put_bytes":29683,"put_count":15}} -{"i":78,"series":2,"stats":{"get_bytes":29450,"get_count":16,"put_bytes":29134,"put_count":15}} -{"i":79,"series":2,"stats":{"get_bytes":31289,"get_count":17,"put_bytes":30908,"put_count":16}} -{"i":80,"series":2,"stats":{"get_bytes":29751,"get_count":17,"put_bytes":29433,"put_count":16}} -{"i":81,"series":2,"stats":{"get_bytes":27147,"get_count":16,"put_bytes":26760,"put_count":15}} -{"i":82,"series":2,"stats":{"get_bytes":30001,"get_count":17,"put_bytes":29682,"put_count":16}} -{"i":83,"series":2,"stats":{"get_bytes":31564,"get_count":17,"put_bytes":31174,"put_count":16}} -{"i":84,"series":2,"stats":{"get_bytes":26734,"get_count":14,"put_bytes":26488,"put_count":14}} -{"i":85,"series":2,"stats":{"get_bytes":32243,"get_count":17,"put_bytes":31856,"put_count":16}} -{"i":86,"series":2,"stats":{"get_bytes":32524,"get_count":16,"put_bytes":32220,"put_count":15}} -{"i":87,"series":2,"stats":{"get_bytes":32914,"get_count":17,"put_bytes":32527,"put_count":16}} -{"i":88,"series":2,"stats":{"get_bytes":31380,"get_count":17,"put_bytes":31059,"put_count":16}} -{"i":89,"series":2,"stats":{"get_bytes":34109,"get_count":17,"put_bytes":33722,"put_count":16}} -{"i":90,"series":2,"stats":{"get_bytes":33451,"get_count":16,"put_bytes":33126,"put_count":15}} -{"i":91,"series":2,"stats":{"get_bytes":33572,"get_count":17,"put_bytes":33177,"put_count":16}} -{"i":92,"series":2,"stats":{"get_bytes":32091,"get_count":15,"put_bytes":31847,"put_count":15}} -{"i":93,"series":2,"stats":{"get_bytes":31736,"get_count":16,"put_bytes":31353,"put_count":15}} -{"i":94,"series":2,"stats":{"get_bytes":31360,"get_count":15,"put_bytes":31103,"put_count":15}} -{"i":95,"series":2,"stats":{"get_bytes":31942,"get_count":16,"put_bytes":31547,"put_count":15}} -{"i":96,"series":2,"stats":{"get_bytes":27298,"get_count":15,"put_bytes":26984,"put_count":14}} -{"i":97,"series":2,"stats":{"get_bytes":31832,"get_count":17,"put_bytes":31443,"put_count":16}} -{"i":98,"series":2,"stats":{"get_bytes":31690,"get_count":16,"put_bytes":31368,"put_count":15}} -{"i":99,"series":2,"stats":{"get_bytes":33795,"get_count":17,"put_bytes":33409,"put_count":16}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3078,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2987,"get_count":3,"put_bytes":1802,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":3810,"get_count":3,"put_bytes":2658,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":4666,"get_count":3,"put_bytes":3475,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":5483,"get_count":3,"put_bytes":4328,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":6336,"get_count":3,"put_bytes":5149,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":7157,"get_count":3,"put_bytes":5998,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":8006,"get_count":3,"put_bytes":6817,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":8825,"get_count":3,"put_bytes":7666,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":9674,"get_count":3,"put_bytes":8628,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":10046,"get_count":3,"put_bytes":8885,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":10893,"get_count":3,"put_bytes":9701,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":11709,"get_count":3,"put_bytes":10549,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":12557,"get_count":3,"put_bytes":11427,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":13150,"get_count":3,"put_bytes":11989,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":13997,"get_count":3,"put_bytes":12804,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":14812,"get_count":3,"put_bytes":13731,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":15440,"get_count":3,"put_bytes":14476,"put_count":5}} +{"i":18,"series":1,"stats":{"get_bytes":15585,"get_count":3,"put_bytes":14423,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":16431,"get_count":3,"put_bytes":15315,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":17324,"get_count":4,"put_bytes":16162,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":17800,"get_count":3,"put_bytes":16671,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":18389,"get_count":3,"put_bytes":17227,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":19533,"get_count":4,"put_bytes":18336,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":19979,"get_count":3,"put_bytes":18888,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":20603,"get_count":3,"put_bytes":19477,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":21194,"get_count":3,"put_bytes":20029,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":22327,"get_count":4,"put_bytes":21298,"put_count":5}} +{"i":28,"series":1,"stats":{"get_bytes":22339,"get_count":3,"put_bytes":21248,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":23555,"get_count":5,"put_bytes":22582,"put_count":7}} +{"i":30,"series":1,"stats":{"get_bytes":23263,"get_count":4,"put_bytes":22334,"put_count":6}} +{"i":31,"series":1,"stats":{"get_bytes":23378,"get_count":4,"put_bytes":22553,"put_count":8}} +{"i":32,"series":1,"stats":{"get_bytes":23090,"get_count":4,"put_bytes":21997,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":23938,"get_count":5,"put_bytes":22870,"put_count":6}} +{"i":34,"series":1,"stats":{"get_bytes":24180,"get_count":5,"put_bytes":23102,"put_count":5}} +{"i":35,"series":1,"stats":{"get_bytes":24671,"get_count":5,"put_bytes":23634,"put_count":6}} +{"i":36,"series":1,"stats":{"get_bytes":24896,"get_count":5,"put_bytes":23807,"put_count":5}} +{"i":37,"series":1,"stats":{"get_bytes":25817,"get_count":6,"put_bytes":24857,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":25971,"get_count":6,"put_bytes":24943,"put_count":7}} +{"i":39,"series":1,"stats":{"get_bytes":25304,"get_count":4,"put_bytes":24274,"put_count":5}} +{"i":40,"series":1,"stats":{"get_bytes":26709,"get_count":7,"put_bytes":25623,"put_count":7}} +{"i":41,"series":1,"stats":{"get_bytes":26254,"get_count":5,"put_bytes":25291,"put_count":7}} +{"i":42,"series":1,"stats":{"get_bytes":26413,"get_count":5,"put_bytes":25474,"put_count":7}} +{"i":43,"series":1,"stats":{"get_bytes":26743,"get_count":6,"put_bytes":25748,"put_count":8}} +{"i":44,"series":1,"stats":{"get_bytes":27104,"get_count":6,"put_bytes":26314,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":27521,"get_count":9,"put_bytes":26329,"put_count":8}} +{"i":46,"series":1,"stats":{"get_bytes":26616,"get_count":5,"put_bytes":25531,"put_count":5}} +{"i":47,"series":1,"stats":{"get_bytes":27299,"get_count":5,"put_bytes":26337,"put_count":7}} +{"i":48,"series":1,"stats":{"get_bytes":27613,"get_count":6,"put_bytes":26751,"put_count":9}} +{"i":49,"series":1,"stats":{"get_bytes":27793,"get_count":7,"put_bytes":26655,"put_count":7}} +{"i":50,"series":1,"stats":{"get_bytes":28336,"get_count":7,"put_bytes":27247,"put_count":7}} +{"i":51,"series":1,"stats":{"get_bytes":29297,"get_count":9,"put_bytes":28266,"put_count":10}} +{"i":52,"series":1,"stats":{"get_bytes":27964,"get_count":5,"put_bytes":27173,"put_count":9}} +{"i":53,"series":1,"stats":{"get_bytes":28648,"get_count":8,"put_bytes":27781,"put_count":11}} +{"i":54,"series":1,"stats":{"get_bytes":28050,"get_count":7,"put_bytes":27050,"put_count":8}} +{"i":55,"series":1,"stats":{"get_bytes":28030,"get_count":7,"put_bytes":27073,"put_count":9}} +{"i":56,"series":1,"stats":{"get_bytes":28586,"get_count":9,"put_bytes":27502,"put_count":9}} +{"i":57,"series":1,"stats":{"get_bytes":29215,"get_count":9,"put_bytes":28184,"put_count":10}} +{"i":58,"series":1,"stats":{"get_bytes":28920,"get_count":8,"put_bytes":28079,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":28057,"get_count":7,"put_bytes":26950,"put_count":7}} +{"i":60,"series":1,"stats":{"get_bytes":29731,"get_count":9,"put_bytes":28578,"put_count":8}} +{"i":61,"series":1,"stats":{"get_bytes":29318,"get_count":8,"put_bytes":28298,"put_count":9}} +{"i":62,"series":1,"stats":{"get_bytes":30382,"get_count":10,"put_bytes":29459,"put_count":12}} +{"i":63,"series":1,"stats":{"get_bytes":29847,"get_count":9,"put_bytes":28892,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":29570,"get_count":8,"put_bytes":28407,"put_count":7}} +{"i":65,"series":1,"stats":{"get_bytes":29785,"get_count":7,"put_bytes":29034,"put_count":12}} +{"i":66,"series":1,"stats":{"get_bytes":29093,"get_count":9,"put_bytes":28007,"put_count":9}} +{"i":67,"series":1,"stats":{"get_bytes":30609,"get_count":11,"put_bytes":29574,"put_count":12}} +{"i":68,"series":1,"stats":{"get_bytes":30016,"get_count":10,"put_bytes":29011,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":29685,"get_count":9,"put_bytes":28721,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":29921,"get_count":9,"put_bytes":29078,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":29713,"get_count":10,"put_bytes":28598,"put_count":10}} +{"i":72,"series":1,"stats":{"get_bytes":30516,"get_count":12,"put_bytes":29584,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":30085,"get_count":12,"put_bytes":29053,"put_count":13}} +{"i":74,"series":1,"stats":{"get_bytes":28862,"get_count":8,"put_bytes":28135,"put_count":13}} +{"i":75,"series":1,"stats":{"get_bytes":29861,"get_count":13,"put_bytes":28752,"put_count":13}} +{"i":76,"series":1,"stats":{"get_bytes":28179,"get_count":9,"put_bytes":27354,"put_count":12}} +{"i":77,"series":1,"stats":{"get_bytes":29704,"get_count":14,"put_bytes":28616,"put_count":14}} +{"i":78,"series":1,"stats":{"get_bytes":28773,"get_count":12,"put_bytes":27841,"put_count":14}} +{"i":79,"series":1,"stats":{"get_bytes":29082,"get_count":13,"put_bytes":28035,"put_count":14}} +{"i":80,"series":1,"stats":{"get_bytes":28081,"get_count":10,"put_bytes":27065,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":28099,"get_count":11,"put_bytes":27150,"put_count":13}} +{"i":82,"series":1,"stats":{"get_bytes":27933,"get_count":11,"put_bytes":26777,"put_count":10}} +{"i":83,"series":1,"stats":{"get_bytes":29068,"get_count":12,"put_bytes":28028,"put_count":13}} +{"i":84,"series":1,"stats":{"get_bytes":29270,"get_count":14,"put_bytes":28363,"put_count":16}} +{"i":85,"series":1,"stats":{"get_bytes":27636,"get_count":11,"put_bytes":26600,"put_count":12}} +{"i":86,"series":1,"stats":{"get_bytes":28466,"get_count":12,"put_bytes":27459,"put_count":13}} +{"i":87,"series":1,"stats":{"get_bytes":27673,"get_count":13,"put_bytes":26719,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":30019,"get_count":17,"put_bytes":28876,"put_count":16}} +{"i":89,"series":1,"stats":{"get_bytes":27272,"get_count":11,"put_bytes":26318,"put_count":13}} +{"i":90,"series":1,"stats":{"get_bytes":27576,"get_count":13,"put_bytes":26658,"put_count":15}} +{"i":91,"series":1,"stats":{"get_bytes":26654,"get_count":12,"put_bytes":25693,"put_count":14}} +{"i":92,"series":1,"stats":{"get_bytes":26244,"get_count":11,"put_bytes":25399,"put_count":14}} +{"i":93,"series":1,"stats":{"get_bytes":25588,"get_count":11,"put_bytes":24700,"put_count":14}} +{"i":94,"series":1,"stats":{"get_bytes":26255,"get_count":14,"put_bytes":25256,"put_count":15}} +{"i":95,"series":1,"stats":{"get_bytes":26365,"get_count":14,"put_bytes":25256,"put_count":14}} +{"i":96,"series":1,"stats":{"get_bytes":26582,"get_count":15,"put_bytes":25440,"put_count":14}} +{"i":97,"series":1,"stats":{"get_bytes":25952,"get_count":13,"put_bytes":24849,"put_count":13}} +{"i":98,"series":1,"stats":{"get_bytes":25340,"get_count":12,"put_bytes":24336,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":25400,"get_count":12,"put_bytes":24437,"put_count":14}} +{"i":0,"series":2,"stats":{"get_bytes":24567,"get_count":11,"put_bytes":23564,"put_count":12}} +{"i":1,"series":2,"stats":{"get_bytes":26538,"get_count":16,"put_bytes":25427,"put_count":16}} +{"i":2,"series":2,"stats":{"get_bytes":25530,"get_count":14,"put_bytes":24459,"put_count":14}} +{"i":3,"series":2,"stats":{"get_bytes":27062,"get_count":15,"put_bytes":25953,"put_count":15}} +{"i":4,"series":2,"stats":{"get_bytes":25571,"get_count":13,"put_bytes":24501,"put_count":13}} +{"i":5,"series":2,"stats":{"get_bytes":23770,"get_count":10,"put_bytes":22740,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":25452,"get_count":13,"put_bytes":24378,"put_count":13}} +{"i":7,"series":2,"stats":{"get_bytes":25512,"get_count":14,"put_bytes":24392,"put_count":14}} +{"i":8,"series":2,"stats":{"get_bytes":24742,"get_count":12,"put_bytes":23668,"put_count":12}} +{"i":9,"series":2,"stats":{"get_bytes":26261,"get_count":14,"put_bytes":25074,"put_count":13}} +{"i":10,"series":2,"stats":{"get_bytes":26297,"get_count":14,"put_bytes":25286,"put_count":15}} +{"i":11,"series":2,"stats":{"get_bytes":24466,"get_count":12,"put_bytes":23436,"put_count":13}} +{"i":12,"series":2,"stats":{"get_bytes":25465,"get_count":14,"put_bytes":24453,"put_count":15}} +{"i":13,"series":2,"stats":{"get_bytes":24018,"get_count":11,"put_bytes":22990,"put_count":12}} +{"i":14,"series":2,"stats":{"get_bytes":26909,"get_count":16,"put_bytes":25838,"put_count":16}} +{"i":15,"series":2,"stats":{"get_bytes":25504,"get_count":13,"put_bytes":24403,"put_count":13}} +{"i":16,"series":2,"stats":{"get_bytes":26138,"get_count":15,"put_bytes":24993,"put_count":14}} +{"i":17,"series":2,"stats":{"get_bytes":26410,"get_count":16,"put_bytes":25321,"put_count":16}} +{"i":18,"series":2,"stats":{"get_bytes":25207,"get_count":14,"put_bytes":24211,"put_count":15}} +{"i":19,"series":2,"stats":{"get_bytes":24476,"get_count":14,"put_bytes":23352,"put_count":14}} +{"i":20,"series":2,"stats":{"get_bytes":26632,"get_count":16,"put_bytes":25477,"put_count":15}} +{"i":21,"series":2,"stats":{"get_bytes":25786,"get_count":16,"put_bytes":24680,"put_count":16}} +{"i":22,"series":2,"stats":{"get_bytes":25090,"get_count":12,"put_bytes":24234,"put_count":15}} +{"i":23,"series":2,"stats":{"get_bytes":23529,"get_count":13,"put_bytes":22421,"put_count":13}} +{"i":24,"series":2,"stats":{"get_bytes":24824,"get_count":17,"put_bytes":23685,"put_count":16}} +{"i":25,"series":2,"stats":{"get_bytes":25012,"get_count":16,"put_bytes":23911,"put_count":16}} +{"i":26,"series":2,"stats":{"get_bytes":23668,"get_count":12,"put_bytes":22740,"put_count":14}} +{"i":27,"series":2,"stats":{"get_bytes":24194,"get_count":15,"put_bytes":23106,"put_count":15}} +{"i":28,"series":2,"stats":{"get_bytes":24674,"get_count":15,"put_bytes":23527,"put_count":14}} +{"i":29,"series":2,"stats":{"get_bytes":23989,"get_count":15,"put_bytes":22949,"put_count":16}} +{"i":30,"series":2,"stats":{"get_bytes":24558,"get_count":15,"put_bytes":23482,"put_count":15}} +{"i":31,"series":2,"stats":{"get_bytes":23109,"get_count":13,"put_bytes":22154,"put_count":15}} +{"i":32,"series":2,"stats":{"get_bytes":23134,"get_count":14,"put_bytes":22129,"put_count":15}} +{"i":33,"series":2,"stats":{"get_bytes":22847,"get_count":15,"put_bytes":21738,"put_count":15}} +{"i":34,"series":2,"stats":{"get_bytes":23609,"get_count":15,"put_bytes":22535,"put_count":15}} +{"i":35,"series":2,"stats":{"get_bytes":24581,"get_count":16,"put_bytes":23470,"put_count":16}} +{"i":36,"series":2,"stats":{"get_bytes":23103,"get_count":15,"put_bytes":22096,"put_count":16}} +{"i":37,"series":2,"stats":{"get_bytes":23393,"get_count":16,"put_bytes":22228,"put_count":15}} +{"i":38,"series":2,"stats":{"get_bytes":23134,"get_count":16,"put_bytes":22052,"put_count":16}} +{"i":39,"series":2,"stats":{"get_bytes":24151,"get_count":16,"put_bytes":23052,"put_count":16}} +{"i":40,"series":2,"stats":{"get_bytes":21612,"get_count":14,"put_bytes":20694,"put_count":16}} +{"i":41,"series":2,"stats":{"get_bytes":20186,"get_count":11,"put_bytes":19220,"put_count":13}} +{"i":42,"series":2,"stats":{"get_bytes":21986,"get_count":17,"put_bytes":20840,"put_count":16}} +{"i":43,"series":2,"stats":{"get_bytes":21626,"get_count":14,"put_bytes":20588,"put_count":15}} +{"i":44,"series":2,"stats":{"get_bytes":21285,"get_count":15,"put_bytes":20289,"put_count":16}} +{"i":45,"series":2,"stats":{"get_bytes":22814,"get_count":17,"put_bytes":21627,"put_count":16}} +{"i":46,"series":2,"stats":{"get_bytes":20881,"get_count":16,"put_bytes":19805,"put_count":16}} +{"i":47,"series":2,"stats":{"get_bytes":20388,"get_count":17,"put_bytes":19211,"put_count":16}} +{"i":48,"series":2,"stats":{"get_bytes":22832,"get_count":17,"put_bytes":21681,"put_count":16}} +{"i":49,"series":2,"stats":{"get_bytes":22388,"get_count":16,"put_bytes":21201,"put_count":15}} +{"i":50,"series":2,"stats":{"get_bytes":22525,"get_count":17,"put_bytes":21379,"put_count":16}} +{"i":51,"series":2,"stats":{"get_bytes":22375,"get_count":16,"put_bytes":21196,"put_count":15}} +{"i":52,"series":2,"stats":{"get_bytes":22501,"get_count":17,"put_bytes":21349,"put_count":16}} +{"i":53,"series":2,"stats":{"get_bytes":22512,"get_count":17,"put_bytes":21334,"put_count":16}} +{"i":54,"series":2,"stats":{"get_bytes":22436,"get_count":16,"put_bytes":21297,"put_count":15}} +{"i":55,"series":2,"stats":{"get_bytes":22586,"get_count":17,"put_bytes":21399,"put_count":16}} +{"i":56,"series":2,"stats":{"get_bytes":22228,"get_count":15,"put_bytes":21158,"put_count":15}} +{"i":57,"series":2,"stats":{"get_bytes":21336,"get_count":14,"put_bytes":20227,"put_count":14}} +{"i":58,"series":2,"stats":{"get_bytes":22131,"get_count":15,"put_bytes":21112,"put_count":16}} +{"i":59,"series":2,"stats":{"get_bytes":21749,"get_count":16,"put_bytes":20561,"put_count":15}} +{"i":60,"series":2,"stats":{"get_bytes":22439,"get_count":17,"put_bytes":21303,"put_count":16}} +{"i":61,"series":2,"stats":{"get_bytes":22757,"get_count":17,"put_bytes":21574,"put_count":16}} +{"i":62,"series":2,"stats":{"get_bytes":22301,"get_count":16,"put_bytes":21149,"put_count":15}} +{"i":63,"series":2,"stats":{"get_bytes":22402,"get_count":17,"put_bytes":21217,"put_count":16}} +{"i":64,"series":2,"stats":{"get_bytes":21213,"get_count":16,"put_bytes":20142,"put_count":16}} +{"i":65,"series":2,"stats":{"get_bytes":22162,"get_count":17,"put_bytes":20977,"put_count":16}} +{"i":66,"series":2,"stats":{"get_bytes":22283,"get_count":16,"put_bytes":21210,"put_count":16}} +{"i":67,"series":2,"stats":{"get_bytes":20485,"get_count":16,"put_bytes":19301,"put_count":15}} +{"i":68,"series":2,"stats":{"get_bytes":22289,"get_count":17,"put_bytes":21142,"put_count":16}} +{"i":69,"series":2,"stats":{"get_bytes":22779,"get_count":16,"put_bytes":21670,"put_count":16}} +{"i":70,"series":2,"stats":{"get_bytes":21800,"get_count":17,"put_bytes":20650,"put_count":16}} +{"i":71,"series":2,"stats":{"get_bytes":22659,"get_count":17,"put_bytes":21471,"put_count":16}} +{"i":72,"series":2,"stats":{"get_bytes":21823,"get_count":16,"put_bytes":20768,"put_count":16}} +{"i":73,"series":2,"stats":{"get_bytes":22286,"get_count":17,"put_bytes":21109,"put_count":16}} +{"i":74,"series":2,"stats":{"get_bytes":21193,"get_count":16,"put_bytes":20122,"put_count":16}} +{"i":75,"series":2,"stats":{"get_bytes":21506,"get_count":16,"put_bytes":20317,"put_count":15}} +{"i":76,"series":2,"stats":{"get_bytes":21895,"get_count":17,"put_bytes":20743,"put_count":16}} +{"i":77,"series":2,"stats":{"get_bytes":21845,"get_count":16,"put_bytes":20659,"put_count":15}} +{"i":78,"series":2,"stats":{"get_bytes":21527,"get_count":16,"put_bytes":20382,"put_count":15}} +{"i":79,"series":2,"stats":{"get_bytes":22471,"get_count":17,"put_bytes":21297,"put_count":16}} +{"i":80,"series":2,"stats":{"get_bytes":21700,"get_count":17,"put_bytes":20553,"put_count":16}} +{"i":81,"series":2,"stats":{"get_bytes":20343,"get_count":16,"put_bytes":19163,"put_count":15}} +{"i":82,"series":2,"stats":{"get_bytes":21817,"get_count":17,"put_bytes":20669,"put_count":16}} +{"i":83,"series":2,"stats":{"get_bytes":22620,"get_count":17,"put_bytes":21437,"put_count":16}} +{"i":84,"series":2,"stats":{"get_bytes":20110,"get_count":14,"put_bytes":19035,"put_count":14}} +{"i":85,"series":2,"stats":{"get_bytes":22970,"get_count":17,"put_bytes":21790,"put_count":16}} +{"i":86,"series":2,"stats":{"get_bytes":23108,"get_count":16,"put_bytes":21975,"put_count":15}} +{"i":87,"series":2,"stats":{"get_bytes":23321,"get_count":17,"put_bytes":22141,"put_count":16}} +{"i":88,"series":2,"stats":{"get_bytes":22543,"get_count":17,"put_bytes":21393,"put_count":16}} +{"i":89,"series":2,"stats":{"get_bytes":23930,"get_count":17,"put_bytes":22750,"put_count":16}} +{"i":90,"series":2,"stats":{"get_bytes":23580,"get_count":16,"put_bytes":22426,"put_count":15}} +{"i":91,"series":2,"stats":{"get_bytes":23656,"get_count":17,"put_bytes":22468,"put_count":16}} +{"i":92,"series":2,"stats":{"get_bytes":22874,"get_count":15,"put_bytes":21801,"put_count":15}} +{"i":93,"series":2,"stats":{"get_bytes":22733,"get_count":16,"put_bytes":21557,"put_count":15}} +{"i":94,"series":2,"stats":{"get_bytes":22522,"get_count":15,"put_bytes":21436,"put_count":15}} +{"i":95,"series":2,"stats":{"get_bytes":22862,"get_count":16,"put_bytes":21674,"put_count":15}} +{"i":96,"series":2,"stats":{"get_bytes":20476,"get_count":15,"put_bytes":19333,"put_count":14}} +{"i":97,"series":2,"stats":{"get_bytes":22827,"get_count":17,"put_bytes":21645,"put_count":16}} +{"i":98,"series":2,"stats":{"get_bytes":22734,"get_count":16,"put_bytes":21583,"put_count":15}} +{"i":99,"series":2,"stats":{"get_bytes":23808,"get_count":17,"put_bytes":22629,"put_count":16}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png index dbc6a4dc8c22671f33d10394e65aab5d489fbe5b..93033725a15d865234ab2b95fd64cda95b72b9c2 100644 GIT binary patch literal 22640 zcmb@u1yGzp*Dg4?yIYXpF2NlF1a}yGfB?Y*1osINJOpYo07em-6SKi79v9v?r?0br#8-ot(e(nZm6M(O|i9z{m>5mprdU^IN} z0Ps@9XTaFxGhxDWpvGxX(6b!iF35E++2?dSx~;*!F%0AKayd7M=D84X3_eZ-Os(Xu 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 L*xINBMNE_ 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 BqzwQ7 diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 79f1febd5..2e63785f3 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":34422,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":34828,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":34013,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":34826,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":34293,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":34556,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":34287,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":34157,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":33460,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":34283,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":22348,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":22558,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":22137,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":22553,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":22285,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":22417,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":22277,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":22215,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":21844,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":22274,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":22274,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":22274,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":22274,"get_count":5,"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 index 7fe009b5f869a62ff288e7170b2d47fbcde16bf1..35a91d0ded776ee63d7d366d60c92925c22cbc55 100644 GIT binary patch literal 14691 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 X+^Tq!B2$;+Q!5=UgKOm)u(1CFYd<>l diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index de29b2a9c..df3049207 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":41985,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":42303,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":43413,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":42464,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":42628,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":42223,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":38920,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":41044,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":41767,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":44206,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":46494,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":41220,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":43572,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":44177,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":43275,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":43422,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":42332,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":45153,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":44329,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":42866,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":46412,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":44818,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":43562,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":45033,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":43212,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":43728,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":40515,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":43835,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":44202,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":45078,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":43688,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":42979,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":43977,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":44088,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":43854,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":43535,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":41460,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":45911,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":45199,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":43150,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":45727,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":40999,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":42899,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":41903,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":45315,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":43668,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":39723,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":42515,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":44028,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":43022,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":43982,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":44094,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":43880,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":43470,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":41821,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":40154,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":41801,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":42908,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":43023,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":41210,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":42169,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":43024,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":42774,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":42570,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":42999,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":44269,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":43153,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":42862,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":43850,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":41878,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":44484,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":40793,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":44288,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":44395,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":43814,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":44666,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":42607,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":46162,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":44564,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":43425,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":43026,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":44030,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":42907,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":44060,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":45207,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":41772,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":43834,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":42797,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":46817,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":43456,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":43873,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":43195,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":42641,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":42349,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":43834,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":43847,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":43705,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":42462,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":41813,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":42625,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":26312,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":26495,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":27077,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":26592,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":26689,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":26477,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":24753,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":25823,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":26223,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":27489,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":28656,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":25934,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":27167,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":27444,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":27013,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":27091,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":26524,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":27973,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":27535,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":26799,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":28640,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":27833,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":27163,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":27916,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":27010,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":27265,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":25560,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":27308,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":27479,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":27962,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":27225,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":26852,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":27378,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":27428,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":27328,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":27139,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":26045,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":28405,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":28019,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":26944,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":28278,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":25840,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":26820,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":26281,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":28054,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":27195,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":25147,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":26629,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":27417,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":26878,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":27374,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":27429,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":27334,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":27126,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":26266,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":25388,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":26249,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":26833,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":26880,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":25916,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":26424,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":26880,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":26758,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":26626,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":26858,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":27534,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":26939,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":26785,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":27310,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":26259,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":27617,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":25700,"get_count":12,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":27555,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":27596,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":27276,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":27729,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":26650,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":28509,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":27694,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":27089,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":26880,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":27424,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":26823,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":27393,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":28014,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":26225,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":27294,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":26776,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":28836,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":27116,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":27333,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":26978,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":26692,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":26532,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":27301,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":27301,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":27231,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":26576,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":26263,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":26680,"get_count":15,"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 index 6ab9899cf4dd10a82f1693eb5f33a2c7a18b59a0..55dd6a6994fe145ab216f9aabdcc21009a58785e 100644 GIT binary patch literal 18437 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 T+N2(|opsPu)m150LSg;~pioiq literal 18131 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 a`OT4&QP9Xk_j4H=?&>N!4=a={!~Q>DxJ29l literal 16842 zcmb8X2RvL~+wVPE7=#3)gdhabg@_1=l1TIt#*7x3=$+^#x-=z3bV8!f3?{nKM~xOG zqekx}x**D&J^s&qpZk2ybIyBSA7S>Y*IL)Q*4}%4fAUsW`|(BUE7Twm=%SkH!zUmR zIR*qG^Mf}wS0tFMAKm^dnMw23_vlFCbH%n;Z z1_diN1rsEm1xJEFioqa4Fd>NWBUeZWsH!;A57fJI<%*P)l$Ms3wY7CnP*6%rN?BQ1 zQ&Uq7gFxIMwTDUuKIy+RXi8B?2E~>;>EGEXNr#K& z2w>A@om+v=%T@X-)*8yRG^tic*GYI9o&qiXe|@=Lv)cX*$$R4W(#GtSxV7oW;*UZZ z`)T`zO*LNG!u^K1Lc;!8e?eQ<%a8uNQ#RTibau*5q3#y6aS|1E$wrowSgZxvE8$IM zr7HuYzs^c$?lkN&yO2);zjOkqPm*py1dnXr)8Yo2wH6c&CDKd zq>HITj~?lqR{i)*H=jz`pa44za%`+cn`rk&;`)@5FuG=6@iS7)5%|U%9~Dj>ns~D$ ze5s-WJ*ac+!ofE$!;G_`!f%n1d0X_#a-CHlsdKe#WfKxym?&_4g_x)-Ui7tMh}{qu zgxw;Ru28&@(AqD8I>5hg@Pd=fDEW*XzKU`Atw}k;9$kOsH97d`@nUV;-ZeCxln|2L zqslDqD)v{$Vy(@oGodLQHZKff?eEIl*r-RR?lmDpFsYMR=e+>B2clq&szl>8hT&0c zl~HO425AUyn0);D66jG>I6k9JzbitJ@@!KPez8}u`45x^XK!rLZhWc&KZ70DWxtA z+vdpMs*1W2jaWVKznd6tHBmZj44+(vS_!vqFt)H7>f*aTLP0*9WQclG?<-pn$LEev zXa?qlt?tQ)PK`8Tf9T4ZXnv6wGUc_^bUyes*)wE8CN1OY`VL`TVIU~EXdP#y3}U^> zWSDG=|E8LREHzAsj(0S=y^Bd7<3}%*rfuiSB9>P~5QHn{frPiSEw^@o{oudgY(ul zL>GH2toqGJnn+#=u8c3gI#^|H6?dN$37IVV@cZw3$IYv{XFCH*MPffH-zd0e*~uJ1 zo5D)d?MII3mM;phUlmA~ke}zUhb$tIi z|4+_hbgFMs2^(Vdo70S6zWx07waC=QP(!*HlcQ%WVvqR_OrE&jU!g{cL}d!~rw5=WENAPiyu*5otv1v2 zy`**~UP#HpV!!DmKbAgPvSUfnkhZzmGU4SNwh{gH5zbGVZ0ro*a1pX`lT)lyc*5P; zY^1~=CXA-}8}8q^AkdSY8n9a)*^`@Eb_+KcotIb}9ynCnVJ`8D0Hf)ybSH~)399cX zshur0gs1fM_55cIn#_f^=V&|%M;$i0FI-oCQVqRi80MSuTq1n3V$=_@3cFyr!?>iU z)!;j>vh<|a_pVxsmMGfTYw@a zl$|}**v}`izqV-OIg*EI_h(r+==(Km(g<*3wuXDE4i}Y1#V+Ql8Ovazl@xuqLZQGe z2I=gx-VDUrrw3T@-hL_WML~h4UpTmfotx~57WeMU+@-i|NxNr!>@L9~s<%SQkquSJ z7g`Ki(8HMb59cq&QeI}XZCR647&u%jU9|cWf&tN85)Y|Fie<5~pPueq zocDIBy=8;3p3Nn8+zJ^^!dqdF5%nw|eQAVmpgE{e4qu&2pIWc>S;r~CL6mP26-^A2 zR*luLjTzIWn7_;u!;4l+n~u!v;S{dHw%~Fr&lE-zc+ka;#tx-R%uOU;`1%v^R+{3O z&a}d|h3SxC7Ylef2IQ<*M?PMPjdF028OY?@V|NxoBaOPm(PFzTJCacdk_DYs@^K7S zQ-Odyc0m3u@zxVZW2Smg4th>cpS}W|CBr>UenltNCQq)3tWzeW{au&k7ytI{3#!3U zjJ9{|+w3obbb`s>(QIG10|GO#%w2Q^G29SqWuF0E*EM}3(?p`0Rei}Nm@HP&?b;NG zk~?HhxsHTRk@R;ZBxvztI+ib{pqPfgw&%(Ee>?M|t| z9>MBI5esP-3?`F}$5y75>oR+zvSNqDV-;U-RjEIOW8ze#t`!x`2DmeX z8Jlp2crgysqZ9TfvW{&%ZL0+d_d9-6VX5q1hO~}p3^`L*{n;|t^)d^YM2jJkM; zQ&EX8yAn9vNT$CI=_;Qb01$eJ%A7=d^4c)-lBU6LFBwWv7gr_VGR$8%{O^N{#tbj z&?}OMCy@Bknf6K&RB&a!fo*Q2``sU9=+R%PE#8J=KJcSK187b54UZ9~s&JES3Zn>0y1BWbwNjEZo<7cE|RN{wlW+LF^0#a_w9 z5FRwF*kWX5iW~i-x*pXKJC;Py%(70|xscEF0FL8E=lE2kNqtqGr7GSQxqDzwIVr1k zg9fKgHSLu(6vj3JXxq~ zCZTR~ICBLF^{fNQMR8;Jvf`b1klD*EY3@SJ*Q^4%jhZz{hLCp5PZdb5AeAslKb{t) zmH+M2S;f~hQ!X{BZBdh3+^mXE#i(fE1#fksUzcYC5 zGI0vCjP9dtzVj?{vyAU!y&^CFH+0Tec_Ph3(3?^S0QbS z@G&RBVyTXr;)!Lpm0&D9R|C#{e0yc>xnuGVypxX5C8&K*k>0MA${WG+BG>z& zp*>=%yA$t>ulEOH_i;$faJl)a?#TC*kzbE!wqru=@ajAQgE8J5y?)zWnU4^dqJ1ehuL4UXJr3?UT|NdoD&mh$-3#LWj!R}UWdFt58 zwJDnr6ekJTBdQC3(*w%dWAFUdIw8Y(uS8&KIk?w9Dv@4ox~OSzaJItmlmzR?1Q`Bk7g8>nc41PEXJo%P z3-wxQ?$;ylhMl4UY{kA`#Dpab{}V7Ok=*Pa9T10B$>R3w)&}*Ltz#wKZV9cdg~TfQ ze|4Jft8=UfP@zj&N~gg3T_?^@746E}=kp+90PIjEI*N>lg6TW_qa4V>q7D8lGp z6Suniq*yhPjZKHf1N9xncGwsIZ{^-Rzr*B2*5{0TdIge6fQ2-6kmC1>|2YhiusDFgKbF z7>1!WLjf|i@m8@m2(iC+fK!5DQofUy4ku`3cis4HusU~MGA2uKV}_z}J4G?!33f~p z`6G|3pOo#AAz5munI>arWsy+!$#f_&zAyAcep&E`luMqy2WyE=$OFNhNNL{uz943x zOi4Qy1>*o;I+;;3n>>Grc2-8o7H~WHby9U4 zQZ&Ql#jibKDWUT;;F0&md{@!<%!Q7~8G%uSV3I<(6^s z$7pBCP}zJH4oTM+)15Mb-%ckpu&=yoH_?zWJD5)24@qw?CLh#>UOd=fFjQIj0l^<6 zIWLmna4=U+IYw292*y0;dqTHdg2IDt=H_Wscfxg7sI{$Kk}w`Bij}wDA`*mNmgsgve8myIO7%8xz6jsRs2{qr zac_LeA}TJ!(MtevQFEW(ObHmB?opJds^@CH=$&S9e_q4@TlnEG&8)No3KQzZ6Vii( znfg9wUJ+Uf7)C0@!|2{K+HRerJ$-g^9nqgE@D*w?1kmdPpwC&GuTu057gAxamk(64 z;%!afsm#Ny9uzRQNbgPA&n`Z!R0xTx_ugGwboq!B5;d{R!zlxfA+(F4 zHkunhH9H*e$DVxby8gA;U}Jk7F;Ev;mb^*PSBOMxX4MLn<2TOQclw@$?pnAr0X?0H zys`OC_k9X~#p96I*U0rt@|P!2!OCZdSAe@QVQ%bpSy^rELo86~U-vXduVWT)!h+8| zpfV+En`=4Rtkx_N*sv}3$ot03cz&Ra6GD3a?gxztKGj?-iO5ZdKumbf5vWWs3a8tjOn?=gaihgozXGbYlC- z3~4`_q1e7wtJ5mkp2>^mlq(`JYla27e;=0^zWqWyt2+6~5yj(fg5SRH!NpP|zu@>aFnv<_ZJT3FfV zlTVts2Zj5IC6^H}3^nc%fvt@141Z&uWea_SHcDP*ZV06pgF#z9k=y6jG;Z7*I{gMr zki>N`v2@k}_g)5!W0D*F$G8ibIrmuuKN^4?NbocCqyXZ5${0h+DNp zB46b&2*_PQMD%?{@f!V&zZA&O>X9APK~Zt?$nP;9T z@lDV7C74R*N3!e}CU?{Cr2hI0e&Oh$JBYMr6^IqCz-PxKL z+HrkyO|J!J^`j#H!m$6%SJ>ad7)v!RxGYCDCSH^o>v&;B+nQnJ-og_I1?~giO~j&m z7LMMrRM$Gs{z#MC!EEee&5ufDI4g^owZZD?8G$(t727@vdMXM+siTRkrnw*8ndOuZXRoum3|+$t!{=W*NR3W9+kg z3EKO;nl8`2SisID0r?|3$*D0I6_G%QqY<5cJiPfCc{7{bUd+$I88R>Kayzej_oZ9$ zj4@*J_sYu~(<&a1q0&)|1=Ng>&BR5q;e)0^XcNw6`uQ59{ox7&_RkRf_ErW0I*{9Y9rUotcmi#)K1S@Hi`{N-^Wk_6GVi1bld zFK8fJm-Nd*ULh8Ecfd&h+ffNB%#E%#W!zlmoURE$HUwklUZ~Y&2F5Not##f-t|`IY zYPhF3R_)5{c@6eSaPKtseB8_PX_0>+;gF=x?WH$5307S9EP*Kp8vW9qmVIBzFd)3J zAq+FeAT`DDxA5xCifr+70U?@|Muee{XHJR&K;XTV1WTt)fBGbi252d==tRk%+)`}UEHOzk_i zECHh3fpu!J$vqli{frB^cim&#{91F(x_Am;eCZr6Pxu&Up=RVjM$ImY5&Y>61Ou+gzRoz-RV@u#+L)GT<) zOT*5NM$))J^)%%%e00pvoEDv081e}@;`gI<7%zH|u#N!G`Yb6UwUmCs~ia|@xn%9Jun;x0eKl5!popq<9&Jp$LV5L1$M>A>8=DyaOQPa(D&1{=p%2?S1pTg}2!`6ZCbD zhxMQh+~`RIt?52fZ#z2Ks)(S>JRV0jz>z#`E)1JAWcLurG+>^2fT?h~XyB;>%+~@i zb}?X*qk(mTxZ6^5>zy*RiUQH5a%0MJK0Ju5xR@p#a7LtN-M7wb1#3!xFW$7KmH$$L z(vtzDc%~MX9Dbm&SfNXRFIYl+6z(bM;p2hEWH~l%Yy|WC?gGo>@%-tifeLhnn9RH> zuq1q{z)JM@jj8wDMg|mIK%*RQ4$2rboN;R>cY4)>gX*sj0DI8Xi}x6-)c!wdBZWrT>H7kEAKC<^(E zGMiiyapVTO{hs)G6JOOt@`q~OSxt*T?%V0>$(c^1+v=u8W&SvG&=3iv#h#-?!tZeCjIdiEgW;>y z`>clFF@Iv^v&l=Fbybej+hIE=JZQA!+2j`-)Rl9d9Eoey8&O4h47=;KId{?Hy7<$3s9tIYXS*IKaN`1+o7wb)BdK>=TkiPv1Lzy-xe&}; z>CLHC_2AD!)eJzyIBjbWl7|g5m4-$FaXiMf`NL9T?eLm#^(7$l;3kYWksgi_yrVD8jwqVcJo<#7qS>HBpXg_`-dU9-l$3sYcfR(Ht!B*LB z&Yq=J{mBkHuq`?TTMPLtq{<0Y44gdQn)RE+4zVPOK9?-dg61-y-h$?+`R7&(G zypMU-TeT8;BPAe0=5N3vXi`(H#L%Tf(FYE&uD8PPbf7m&>DYis)Hm$?9g&)7Fo{eB z-aXCUdf$TepoE6=$v8j=uwFqJ?j0hkFjW^~Nr7YdvT=HKjo}kI+826FxRW-VFWk_2 z2zXRiFR}S+?*+dKRr&<<77l4Kh3{fYb6Qh`#cQ>d~4m+yTxrMp8DbRj=EfGf8;?@=JLliHacSl|*<3e?uVfLYD@ zKs#)${$-ZT8R3O9c0eTTZBON-whc4(3iNx%A&3$9{G+j? zWvzhv85juS3N)`K#nwf@xCqGOI7kySkk5~j&Jqg#4VXb&!uJpgQV0dQ?P>X9MuDLL z3MMFdgNPCO|NF2Ic*iB<_hc~U2C3aoO{{+uk&}dXI<`F0P^2|l$f*)Th zG{+@+#ibZXz32H}k_S9|0Eb?ee{CWU7&+{K(|R@O-5Wc5_No2;Zn-BN@=T0ZV1Ab@ zW6FUe`J7Z=Cbq&E&~P07?<$gyq>DUrGhXtYXb)9NW?;^g>j)>)+d-K@ zV{yYV*M*wtG9P5(kRJzc>i?YzphD5rP$LKqfzb!;%jRA=6})fNG4s+Rwq?0#@|4HP z9$ zz4%a9f~;(Rt`vAF8GYNG*2J)P^9H8VozS8zv3Eo^tc}NvG6@v+EZldyPj-QI^J`$JW#qZ`&rFPz2{a9BcQmG zzmFoIDmjh5jn|%7-)J>n<+|VVI8j)V;{j0pM%fExD{Xp@Sp68a;7NwkzP@fk3J)$O-Ja7zJ^W*#k>Y<+akc1WNyfAhyxd#Tsp#%!OOeS-A6 z#4$7pkCCO5u(-%Ko&<*r=?h)A)a4e%y<$&8lq;#3jl!oNX)uq0+ukI|-tX7xyq>&M z;}Zs&_5LVURX|TUAeknuNFKQt!Ib+;MjU0mr(LX>t?pEA8~3T7@q@^qAf6WS4Kh|f z{_(P~dq7rZteONLBLaf++CDt5m(4qfqyACPs)mqV<++C-xzWtilI1Umf-Y0GX@n=p z_cL>dKP}EK$|#emuh0eSz_C={QXPe zFUW@p`drUns9?QrKe@)KCu-DtzxyF-ES)`Gc+iVLnXp#_Jdc;mxL2gim)rO+cHt)p z4sR2Vo{~L8tIi7sNNH@PQI_2OW`8^4FfnU^e)^N&M;hztwvX~rCoUTfLeQm-5V1@( ziLfmX8GV^(8ezHUV%i}AmF{1F=T84d{A$(9UULzXw;CR{dZ&}(G$N67^74c=@z>_O zdXP2To_S+W(Pqw;!&3Z4!Gib@M!@v%_ERpaK$?GP0mvTit+U{gmPXajO6j*X+_4wp zY~yT9Q}qc;32{R1ONz^b9REF${eMdhM%!(CZFp6PO{*3EjqcuRqm87Nc`PoxE`wT1 zP?y6GCbBp?b3G*kVMv@B>c8j?*Oi-0*yZ7boV0R(>B92@k<1?}dTops(RNRzgaTK~ zK%je>pSulm`H@ec4S0QmsAp|U^-{>@ zPskb9#HN^MZYoPeMwl34;QlliWehRIJ4MXB=CWPi`0`|rndDh`l9mzcy2_loL%F@E z9<-h5Ov#oC+UHI1s5d$Gigy8Cg8HlwMU|NH7iK{}Sf+!rg0_%EDZn2X$&8xMm{0cM zQlE!w{lF+H?#+(beL{iAF$=H}2~eSK$2`3}tTCb`#Bu`n{Qh_HECf8&3Ij>xBxp@e z`Ws2FhC;~@(zTwGYeyWfjqhrVdT=L2?E8&j%l@SX7$d3y+KFm_dO!^z`yXn6Zc+4^ zxZ2x)%394?|jv)|8UP@F%;kfAs=goNL#O-ox>JsBkhTG)=Wc#5Q z+!^-pfYPg1bUsh(KFCLs48bNXp>6BTCVi!4aPz>!@sgnf(wVh^#qhulI`$WH+$8Y| zGF2vN0ReG#D#FD_GA;yAWU!hXZJ6?k1W}j#*w=T~f@j)*)107ivx=hJ*+bZAU8X7W z!8T!^Yek`Z7&(!-)lSI zV!f{+MGvgg|Mu%y#F_G?$)f9}r6iy^TJiQ!Pt(GpKd@g8xoH)^GbTte{XXTaFi`>U zt`@O6Y41q}%JE(1cFaMJ=iaC=o&W{M7ua5MY&C|Bds#h2{pxD3*e96;JY!!VbFRVR)DYx+=PKovFhf><5SNQ+ z&6MBt0Fs=QbNrwDpp0RkU-_dw-|4NDV$ckzOx8#H)$26 zo&vLf_Q$oN6 z{gL&dp3Im;zON0U-+U-}InkszIw)_UsI6~N%>@TT%_y^gQO z6T74}MnKnbNYsqxF*WSS+0s3TpYTMk&ihGz!A4-BQMzy^HYi13ekExgn9 zC&y%De|-v1EgYEXN}qBP?rRY5^2sZ%#=!lpYof&7Z^Q6KnR@>dNXuDJ1;$ZMw(0QC zh(D-yy|xn9W#MPduYlmhFQnz00fGWko^(Fi-mItaXp8%lll7nL%0nmWFjq7u<=LYU z-ugTK0JQ)6zd(CFWZ=ibtAT@3xHV&V1r}XmI)sz_v_(3PP5kkKW_?;b{g0;&V)jBX znJF)N*kF?J0ph3_neky#ttX|%cVx48j5b+(^xUlM0i6} zu)GD)7qvw1$!n}kTBD01sdvpX{Sq(D$eLb9OOzoq^`tK)+Akya#jC8wdRlgL*1pN3 zk?d1`8}cH-%dxJcFEg6&8XNMw=R|Y?m^jmtu`|HD(08Fg{tA4xot>K!VE9GIP0_q& z4DjWVMjVaDTm6l&#G2@$&JuI5fk?>`>v}f& z?rueAkwUPY6W>?nMB$?Cp_#PCHeL8+)ZkN15pgo#uVQ>T6%nJjzol5s<0!iegTSth zN0~RwO8ZUZX}Di-V17!C^Aj=g2rp~6d@M5icTKW#^S38WS6u{z$&fB$e`rV2!TWY3 z6%H%6%QEaDbGgr$?~f5~AAtOZA2v3Zt9Z~{Nl8YvWk&LshU?iT^5Tcz(gWP^TPNgbbh8IW2kc6oSiO)@V@N9Gj-ZO})vUbSfTq#uIo z`jl|=>C48U;ix~M?4##fOd>4F?e}l~zh&WpEzuFs3XP-F$~syLxNEv0onB(7X&UMF z*e=uAN_fp9cqFQOZ?^>7n1>|zvFzih*L!^ejft$^HF@;;=tkSJ>2ka~>DM~ihu3|1 z<>z)<(;43^T5&V5rEsUc+u&U%f9}h6Co{?tA8Yb5D3nN@`XV*s6^86x&Y#fx-lh-T zU&fXw63Nu^gAKt<|5KZDtmQsSx7x6C^~I?l5l4An_-X6cgge;MSw6a`Mr79IHE}3o z>jrHFa|JIm@^hsC>Aw z4gYhlTId#JR1`m!BtRszLvv%L)C&-j^U8ev2?8kYbC|apTx|T}9T^d-X3)y!u70!3 zaXv3|S+Z+@)$wh+U8E9$6kML^%(iTuV%->=CD!tiWf>4Fcwfmv(KlSM3`lB|LX}eg z7cq^N6tYQ^6SL3Nnsq6_{lCP@S4jx_fmSrUWyQig*kB8#3^=c9W>|`Q->M#YTYEh z>z?hm9vk=L-=E}srW)W2q0{r>O@K-zR|Vc1 zo^#a(0K#m`wiy8oxJ$zP$8c|fA8!x0)toId3*`_CV+NPAO#J!X({0!dTX410Chf1o z(yjIfUDx&Y{kcc-CK&T2tXNFt&dEIan)rJfdUXdh4*rE3J(`6=U4{aqsDoCkt<_GUJ$cxcp-BWd$!y)tm z2~1K79V60Jc>( zdA>qL>3fMh90;h$u;NeWN&r#XUX;ywQg>)E=LKR#QE)G+-B@h#^;`(AN(ZTs9BImDvH*7>K#FjUq#2tQ*prEodN z&F0N8iSzJ3yBt9A;5=!%2_|vAJ)D69C@lUd)dbr*ALA+6A@%)#Sa(>-Gg6`z^=ttY z`sbyJW`vVN$j5c`kfmGf*r|SVAm|QmS2I&uUkmY?|5zD;jLroRZz&20B!9*g>LnnV z(HK@dUOAvtR~20HjD%r@Rpm(XGes_b{1N^36sRGT4-hFBY*-}}%Akn?q4l>1-T*yU zCcXNlAo|CxthM$ZU5^MC=1woZnm|x6tWZS^iih)ov7u1aEVAlnJlOE(0Iw%*2VM<0 z7vS&}W^l)?9}*DJIu^ZgMLYMOqgT4QdMa$_W~^eZV8*_Ln*s0u%FZ3~=lMCJAvK__0@lgA@SUc|r2F2*JEQhnpojmHb^}TuATfE6 zE&8jaZvnu#J0-NU2Fwa^<_-cbA!PDT0QQ=btd{k~@ocdAZU`kcjadi>5Qa5d9Ym_n zGqSuPuLX}X3b`>vE`*C_tiu5BxL;EEy5hszsN~*Asb#q?3%%D9laDgVtldp^$km5e4;y;3+|_k(bNM{~4;b6(5%fW7nAwAn&w zzh462UHoD4Yy4zQ7d@cI5eeJC$twfkvG4gu#Z5U_#8)q|-neBj5pZA8QO)}3qgb~M zN$((IxFNW~tuXS{K5gL!i{+{=R^-@S@l zJ;T3E?gp~n@{*YnIl!&u8h>Dnhu{BHgJ-uxu%WB~wf^I@w6r^OYNckV@ig6o7xDSV zo9_Ui)2>nUbwM1_2v$5%$6WPgM-1eI3Xdizju#?lr_6B3EtqiqJN4quS}Gc|X1-sV z+|5kd$=HxZcB4oAk}^a(=}@gkni1AmP#SOz4L+n6d;Y&L`2DIpjXR;U!qrjrX1GBJ z5zW7XBKPW*MPUmU!y}4k?*7qss6Mdue}D6lSE)8p`#8HzSTm6*K`iDvGRF+w8Yo5#^ugR^_??4q(A>}Ji23+ama`UN;46w5{{ zq8kY@tR9Ko_EsC>|2YO6mBzUNjsbYNFI*JRCJeXlh|vD9oBZkU2TvpFSZ(i|&1*sg z$Gb-jngf00Ys|oqvi!BGHRgH#1CN#o`AM!&_gTAL>kJ}H@yI1AZcO3+Vl@%GB8#c) zcmC4ko;G@60lJd1e~&(ZVp-DG#50XBRcG$~58EHHqMl$j%yrUpxh-0Vg-TBzibzUr z0iucg4}X`RO3Bgj$M#AQD!(F9aLrqWpErboxu8rQ==1M5DPFwyFZ^EtWQSnZrlz%O z!vSfI^W9BNx-XuQqAb-1bIXc?z6oZb0r+i2xN(XWuu1Gx+yi@19J8k72W{n=?dSFf zFf5ZIT@EsB!y!)>4B*;h-w%@~R;JyaKy?6#5S=x?42{$(rv9bFZLv97=jRJ9R2Scn z{ttAzdB=^(yje$6d)>*uV+ppYm4z2fIB3R}4iB6(EWP6ha`OPBP1;LIOTd28Q<7)w z;3it?-ERgNxAzXR@U;ZdBBj>~kx3NOl==opp*Cib9(5wx z-;NCMsujWnIPDJ3p>mB0_*KYiY}`okZ)&QcU+-l+ogxKy2IB>l{r zlnu3c|8h5LPz=U6br=(=N!%FXr+>MJqDWJ&_f!0bYJd|^4M^})z$m=+f3?oviH zgZcyL#bXB5q%(C~!eSUAuw_s5a?~f)4>(9~jJQ&;rnZ}t)Jvkbj%0{Ph4|5p;s-(RFAJ|)PN!L8h9 z)}(Wvq2NYG)f+(zJ=)zCO0T4 z4n;aoPF;jL*z?Th_y@sz6yU5Yql=f9kbp82QlEqTq6jbj7l3D`;ZrDY=+PwIPof?^ zC-XdLM|wDXwzwBb0^Co=({hHdDo0{+)Rf`AT%{7-r>{`ffa8xtt)~Y8kbj*UeL`=1 zID>lYJd13yA*~7gD*T(p@i81{DvtK&sOsO=Qju{>SYuT%t7BPq|4rju=XgwEDu!m~ zsJ(O;D73|2d<77>-K(a^+-CA_5buMSil8U1HojjtR%?jTAq$+f;7lz0v~gzW0dA`? zgjdOjXSRg_Hn8!C0ceIF44o*1drg zkCPLDb>hXy851km?WxT-Fxi;19QggQ4!b5UMfhVXoc$GDPYZi1K+j_9$xAjEGAncw zfZq(v%yzL>|stz z`R$hM{Rq?`(G)-eVL+MEWBkna9`H9CrhRW@-g=V1LX{GG03;A*wTNmVIUr+UUhCEG zuvw%CFr3TpWsEAr;xb&J+Ca~$9ch}!2_c_Yjla_afNs_&-ltJ+^9sfM+HtGGxR!D@1| zCU&AYfZ1iomD$wO9k3y1PD@OCl_>({ObbAN-MVib^u~{8k+sBPj4m&Bv}E#P01YsZ zr&{-|yGa$=y1+&>ur=*x0EI@qt>HbQfgwKeS%{1Nru(YIjR$Jfb02g$hGu>6I9{f)s0Ol^z*<5H_Em^G42oHI_jpT6lIzW1-gE-Bvh`7aC-Hp zrW7;*{IU98vm~k*bNF}FXX8+bNa7Yk3n2n{FPilYHBFXFl@h?lVek<^@4q0!MN(|A zX2bhS2-)@RXjC&9JfB(%8gv($g#1x57M+-Z!#dm^Dh{0n#IsJdZ`*0sPeH?lp<~f5 z@%D^=2Zo|!UgEG8kLHC&NmvxP@*8JL8*e`Yn6mKHLASjr#W4Vn|0nXkx7`4>1{&l2 z2Y6eFI;#8XUEz6Y^K}cRVXIBz(2^w16Ux^p?)lQlctY4`*4r#q=;!!k&mp(`iV`khMS2A<`nTv?;VQ~!#3 zH1c8%QAKcXYV7)bWT+3|a>p2Ny0Q02?%TjZGnvo8l456tEGhJWwL8YT`BAjrm>Skh zRPH#Y2YJ2_{WDlm5DPISs(12y@^6GS9K1%)016q?%2qjPF4_qIfQtdqX!W3u@d>W6 zdGszalpI*5N1U4v>zZ64IaEN!5g&nPl&?_SMnJWOw&UkYb+v(=)aTKtQ77)v`Yf5+ zNC^paiS9VQq80V(M;s&vSjFA;b@OS@J^sAc4~dlrjCI(K5sv)&$rWM=EaPtVx_Pk0 zc(Jl!EG3ZFy&W?j^?g>zfWnA4T-ME@wr!We;=9L;B*3QTX7ui$@J})w43H8q0&yPU zlgS@TFE=aH$qrl{`DF6MeNP004JSa+l<2DJ$Ncs8Eh&hBX(cxGK`15tXx@PPCxFs^zo4n2`UPrP2NCg1l0KU6t z3+R9nfc}y(2Xrkxbh*63K2(6^Ej|RHln84`X(-_8ImtH%;;AWI#W;hlTfl5hZBGfY zze0ihfX{z@TyD~}KtUG3XRJ?9TwlOzdZXqcz+}7m`nimwuY!-zpr=&Ax3%h6ew01q zGo|PUSU+WbBxJ7wkbldYWL&CF;@Ij;RJ)EN*D}(9(g@b;n z`k&`quXUWzTDW!XA4(N0kqJQZwh9=MCUHW1^crjltTOyD*D(&tO6T^)YFzy=E$yH{ zYyAI&_W#iX6#p>nW`;I5q)ZqddWkf!q}tdYd*=!Q*4N1t2N=az6@}t{o?t zPl1}e3>2ac5b?E`jVg&)7*D#_t_TWeyM5P zEGxs=OXZX^q$RQb-Sw{4XV~!`1a9-jQ)j(*Nk&jWjSyiDeOTJ54G*>Yq?^+rfP5C0 z0VoX|-wAy*O_?V<4c8169Ku`EF$O-~ez8t)U=khjevY<{t0=0&T#qy;W^?@@Japst z$Hq~mKa|vCfH>fCAiJoMr2rGJ;nJa^!nOEkUCVP9NyDf0u%>_v!ZsygC+PmI^=;~ytsj4%(d#UCcalEiv7>NHp}AMm%7 zM8BvsV9^Q1R%S?B(RIVLc#(LqSqjUWD^s6D5;;Ql2|r#Zo5mGqL>s($Y@lIS_FZzo z{`PYF!n^WGf)d}$jaNzs!iT0#OLu;cJ*|0hOd;3WRX)kaTf-Bbzttk9Gz g1^C}dt9%53NGDO(ANEMY9eNcJt+cT<*REg?I}I)kw@7!#thW-Qr5 z_I;UznD@TxdHOw1&*yzVzdv6e#hm*(*E#3f&-J}UXltp`({Rv`kdV-;tKHNkA)!E% zkdU7}P7aJv$U9snAt9C5zH?87NF)LySy@@&4;%?eFcC*WBw1U-DU!T@PoiWuMZ}#Y z30A}f6EDMqBS}aUgGq?N#2{jSuAm?=Rq;zNFpq$n`19O`{>B#~ac%rDvHET4+8S|)1Xzva0q}p&xyW;(k%m7% zN3yd&1hzs#(iPslOag9^NYed@csiaKq`eUwOjIG+4}P?m=({l&)mYJzjfBf*;QskJDDvcMuCcO%i}(nDvl|!x4e= z5OG~K85_intm>|}#J62Iv_DZXi+C1`#q#j*n46ntW@a`uH7O`4OioUcWVjhrF3x3= zw6wIaB?$tOB%yw&X5>afa-tRdN17mXf|G>gEQ$I}rF-6KtK-y(ZwffVrr=y_`Gar0 zw|0&oy^p6)$J6|{(%91ov!hctez3uOZhDps;Pij_`npg8t6MHFHoVQr3OOiledT&% zc0ONi${`q0p6TWC}1PMVyUpTg1b z?$dtv)2yA}Gp?Xz6M|yQfArQPBpU^|B`~H?U-Xd-NuUm$@Hi!_Ws5Bvo?92V$hO^^`H z%#0Rmr-B5Y2@pL;o#muRQe1TS@B$&gHDoq&Tzj6PWxpqlew{ z)Tr+zpYtQxC7Bb+giv97_^7`UkQ`MHgHToYly(pD?f|dtm;>%t?`9A3;)nz z5L#FCuj?tOyWDgLY_IPeTGZNyMXBWn>Lt*Ia25GdV8=jc@KIGR=W@K5l8108h(&qa3GW?Go3GZdCOBq z;p0-^s6YT0w8aXttl`!diL400`=t#!3ku6aiD&+6gN~g^VXro2KQFR(jGR-vibR&@ zwN?AFnA|cNdiUnh2m`{Z(qoav6uE*n77bp?3uDoYb|MIuyty132{o1z{hs(pUNlys zks_!t+s{GfRyz zmdaVt+1a_pDwe6hB%g@tGf*>nFPn%_1deQkjl;Oo(Q&(?wy=Y6mFHBO-!vms(bC!U zw(I!kF+$40t4;_zG|FPwJBQ^2HKMxBq}GWe)yin8o`s1u4EK@Q`}#r&$!Ta+gNm-m zvGJrH1IDB}?nGQS@^wV(o9Oj)_ijW`v9w#)*emIe`P!I=o~D?GZ@Dr#jHB`k%QSlipRd1v=`2~8R4Ux| zp@ghsjJUTsx|P;dbp35Wo(b*z(fv&9+(fR7KTR&JO?xga>)2*tcJ9$VXGgbfq_S8{ z95;ml@rEWFLw;1O(HGIZl1XQuFK!o3LMEPz4SS8K%K9-u-_*-aKG= z9dQPi>jFf}pyZzlsP&$znT?~V_k{+y*VV?~U=X_5;&r#8gJ%bQ#Pw-<-8a`RfMqB zS_NL{%00-(qbyq-?gQOa_mgCB0B9?=(MPc3!u}CKL+X;g>Ts@Y_wFTo-iTfKa~IIT zRBOldzkNe6XOwm|d@R^qgI6J4wrF@(0+Z}##>qPbi4Em zFL?$tZZmP_h3t>dS{)Z|D3w)8h@cw84p>Eb@^**tU_CoaL2k-VvS1Cy(7orl{>vfB$Ic&*k;%!?6;k{tcJP46;s9g@^VD=G-W1CM0? z(hMJ^WEr$>$f62-mV2~&*6OH_XqFooKr{&;HOtjZvcC~X=g_kxUYGPB-z0jR0ae&u z?d6l))HF1!f4I+Ix?z7xDd7XvV8(zI$2auRvCyChy#3H_Qi31>xBwgCrSrL#JW1 z#dE`xJI_Q1+Jr>AhRZOW5=6ludMuHn!j=YAInl|tTi_`swykeNO@uj-&^O2zlzTOO z_X#IZ?3t%59<8^rX!mfs7fNBzq6ZlX6HMdCL5NSN!Gvf*IiRY6f&qr&!HxAfe(f)Z zn0uU)C!v`kxMmaI-K>wtg^dV+>Xa#6CUZy5q95oZ*Mq#O5ioh-0&y}8gW~-ouU*oO z_xT=$QUGZJsAZi@u6yhVKBQIv(Qo58bZQ}prHq3mkiGr~?||NE7Eb)gDUDfzrWx-+Hn4Zp?Y77gRs?gVP8(9+ zmU(D*Ab&ewzCl$jd*l$G~Z4jx< z<1zE3!)8zDQ!d65|8Pzq?)s<#+OX$-hiISOanxe+loWf_3F;5Xbye-_)y|5yOl-Dw zqV=WTWHd|ApMuz3o!GO%c-#%-F9^&T$vP;$dnI#4Oex7eQ=gos@P^gShVAu(PYrt` zEu}N}u$FhR9z8$qV3c~dwE8I#9@`b2R6p>H_rk|)6$786uezVJmX$yU+b*s+;C-45Y2)=t26Bge{Pr=nN_rKi<~2$UW9=<2^@U@AK$CQ zAD>`{6+Lsl3pc$m@Tj-(e5rk*l*P`}wzt6JqI7+74vgMjUyZvh+g*0!t)i47gi5@( zn0w)f3FK@he@qH*WnM%MzE$nC95d7%J#j{f&)QARwk`6Zu9!oEgiXe=i2F8V%g#5l zLL1m%g07}NgqLpy#2c=MXZbRdX$)@eV>H0VcwiZXA&KaA*4_HceFh3Gt9G6b0b4&=cKD>7FF9_dWV8PBXsLLbvm z1KaQkShsuGgnziGYY~cjq0fg7%-%b7uNL^7P>Bs{e4>&>6MGU`o-wdn61JaEoSfBs z0@%hy!r>Di;1Lz@hJk8*l;d0vuW`lP zla9p++Pw6D-4V<`JPE~LH5l#{fHdvu1O)k+G2Xj$3!b16ATV>_U7o~?&a=sjUSTDV z6lE^rMGG#v?~8Mvis=dIuPcwd9jHC&$GI~v;s2PXiV>RN9gh_zLk6`K_PW%*yE1jS zfAqTHwMMAHag;3Pv}OnHSr1(R5`7WvS+nL9L;9E|nh~n+Zhwtgn$p16Itae%@^zh! z%DwPx2(IYuo#!vf9lWh~d8hK{n3X7vfa63)(E4x>ZgfT~?K;Am^C?+if(<|@=@#|V z9$#YT3ZFj2+tE{T97SGzoYkzs2%W8T)tStGq~+mi>1#dN`ibGa5aymYErr1q2=~{} zNFKOGp>(AhoQhy?KJK8bN3V58PAnGeWpYYvzB=x6067*$Me{ z)uqXhMk(}emAxXbM?n-fH%x3hDbx1MD+ z*L*&f=yr>Nslt$T?G!ZO=`rBQ{;5uIp9I$1Mk3M5aQ$-BdW%wdP?zvyn#slexZXvJ z)jMR!P?Z1(c-ZfcAZ_Rsv^Rfvh)5?P;F>F(U=?UL?=0qCrG% zlp(VB96Fec!hmnwd$LWFp(8fOg#G8HmKI)eNev<-u~JRz-arV%0S4cE3F16{T)3vz zPUGA-X?eWag?Ru z^|kTc7gwnB zMDt4uDoOyUv_`EekHO|gPBL2W;y!-1s3G;}bRQF}+a?*OP@tNc8L7s*@Q@LA^@K(r zLoETWSen)C8hLQ$vwTVaUE6~TA=*~u!)2gm68dm`yL0>jtjL)QlzK-=e1PC`5roX*KrWZ)g^`C0Q5Ns&GF|PTh>9+1gW@yZHYMgn`x@)Hx`(Tf3W2F0PRCX~$<(7?7mj?i~WZYBq{Z35wlQ*eDZ zmL&GY&?Q-!jP?>5Ow<9dd8V*F8`u%?(4ayr(Gz^dp)DUGgwtTn@?7 z9%R3}zV)4PYSO%d;WoZ}*mbd7!Moi>B5M{_827z?H&glzQNL)ssXGTjfJis!m5Qcv zC5axK_qyg0Rl=cdpST8xQ|=N-ZC-B8I#|GBcFp+C-IH+LdCz8$k2x~w+`VYfiF#s4 zYAW+6aijIs!FBl)E%QO&+F+*^ko)J9Mb)o3iBK4)*JK`h# zz}U5RiAGqVi&dntbuE1_baY*<5ecOnG}G0dE!`FZ0|@Xubtq^(MudWxAe)Z;8tU0M z6enxwsq+C`!*S)qrAebvJJGIWOezw`!Q$c(Y1zyLdhzb*6Nx5Vr58{Y{u?&SZK0k9 z2+5sdG6ZG6j=!f^h*-u|%%r>a9XRQ#|E4>Iy7k95(>Ij2Xlf&IMHW}wriQcpJZpga z5BM1;TCd%K!@NoD+rNS500Ofp8IsJ&^z_g%x1wdp)({hu>Ib1O@?^*~&e$zw(}U|y zUrR5FcJZQv4}J%10tySz0MYs1p*kK62&%uS{tD}{Kp{rPeeUmo{@0Z2U<&Y#`W^fM z0RWkRPyjNf{*&<&kg?65jK?MbueDPIY9$l-he-ODV88%^08kxaE6j!z)Vw4zq_m^J z5WuOu1WVq;W(w3c7&&Hr9><+44W4(F$kNIeYjGNslXS^C18ArAQ*ax$KTgje)b56R zJN7g<CmXS-^Ba-$FKd~gUzySE7}8-Yv*B8Z zcT3L39qO3H1}q4bJPYCG53?9d`1CRVRf?4Mk|$91<3#ClJATH)L_mv|6yf@pOI?X~4kd({Ioo-4&)D*W(EpD0r>q&4%S zWsBWwfChSo3N@IFP6+3qmiu36!icy6`LZa_w*se-U_eBlK=sFp2l@-hZ&;6AHumnp z;-Mgew&*J;z?ykIOZJ^v5c*7@?J`2k^gKQwtn?R{I$>$$<#OM00lDEk;2r@t>BVwm z^!*>aynh@SlMSBhB@(xkSMpLv${oXil33`rYZe5FrO^CSF~5 z`OeQwRfktStF8b?V7gZwh%xy(un~f--AU_EefBwp|B3W!IIg_RjRWa4@?IA_wahZE zyrbG`#&gmwE1b|O!QMJz@x>+3TJr7%Pz1k*c`m9)cHM3>58_TkTc+HCE?sxJ;}S`@ZAN<0HOo*n z1DNNqpi3A>+FAPY{ItNLSNu;-LKSaN7|?G&h~o+m0b~Zq>FxdLX1tPD?$P)wEiyO* zhgRL}gE&{?8)p!^fn`@3wHRo@3l~K581>;5`25&RL=G^I1EKGjK{vhKmZRYY#7~Le z79p@j1Cak+&i;yMDgoq_wuYAgi$(^;e+USknt5@(J2A=<&mE=2Ahqv;KP zpfjmR$`L2L#Cb`P{KfxwlLQwk`(R_}!?3QN+9Ral^W@2qN~{S>cZ>#Wcg*@Fe|z6O zrOBno93hDZn;)XeOY{P&jI3_t?+Y&P+uRx3Xd|#lw*R96o?<3Zh50;}_DRHRCMWkP zB(UsxQE%#IGM{`X*m~{@rCs~pKi#KMH=x4H&$_m}164JM=1f0_p2+&yLto@tPc@UM z!CcB`TU&@9&&{#UH`OY6__O)6DezJ>4Je#UecRi$2F8{|-O(9K`@ZFc;+Fck-Gz&p zeSxk~T)(Nj+v~4kXY`Lh+A^wPtMMUf1(;+XTz zs*WWRT&hHWb3-Y4Myk>98M|q2+qa_I_Vyl-?ruw^{NZfpE3W+u(yk&q0VPMd16gvx z_IY`%?GbLs`pY4NIL?D23s<`sAT^MRWM-0T50CBiMHG)M?Z=Qgod7YN;B(cBafx=2 zh9={-nH-uX(@N)i8w1lyk}{Jh-I0=}z8^WwDvM}^8VldoU}u(k*+3uZ8RV{SJR;g zG~%{Su4NXSPUOh(CdbY1Z*+2Z>;{Cw`v9rDI(I?r;xH8;1H@ zjS-)P9DOg1E|BoOiI1gc2uwMY!e{v*$wvqnP1}`t25OVcR6Bf6&1PSDHDWep<@-Zd z_0;klYx2UD7>Lk>@Q{%6o22kC*Bc8rtZ;`&4$kfKX|A?NY$)5y>v=i@c-X&@4jl6&3LjId5eK zBvb0Hip+d^e3waeG@a35U?o+Q`|Ex)=Lt*=XWWh*9rBZlTiIi}3zZEL?KSli&616Y z=U-JP%}FfhOTexbOy$Zr4Vl~d+I3exru#3F_x~6PjvZKvStuK`8a5+Tdb#5BZ6253 zif&8A<{u2^?~>poIY(alF+)cP4Ual-G>3WtiZYOu5WTk0sq2HnyRLgAB=+?uP*ZkI zbf}%qv|HBRv5T3Ijwo>omf!*gW+%B~|CO&h=2cy#tyEWB*BY+1Y7uQF`E>CXrk7{@ zfEMSy!1@gAav*%KaO3Fmr`htpCxlqijrjZUMbX+Ghy;x>31Hl+9EvulXptD~y#jyzIRS_~R@Q)hH?8=0Vs z1nko~6o@nDZT*EaUv`uwEz z8EwAZZ~w0$!g%C@GW{E#;oDg=-RTxXE1Dm7_%c+l+(kv zxD|R?{kX6Gf^c>FkeZ~jcZnroQb?3K&Os!K_I(#83CB5f%rgfOff+nJu|s1=i?eh> z?hA4@FFd_(%W;I^iP4QDd*d+3SKNLFkxE<7+4pmP>QK{a|2S@`Vkx$PY-EAWZ`jV~ z2`Q^f*Q52gjdgJBAxgQi$9}rmJ*azGX;;4FbTCKBXtonG)GpD`n9a}C{C2$81L;~> ziN}cst5PkEx?kr{-tkhqJ7CpcJLrW;SDIiuEX=!(%7aZ;A3Av+QicZgl-$)Ujc~WT z&FXT!{5ZIQwdTt;g(jr8gyc@z&7@yD2=Kt}*gs|7Q3@(G!Dwh#6m>dzLnk zD-%?h!pT2a<9{Sy^w9H=In?ELW~v#jC48?PfbqpG{KGdlFZaQ=M4HC8uwNsIoq#0=Y@6-Qt>giL4`y}$6tbv_L6vp&vd ziN+s+49k;@(MRPDrq@XNH&SpM@q!9vbg0j}w^^w?y{jH6Wd9FEvwOM^bCofQlKJkZNH-%XntMN}BGt=DVuR7F|Hw`0L zpG!<44eUxKpaF&f2`Z$A9K5BrxPHLAh*0joBf=F4#AF#k7RD>~~1JZwV; z_w1(NF!g_;w0GQXeb~ECk(qyqH#ybOL837C_s^W4pPNDxFFdwCW}n3uk(GkruvI@- zV4Zc$_{H%e_5#HJ=8-nhtdR-_fKE4oI|V&O;&C$C0tD_G?HJrCPzxua=Yadu zF*1#t=3T((iY&U48xl6#H|fjU6AKcUQX1s%)XqkCc*(2q0`o06p-RzSGU+b&o;@l{ zQ5VkYmqPnPL|$sa2W|l5j%3KYNCEWA&1bzu=k>2fP@XoXGSGHDA-r0=d^$p(9Iyr8 zZs_gSL=c&jo4ekx2+TokyTG)SLJJ@=_-MX5MUQAf@tOb z#VfD-SX+24ehzqW=iX@6ddF@QqqM6kdp$#?>M*Ra@d^hLnEq^x0TsEOY7owx(^gRc zLQh@}=YiHMhxD|Z2%#h@sP;L~Zlk9#NX9B~=o)@3;^d5PP`^*gcN`@?5p`7|?a>e? ze)OkUkZMD!_}=&>KYd^bXeg+ZK%JZa7p;7ozw zyRQT20JyjDJq8Tzx7MCoNybBQM!(hfm)Z;l?%XH6$oRMZS}}k!zMLV`0NqUy9&r{p z?{Q(qRD1riU_Ia$U{~b789V;m(eD-hvI|#zaCx%dRKJYo=`zS-=1&%51=go%rKA3^ zqSj)NIOjjaD3JK+Z(?Udz%tIbjlZZ&oQy101oAYJxFqV0mTIZ z(DA=jKm`gL=K#vXZ!+an^l4;D(diRBGMnPhRwG0FrQl4Y{0v6=D4(ojP6o zO-}&bFJ*WFRG5EUfQ;4_Xk3*YM;%?>_`%B?@#_giqW;t;;FSWuK;e^69PqAI1TVTz zQ|^v=ysQ>C0qieqsw+{jpn=e6yuffMJHLPQ($%i_aHaLrQ()DzXf4i?_c~M4KL(g( z!bQ6x0Ca@vw;6hy0V3Ks^QVZoKoQ;jmpmVequXSjzr7yFdCLzB0vR&w(3u)an3L*+ zE+%X^Z0j)~y)Zq5Rg`$dV1n0tW2-(ZO1G$iM;#Df8jY~SOCJAS-t6^0kIwXx)q0RpbmQMoel?arK_OxJWSj`$fINffL^heYq`j=84q0#n~CUk(}ubbX4wlD z%7QO;bDy%u^LgyfbxG&CUlXkSg%0&U^fYeqimkq@d|aPDbrm#f?}Z2OMH;tw`QQOp zg#0gHi9_qyU=j~)jI5O-4|L*m(oJxKbbsMW`Y|*8$6%&?6QHB00_#}od(Dlvj?&U} zw_pJITXwei!dEPeV5X-)1GiI8H<0C-qxT9G;Az@31yLr0B%beUM_!0lGHz(ig`DDw zJ_-dY1C&%gzq%>0B4FIp+YTYOG8n;#7w0{|)2f53XS7CG8u=e9x z`aq;5&>ze$AE*!rGGMoIp&E4AkX%!E4(u;8?nLZp(GXK#atpfEf-++R?>SW`0Vs4(bLpB81i*!H%Q`+IT z2qTZoyjF_8Dhwr6wZC?6ZRzSUXv2u;1r4Vq8w;=MmWqJE9>qClYO4N0d)#j)C5 zp7d83kREWXz5QWvx;e%Gu<5Z!{OB!?kM%5khtR)~A~7H{Pu(#|D;?UKO9t@R4bSe% zcRnZgQw`4q*a5i;Ct`&l3|vQfTQ2W}^P_`zALfbd+hJ`N`8?M<#ZI$=At;SnICWj` zQ=x3fXh72p=%7PdT&rxeat?A`xiWrW=$#L?|uanz}q%CK5n6;p|2UrXPax-DQ| zp*UHQo$$c)dKvS|kFB3*5#W0HdsAn{DuFr@PzNVP;?oLHCK|uT#@rJsK%kjLOC@D9#DMu9wLms>xy^1)DO5A^184-N4VS)>|j2@G`l8Z>sHq&eml zLX!YCWMBK~MIvoacFgN+#Qy+q83y1(Ku$=f6->TDwE&|i9 zRCq|3hFc0)K}i2#(lG`i_{ra)`sUBX6pA|q2<0EuqYvN$BnUpx_J5ZZPia7bQ6$p+ zDm(uYll?uwhJTlxe`V4FU0{(w?tjS#khmzRej&e$AA|zz@kvb#Z+wPO0MI=OB2Tj%s`>k%y1t4anZTX z;>Z1;RpfEhBN?>vlAe*I%blMI9hvqs8y6`EuMpkwZ9fv9Da5okuLIbToeL}HQC zRhk#QDDum&)$@T#l4QsiN0+r1;^KZ13YH08<8be>|8a?oRv{86$Xc(*iXVCt6R+B6 z?)YE_P{eW6eI@p!W8+r*F=lGQKL9W^ZQIcBYh}H}#dGKlq?AY@-ae(~y7{?qU0|6O z3?I8>=cU_u8eg?hk5m&|n`4b@-V8`S)H9@g2U-^=K;L3WhU5n0#x0Gl8MHq3=80MR z%+a%D8>uBGy7_GuNdJg4A3>QIiWKU39%?(AV*%)|AMK=WEi=CPBI%A&_EOp1`&0R2 zb+yH#U-cVqYB}d)S$~yfp2>!IzDJh~npo6@f4uIxhLSMs&&@{eosu~XnwWvehv@JK z-sq~Hr1#kXg4vH_%zSzbR3O!#3bX}_;y{K!O@kjKp8wO1Z1q55`ag+pfyC|qAqMN+ zK-xcvtwG}a{}3C3#Q)>L5@7gN2>HYNZ*~BFz9IK#{VP!n5OEnd`{bV@r2RTx?-@p6^Vk@@zflz{fq4Lbt(Zn6D+A zRCXF1LBV>whcY}&TYG+B z_eAJ6>!gQE%26xN<(*>X6R4(pT|v=-pUsbm?abXJO|Lf?CJD=l{kdzTG{7aW)M8-8 zi%o;*VCakg*%vw*t`8h|A#3H*#99yII(1a&+0=sFX$s4Qh@=7SnJJ#e<r;0@7USGL|B8BgzQ8bRgtCDD;7 zhYgu!7>V$B`QM{!Z7nyon122-lXkmjRr1F>Ez1L=C6k|gJx&&zB&^`Ss3Rkl(_9S2 zvR83;4;bhySBhc9QhEt^EDT-+YihAwcI>Fs$LN2OPtUaPUw9(*R?6YqVr$ALCmP5I z<!2OI$X5aioIE1p6W@b7Yeygl}VTa`S*A!ae?d3a*dQRe())#!G$YI#@E8;5T_Hh3I|v2MHg2Pasml%B~9&i?yd z<7-lO`eFgbRkXrZBlK4(t)lxx4j8*+p#jnLM>!L=I4L5&rvaH)9SP|ll0qAoOit-n zM6OzfLRKvHJfHX}*>M`jtX7%6!CxA1?g>BQL<|guQyI(<XNk>3r4uTlDxSe6+Eryh(K4n{;P3=xWUI@SsG_wf#`ZvC+{IDLq)d z!La+`iJ`4GApTG^`{t2mPEbgnO|C_~qZdoLD?>*c10Cd?WASS1r!Q$s#+oB`^?~64 z2Emze!eMaX$&ygsaFp>-(gg4V@l?T^yOz-6z}#Ey_>XiJRf9WyXC^mpN470{QQ*J# zrFk}M(()r?;cbe`=eBeWM6|I{k%J9ek E2L~@62><{9 literal 15291 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 diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 23f4347b7..685f9450b 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2187,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":181,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2189,"get_count":3,"put_bytes":262,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2270,"get_count":3,"put_bytes":346,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":420,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2428,"get_count":3,"put_bytes":494,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2502,"get_count":3,"put_bytes":568,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2576,"get_count":3,"put_bytes":642,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2650,"get_count":3,"put_bytes":716,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2724,"get_count":3,"put_bytes":790,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2798,"get_count":3,"put_bytes":864,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2872,"get_count":3,"put_bytes":938,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2946,"get_count":3,"put_bytes":1012,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":3020,"get_count":3,"put_bytes":1086,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":3094,"get_count":3,"put_bytes":1160,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":3168,"get_count":3,"put_bytes":1234,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":3242,"get_count":3,"put_bytes":1309,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":3317,"get_count":3,"put_bytes":1384,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":3392,"get_count":3,"put_bytes":1459,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":3467,"get_count":3,"put_bytes":1534,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":3542,"get_count":3,"put_bytes":1608,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":3616,"get_count":3,"put_bytes":1683,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":3691,"get_count":3,"put_bytes":1758,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":3766,"get_count":3,"put_bytes":1832,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1908,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":3916,"get_count":3,"put_bytes":1983,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":3991,"get_count":3,"put_bytes":2058,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":4066,"get_count":3,"put_bytes":2133,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":4141,"get_count":3,"put_bytes":2208,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":4216,"get_count":3,"put_bytes":2283,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":4291,"get_count":3,"put_bytes":2358,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":4366,"get_count":3,"put_bytes":2433,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":4441,"get_count":3,"put_bytes":2507,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":4515,"get_count":3,"put_bytes":2582,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":4590,"get_count":3,"put_bytes":2657,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":4665,"get_count":3,"put_bytes":2732,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":4740,"get_count":3,"put_bytes":2806,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":4814,"get_count":3,"put_bytes":2881,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":4889,"get_count":3,"put_bytes":2956,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":4964,"get_count":3,"put_bytes":3031,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":5039,"get_count":3,"put_bytes":3106,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":5114,"get_count":3,"put_bytes":3181,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":5189,"get_count":3,"put_bytes":3256,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":5264,"get_count":3,"put_bytes":3331,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":5339,"get_count":3,"put_bytes":3405,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":5413,"get_count":3,"put_bytes":3480,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":5488,"get_count":3,"put_bytes":3555,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":5563,"get_count":3,"put_bytes":3630,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":5638,"get_count":3,"put_bytes":3705,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":5713,"get_count":3,"put_bytes":3780,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":5788,"get_count":3,"put_bytes":3855,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":5863,"get_count":3,"put_bytes":3930,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":5938,"get_count":3,"put_bytes":4005,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":6013,"get_count":3,"put_bytes":4080,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":6088,"get_count":3,"put_bytes":4155,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":6163,"get_count":3,"put_bytes":4230,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":6238,"get_count":3,"put_bytes":4305,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":6313,"get_count":3,"put_bytes":4380,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":6388,"get_count":3,"put_bytes":4455,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":6463,"get_count":3,"put_bytes":4530,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":6538,"get_count":3,"put_bytes":4604,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":6612,"get_count":3,"put_bytes":4678,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":6686,"get_count":3,"put_bytes":4753,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":6761,"get_count":3,"put_bytes":4828,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":6836,"get_count":3,"put_bytes":4903,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":6911,"get_count":3,"put_bytes":4978,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":6986,"get_count":3,"put_bytes":5053,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":7061,"get_count":3,"put_bytes":5127,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":7135,"get_count":3,"put_bytes":5202,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":7210,"get_count":3,"put_bytes":5277,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":7285,"get_count":3,"put_bytes":5352,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":7360,"get_count":3,"put_bytes":5427,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":7435,"get_count":3,"put_bytes":5502,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":7510,"get_count":3,"put_bytes":5577,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":7585,"get_count":3,"put_bytes":5651,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":7659,"get_count":3,"put_bytes":5725,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":7733,"get_count":3,"put_bytes":5800,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":7808,"get_count":3,"put_bytes":5874,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":7882,"get_count":3,"put_bytes":5948,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":7956,"get_count":3,"put_bytes":6023,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":8031,"get_count":3,"put_bytes":6098,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":8106,"get_count":3,"put_bytes":6172,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":8180,"get_count":3,"put_bytes":6247,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":8255,"get_count":3,"put_bytes":6322,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":8330,"get_count":3,"put_bytes":6397,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":8405,"get_count":3,"put_bytes":6471,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":8479,"get_count":3,"put_bytes":6545,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":8553,"get_count":3,"put_bytes":6619,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":8627,"get_count":3,"put_bytes":6693,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":8701,"get_count":3,"put_bytes":6768,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":8776,"get_count":3,"put_bytes":6843,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":8851,"get_count":3,"put_bytes":6917,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":8925,"get_count":3,"put_bytes":6991,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":8999,"get_count":3,"put_bytes":7065,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":9073,"get_count":3,"put_bytes":7140,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":9148,"get_count":3,"put_bytes":7215,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":9223,"get_count":3,"put_bytes":7289,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":9297,"get_count":3,"put_bytes":7363,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":9371,"get_count":3,"put_bytes":7438,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":9446,"get_count":3,"put_bytes":7512,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":9520,"get_count":3,"put_bytes":88,"put_count":1}} -{"i":1,"series":2,"stats":{"get_bytes":9520,"get_count":3,"put_bytes":7586,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":9594,"get_count":3,"put_bytes":7659,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":9667,"get_count":3,"put_bytes":7733,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":9741,"get_count":3,"put_bytes":7807,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":9815,"get_count":3,"put_bytes":7880,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":9888,"get_count":3,"put_bytes":7953,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":9961,"get_count":3,"put_bytes":8027,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":10035,"get_count":3,"put_bytes":8101,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":10109,"get_count":3,"put_bytes":8175,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":10183,"get_count":3,"put_bytes":8248,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":10256,"get_count":3,"put_bytes":8321,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":10329,"get_count":3,"put_bytes":8395,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":10403,"get_count":3,"put_bytes":8469,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":10477,"get_count":3,"put_bytes":8542,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":10550,"get_count":3,"put_bytes":8615,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":10623,"get_count":3,"put_bytes":8690,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":10698,"get_count":3,"put_bytes":8765,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":10773,"get_count":3,"put_bytes":8840,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":10848,"get_count":3,"put_bytes":8915,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":10923,"get_count":3,"put_bytes":8990,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":10998,"get_count":3,"put_bytes":9065,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":11073,"get_count":3,"put_bytes":9139,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":11147,"get_count":3,"put_bytes":9214,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":11222,"get_count":3,"put_bytes":9288,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":11296,"get_count":3,"put_bytes":9363,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":11371,"get_count":3,"put_bytes":9438,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":11446,"get_count":3,"put_bytes":9512,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":11520,"get_count":3,"put_bytes":9587,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":11595,"get_count":3,"put_bytes":9661,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":11669,"get_count":3,"put_bytes":9736,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":11744,"get_count":3,"put_bytes":9810,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":11818,"get_count":3,"put_bytes":9884,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":11892,"get_count":3,"put_bytes":9958,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":11966,"get_count":3,"put_bytes":10032,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":12040,"get_count":3,"put_bytes":10106,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":12114,"get_count":3,"put_bytes":10180,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":12188,"get_count":3,"put_bytes":10255,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":12263,"get_count":3,"put_bytes":10330,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":12338,"get_count":3,"put_bytes":10404,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":12412,"get_count":3,"put_bytes":10478,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":12486,"get_count":3,"put_bytes":10553,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":12561,"get_count":3,"put_bytes":10628,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":12636,"get_count":3,"put_bytes":10703,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":12711,"get_count":3,"put_bytes":10778,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":12786,"get_count":3,"put_bytes":10853,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":12861,"get_count":3,"put_bytes":10927,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":12935,"get_count":3,"put_bytes":11001,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":13009,"get_count":3,"put_bytes":11075,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":13083,"get_count":3,"put_bytes":11150,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":13158,"get_count":3,"put_bytes":11224,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":13232,"get_count":3,"put_bytes":11298,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":13306,"get_count":3,"put_bytes":11372,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":13380,"get_count":3,"put_bytes":11447,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":13455,"get_count":3,"put_bytes":11522,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":13530,"get_count":3,"put_bytes":11597,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":13605,"get_count":3,"put_bytes":11672,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":13680,"get_count":3,"put_bytes":11746,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":13754,"get_count":3,"put_bytes":11821,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":13829,"get_count":3,"put_bytes":11896,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":13904,"get_count":3,"put_bytes":11970,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":13978,"get_count":3,"put_bytes":12044,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":14052,"get_count":3,"put_bytes":12119,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":14127,"get_count":3,"put_bytes":12193,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":14201,"get_count":3,"put_bytes":12267,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":14275,"get_count":3,"put_bytes":12342,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":14350,"get_count":3,"put_bytes":12417,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":14425,"get_count":3,"put_bytes":12491,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":14499,"get_count":3,"put_bytes":12565,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":14573,"get_count":3,"put_bytes":12640,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":14648,"get_count":3,"put_bytes":12790,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":14468,"get_count":3,"put_bytes":12534,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":14542,"get_count":3,"put_bytes":12609,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":14617,"get_count":3,"put_bytes":12683,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":14691,"get_count":3,"put_bytes":12757,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":14765,"get_count":3,"put_bytes":12831,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":14839,"get_count":3,"put_bytes":12986,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":14660,"get_count":3,"put_bytes":12727,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":14735,"get_count":3,"put_bytes":12801,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":14809,"get_count":3,"put_bytes":12875,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":14883,"get_count":3,"put_bytes":12950,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":15288,"get_count":4,"put_bytes":13355,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":14958,"get_count":3,"put_bytes":13023,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":15031,"get_count":3,"put_bytes":13098,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":15106,"get_count":3,"put_bytes":13172,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":15180,"get_count":3,"put_bytes":13246,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":15254,"get_count":3,"put_bytes":13320,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":15328,"get_count":3,"put_bytes":13394,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":15402,"get_count":3,"put_bytes":13469,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":15477,"get_count":3,"put_bytes":13544,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":15552,"get_count":3,"put_bytes":13619,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":15627,"get_count":3,"put_bytes":13693,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":15701,"get_count":3,"put_bytes":13768,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":15776,"get_count":3,"put_bytes":13842,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":15850,"get_count":3,"put_bytes":13917,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":15925,"get_count":3,"put_bytes":13992,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":16000,"get_count":3,"put_bytes":14066,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":16074,"get_count":3,"put_bytes":14141,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":16149,"get_count":3,"put_bytes":14215,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":16557,"get_count":4,"put_bytes":14624,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":145,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":190,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2198,"get_count":3,"put_bytes":238,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":276,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2284,"get_count":3,"put_bytes":314,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2322,"get_count":3,"put_bytes":352,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2360,"get_count":3,"put_bytes":390,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2398,"get_count":3,"put_bytes":428,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2436,"get_count":3,"put_bytes":466,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2474,"get_count":3,"put_bytes":504,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":542,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2550,"get_count":3,"put_bytes":580,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2588,"get_count":3,"put_bytes":618,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2626,"get_count":3,"put_bytes":656,"put_count":2}} +{"i":15,"series":1,"stats":{"get_bytes":2664,"get_count":3,"put_bytes":694,"put_count":2}} +{"i":16,"series":1,"stats":{"get_bytes":2702,"get_count":3,"put_bytes":732,"put_count":2}} +{"i":17,"series":1,"stats":{"get_bytes":2740,"get_count":3,"put_bytes":770,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2778,"get_count":3,"put_bytes":808,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2816,"get_count":3,"put_bytes":846,"put_count":2}} +{"i":20,"series":1,"stats":{"get_bytes":2854,"get_count":3,"put_bytes":884,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2892,"get_count":3,"put_bytes":922,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2930,"get_count":3,"put_bytes":960,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2968,"get_count":3,"put_bytes":998,"put_count":2}} +{"i":24,"series":1,"stats":{"get_bytes":3006,"get_count":3,"put_bytes":1037,"put_count":2}} +{"i":25,"series":1,"stats":{"get_bytes":3045,"get_count":3,"put_bytes":1075,"put_count":2}} +{"i":26,"series":1,"stats":{"get_bytes":3083,"get_count":3,"put_bytes":1113,"put_count":2}} +{"i":27,"series":1,"stats":{"get_bytes":3121,"get_count":3,"put_bytes":1151,"put_count":2}} +{"i":28,"series":1,"stats":{"get_bytes":3159,"get_count":3,"put_bytes":1189,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":3197,"get_count":3,"put_bytes":1227,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":3235,"get_count":3,"put_bytes":1265,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3273,"get_count":3,"put_bytes":1303,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3311,"get_count":3,"put_bytes":1340,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3348,"get_count":3,"put_bytes":1378,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3386,"get_count":3,"put_bytes":1416,"put_count":2}} +{"i":35,"series":1,"stats":{"get_bytes":3424,"get_count":3,"put_bytes":1454,"put_count":2}} +{"i":36,"series":1,"stats":{"get_bytes":3462,"get_count":3,"put_bytes":1491,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3499,"get_count":3,"put_bytes":1529,"put_count":2}} +{"i":38,"series":1,"stats":{"get_bytes":3537,"get_count":3,"put_bytes":1567,"put_count":2}} +{"i":39,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1605,"put_count":2}} +{"i":40,"series":1,"stats":{"get_bytes":3613,"get_count":3,"put_bytes":1643,"put_count":2}} +{"i":41,"series":1,"stats":{"get_bytes":3651,"get_count":3,"put_bytes":1681,"put_count":2}} +{"i":42,"series":1,"stats":{"get_bytes":3689,"get_count":3,"put_bytes":1719,"put_count":2}} +{"i":43,"series":1,"stats":{"get_bytes":3727,"get_count":3,"put_bytes":1757,"put_count":2}} +{"i":44,"series":1,"stats":{"get_bytes":3765,"get_count":3,"put_bytes":1794,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3802,"get_count":3,"put_bytes":1832,"put_count":2}} +{"i":46,"series":1,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1870,"put_count":2}} +{"i":47,"series":1,"stats":{"get_bytes":3878,"get_count":3,"put_bytes":1908,"put_count":2}} +{"i":48,"series":1,"stats":{"get_bytes":3916,"get_count":3,"put_bytes":1946,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3954,"get_count":3,"put_bytes":1984,"put_count":2}} +{"i":50,"series":1,"stats":{"get_bytes":3992,"get_count":3,"put_bytes":2022,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":4030,"get_count":3,"put_bytes":2060,"put_count":2}} +{"i":52,"series":1,"stats":{"get_bytes":4068,"get_count":3,"put_bytes":2098,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":4106,"get_count":3,"put_bytes":2136,"put_count":2}} +{"i":54,"series":1,"stats":{"get_bytes":4144,"get_count":3,"put_bytes":2174,"put_count":2}} +{"i":55,"series":1,"stats":{"get_bytes":4182,"get_count":3,"put_bytes":2212,"put_count":2}} +{"i":56,"series":1,"stats":{"get_bytes":4220,"get_count":3,"put_bytes":2250,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":4258,"get_count":3,"put_bytes":2288,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":4296,"get_count":3,"put_bytes":2326,"put_count":2}} +{"i":59,"series":1,"stats":{"get_bytes":4334,"get_count":3,"put_bytes":2364,"put_count":2}} +{"i":60,"series":1,"stats":{"get_bytes":4372,"get_count":3,"put_bytes":2402,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":4410,"get_count":3,"put_bytes":2439,"put_count":2}} +{"i":62,"series":1,"stats":{"get_bytes":4447,"get_count":3,"put_bytes":2477,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":4485,"get_count":3,"put_bytes":2515,"put_count":2}} +{"i":64,"series":1,"stats":{"get_bytes":4523,"get_count":3,"put_bytes":2553,"put_count":2}} +{"i":65,"series":1,"stats":{"get_bytes":4561,"get_count":3,"put_bytes":2591,"put_count":2}} +{"i":66,"series":1,"stats":{"get_bytes":4599,"get_count":3,"put_bytes":2629,"put_count":2}} +{"i":67,"series":1,"stats":{"get_bytes":4637,"get_count":3,"put_bytes":2666,"put_count":2}} +{"i":68,"series":1,"stats":{"get_bytes":4674,"get_count":3,"put_bytes":2704,"put_count":2}} +{"i":69,"series":1,"stats":{"get_bytes":4712,"get_count":3,"put_bytes":2742,"put_count":2}} +{"i":70,"series":1,"stats":{"get_bytes":4750,"get_count":3,"put_bytes":2780,"put_count":2}} +{"i":71,"series":1,"stats":{"get_bytes":4788,"get_count":3,"put_bytes":2818,"put_count":2}} +{"i":72,"series":1,"stats":{"get_bytes":4826,"get_count":3,"put_bytes":2856,"put_count":2}} +{"i":73,"series":1,"stats":{"get_bytes":4864,"get_count":3,"put_bytes":2894,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":4902,"get_count":3,"put_bytes":2931,"put_count":2}} +{"i":75,"series":1,"stats":{"get_bytes":4939,"get_count":3,"put_bytes":2968,"put_count":2}} +{"i":76,"series":1,"stats":{"get_bytes":4976,"get_count":3,"put_bytes":3006,"put_count":2}} +{"i":77,"series":1,"stats":{"get_bytes":5014,"get_count":3,"put_bytes":3043,"put_count":2}} +{"i":78,"series":1,"stats":{"get_bytes":5051,"get_count":3,"put_bytes":3080,"put_count":2}} +{"i":79,"series":1,"stats":{"get_bytes":5088,"get_count":3,"put_bytes":3118,"put_count":2}} +{"i":80,"series":1,"stats":{"get_bytes":5126,"get_count":3,"put_bytes":3156,"put_count":2}} +{"i":81,"series":1,"stats":{"get_bytes":5164,"get_count":3,"put_bytes":3194,"put_count":2}} +{"i":82,"series":1,"stats":{"get_bytes":5202,"get_count":3,"put_bytes":3232,"put_count":2}} +{"i":83,"series":1,"stats":{"get_bytes":5240,"get_count":3,"put_bytes":3270,"put_count":2}} +{"i":84,"series":1,"stats":{"get_bytes":5278,"get_count":3,"put_bytes":3308,"put_count":2}} +{"i":85,"series":1,"stats":{"get_bytes":5316,"get_count":3,"put_bytes":3345,"put_count":2}} +{"i":86,"series":1,"stats":{"get_bytes":5353,"get_count":3,"put_bytes":3382,"put_count":2}} +{"i":87,"series":1,"stats":{"get_bytes":5390,"get_count":3,"put_bytes":3419,"put_count":2}} +{"i":88,"series":1,"stats":{"get_bytes":5427,"get_count":3,"put_bytes":3456,"put_count":2}} +{"i":89,"series":1,"stats":{"get_bytes":5464,"get_count":3,"put_bytes":3494,"put_count":2}} +{"i":90,"series":1,"stats":{"get_bytes":5502,"get_count":3,"put_bytes":3532,"put_count":2}} +{"i":91,"series":1,"stats":{"get_bytes":5540,"get_count":3,"put_bytes":3570,"put_count":2}} +{"i":92,"series":1,"stats":{"get_bytes":5578,"get_count":3,"put_bytes":3607,"put_count":2}} +{"i":93,"series":1,"stats":{"get_bytes":5615,"get_count":3,"put_bytes":3645,"put_count":2}} +{"i":94,"series":1,"stats":{"get_bytes":5653,"get_count":3,"put_bytes":3683,"put_count":2}} +{"i":95,"series":1,"stats":{"get_bytes":5691,"get_count":3,"put_bytes":3721,"put_count":2}} +{"i":96,"series":1,"stats":{"get_bytes":5729,"get_count":3,"put_bytes":3758,"put_count":2}} +{"i":97,"series":1,"stats":{"get_bytes":5766,"get_count":3,"put_bytes":3795,"put_count":2}} +{"i":98,"series":1,"stats":{"get_bytes":5803,"get_count":3,"put_bytes":3833,"put_count":2}} +{"i":99,"series":1,"stats":{"get_bytes":5841,"get_count":3,"put_bytes":3870,"put_count":2}} +{"i":0,"series":2,"stats":{"get_bytes":5878,"get_count":3,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":5878,"get_count":3,"put_bytes":3908,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":5916,"get_count":3,"put_bytes":3945,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":5953,"get_count":3,"put_bytes":3983,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":5991,"get_count":3,"put_bytes":4021,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":6029,"get_count":3,"put_bytes":4059,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":6067,"get_count":3,"put_bytes":4096,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":6104,"get_count":3,"put_bytes":4134,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":6142,"get_count":3,"put_bytes":4172,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":6180,"get_count":3,"put_bytes":4210,"put_count":2}} +{"i":10,"series":2,"stats":{"get_bytes":6218,"get_count":3,"put_bytes":4247,"put_count":2}} +{"i":11,"series":2,"stats":{"get_bytes":6255,"get_count":3,"put_bytes":4285,"put_count":2}} +{"i":12,"series":2,"stats":{"get_bytes":6293,"get_count":3,"put_bytes":4323,"put_count":2}} +{"i":13,"series":2,"stats":{"get_bytes":6331,"get_count":3,"put_bytes":4361,"put_count":2}} +{"i":14,"series":2,"stats":{"get_bytes":6369,"get_count":3,"put_bytes":4398,"put_count":2}} +{"i":15,"series":2,"stats":{"get_bytes":6406,"get_count":3,"put_bytes":4435,"put_count":2}} +{"i":16,"series":2,"stats":{"get_bytes":6443,"get_count":3,"put_bytes":4473,"put_count":2}} +{"i":17,"series":2,"stats":{"get_bytes":6481,"get_count":3,"put_bytes":4511,"put_count":2}} +{"i":18,"series":2,"stats":{"get_bytes":6519,"get_count":3,"put_bytes":4549,"put_count":2}} +{"i":19,"series":2,"stats":{"get_bytes":6557,"get_count":3,"put_bytes":4587,"put_count":2}} +{"i":20,"series":2,"stats":{"get_bytes":6595,"get_count":3,"put_bytes":4625,"put_count":2}} +{"i":21,"series":2,"stats":{"get_bytes":6633,"get_count":3,"put_bytes":4663,"put_count":2}} +{"i":22,"series":2,"stats":{"get_bytes":6671,"get_count":3,"put_bytes":4700,"put_count":2}} +{"i":23,"series":2,"stats":{"get_bytes":6708,"get_count":3,"put_bytes":4738,"put_count":2}} +{"i":24,"series":2,"stats":{"get_bytes":6746,"get_count":3,"put_bytes":4775,"put_count":2}} +{"i":25,"series":2,"stats":{"get_bytes":6783,"get_count":3,"put_bytes":4813,"put_count":2}} +{"i":26,"series":2,"stats":{"get_bytes":6821,"get_count":3,"put_bytes":4851,"put_count":2}} +{"i":27,"series":2,"stats":{"get_bytes":6859,"get_count":3,"put_bytes":4888,"put_count":2}} +{"i":28,"series":2,"stats":{"get_bytes":6896,"get_count":3,"put_bytes":4926,"put_count":2}} +{"i":29,"series":2,"stats":{"get_bytes":6934,"get_count":3,"put_bytes":4963,"put_count":2}} +{"i":30,"series":2,"stats":{"get_bytes":6971,"get_count":3,"put_bytes":5001,"put_count":2}} +{"i":31,"series":2,"stats":{"get_bytes":7009,"get_count":3,"put_bytes":5038,"put_count":2}} +{"i":32,"series":2,"stats":{"get_bytes":7046,"get_count":3,"put_bytes":5075,"put_count":2}} +{"i":33,"series":2,"stats":{"get_bytes":7083,"get_count":3,"put_bytes":5112,"put_count":2}} +{"i":34,"series":2,"stats":{"get_bytes":7120,"get_count":3,"put_bytes":5149,"put_count":2}} +{"i":35,"series":2,"stats":{"get_bytes":7157,"get_count":3,"put_bytes":5186,"put_count":2}} +{"i":36,"series":2,"stats":{"get_bytes":7194,"get_count":3,"put_bytes":5223,"put_count":2}} +{"i":37,"series":2,"stats":{"get_bytes":7231,"get_count":3,"put_bytes":5261,"put_count":2}} +{"i":38,"series":2,"stats":{"get_bytes":7269,"get_count":3,"put_bytes":5299,"put_count":2}} +{"i":39,"series":2,"stats":{"get_bytes":7307,"get_count":3,"put_bytes":5336,"put_count":2}} +{"i":40,"series":2,"stats":{"get_bytes":7344,"get_count":3,"put_bytes":5373,"put_count":2}} +{"i":41,"series":2,"stats":{"get_bytes":7381,"get_count":3,"put_bytes":5411,"put_count":2}} +{"i":42,"series":2,"stats":{"get_bytes":7419,"get_count":3,"put_bytes":5449,"put_count":2}} +{"i":43,"series":2,"stats":{"get_bytes":7457,"get_count":3,"put_bytes":5487,"put_count":2}} +{"i":44,"series":2,"stats":{"get_bytes":7495,"get_count":3,"put_bytes":5525,"put_count":2}} +{"i":45,"series":2,"stats":{"get_bytes":7533,"get_count":3,"put_bytes":5563,"put_count":2}} +{"i":46,"series":2,"stats":{"get_bytes":7571,"get_count":3,"put_bytes":5600,"put_count":2}} +{"i":47,"series":2,"stats":{"get_bytes":7608,"get_count":3,"put_bytes":5637,"put_count":2}} +{"i":48,"series":2,"stats":{"get_bytes":7645,"get_count":3,"put_bytes":5674,"put_count":2}} +{"i":49,"series":2,"stats":{"get_bytes":7682,"get_count":3,"put_bytes":5712,"put_count":2}} +{"i":50,"series":2,"stats":{"get_bytes":7720,"get_count":3,"put_bytes":5749,"put_count":2}} +{"i":51,"series":2,"stats":{"get_bytes":7757,"get_count":3,"put_bytes":5786,"put_count":2}} +{"i":52,"series":2,"stats":{"get_bytes":7794,"get_count":3,"put_bytes":5824,"put_count":2}} +{"i":53,"series":2,"stats":{"get_bytes":7832,"get_count":3,"put_bytes":5862,"put_count":2}} +{"i":54,"series":2,"stats":{"get_bytes":7870,"get_count":3,"put_bytes":5900,"put_count":2}} +{"i":55,"series":2,"stats":{"get_bytes":7908,"get_count":3,"put_bytes":5938,"put_count":2}} +{"i":56,"series":2,"stats":{"get_bytes":7946,"get_count":3,"put_bytes":5976,"put_count":2}} +{"i":57,"series":2,"stats":{"get_bytes":7984,"get_count":3,"put_bytes":6013,"put_count":2}} +{"i":58,"series":2,"stats":{"get_bytes":8021,"get_count":3,"put_bytes":6051,"put_count":2}} +{"i":59,"series":2,"stats":{"get_bytes":8059,"get_count":3,"put_bytes":6089,"put_count":2}} +{"i":60,"series":2,"stats":{"get_bytes":8097,"get_count":3,"put_bytes":6126,"put_count":2}} +{"i":61,"series":2,"stats":{"get_bytes":8134,"get_count":3,"put_bytes":6163,"put_count":2}} +{"i":62,"series":2,"stats":{"get_bytes":8171,"get_count":3,"put_bytes":6201,"put_count":2}} +{"i":63,"series":2,"stats":{"get_bytes":8209,"get_count":3,"put_bytes":6238,"put_count":2}} +{"i":64,"series":2,"stats":{"get_bytes":8246,"get_count":3,"put_bytes":6275,"put_count":2}} +{"i":65,"series":2,"stats":{"get_bytes":8283,"get_count":3,"put_bytes":6313,"put_count":2}} +{"i":66,"series":2,"stats":{"get_bytes":8321,"get_count":3,"put_bytes":6351,"put_count":2}} +{"i":67,"series":2,"stats":{"get_bytes":8359,"get_count":3,"put_bytes":6388,"put_count":2}} +{"i":68,"series":2,"stats":{"get_bytes":8396,"get_count":3,"put_bytes":6425,"put_count":2}} +{"i":69,"series":2,"stats":{"get_bytes":8433,"get_count":3,"put_bytes":6463,"put_count":2}} +{"i":70,"series":2,"stats":{"get_bytes":8471,"get_count":3,"put_bytes":6576,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":8402,"get_count":3,"put_bytes":6431,"put_count":2}} +{"i":72,"series":2,"stats":{"get_bytes":8439,"get_count":3,"put_bytes":6469,"put_count":2}} +{"i":73,"series":2,"stats":{"get_bytes":8477,"get_count":3,"put_bytes":6506,"put_count":2}} +{"i":74,"series":2,"stats":{"get_bytes":8514,"get_count":3,"put_bytes":6543,"put_count":2}} +{"i":75,"series":2,"stats":{"get_bytes":8551,"get_count":3,"put_bytes":6580,"put_count":2}} +{"i":76,"series":2,"stats":{"get_bytes":8588,"get_count":3,"put_bytes":6698,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":8519,"get_count":3,"put_bytes":6549,"put_count":2}} +{"i":78,"series":2,"stats":{"get_bytes":8557,"get_count":3,"put_bytes":6586,"put_count":2}} +{"i":79,"series":2,"stats":{"get_bytes":8594,"get_count":3,"put_bytes":6623,"put_count":2}} +{"i":80,"series":2,"stats":{"get_bytes":8631,"get_count":3,"put_bytes":6661,"put_count":2}} +{"i":81,"series":2,"stats":{"get_bytes":8851,"get_count":4,"put_bytes":6881,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":8669,"get_count":3,"put_bytes":6698,"put_count":2}} +{"i":83,"series":2,"stats":{"get_bytes":8706,"get_count":3,"put_bytes":6736,"put_count":2}} +{"i":84,"series":2,"stats":{"get_bytes":8744,"get_count":3,"put_bytes":6773,"put_count":2}} +{"i":85,"series":2,"stats":{"get_bytes":8781,"get_count":3,"put_bytes":6810,"put_count":2}} +{"i":86,"series":2,"stats":{"get_bytes":8818,"get_count":3,"put_bytes":6847,"put_count":2}} +{"i":87,"series":2,"stats":{"get_bytes":8855,"get_count":3,"put_bytes":6884,"put_count":2}} +{"i":88,"series":2,"stats":{"get_bytes":8892,"get_count":3,"put_bytes":6922,"put_count":2}} +{"i":89,"series":2,"stats":{"get_bytes":8930,"get_count":3,"put_bytes":6960,"put_count":2}} +{"i":90,"series":2,"stats":{"get_bytes":8968,"get_count":3,"put_bytes":6998,"put_count":2}} +{"i":91,"series":2,"stats":{"get_bytes":9006,"get_count":3,"put_bytes":7035,"put_count":2}} +{"i":92,"series":2,"stats":{"get_bytes":9043,"get_count":3,"put_bytes":7073,"put_count":2}} +{"i":93,"series":2,"stats":{"get_bytes":9081,"get_count":3,"put_bytes":7110,"put_count":2}} +{"i":94,"series":2,"stats":{"get_bytes":9118,"get_count":3,"put_bytes":7148,"put_count":2}} +{"i":95,"series":2,"stats":{"get_bytes":9156,"get_count":3,"put_bytes":7186,"put_count":2}} +{"i":96,"series":2,"stats":{"get_bytes":9194,"get_count":3,"put_bytes":7223,"put_count":2}} +{"i":97,"series":2,"stats":{"get_bytes":9231,"get_count":3,"put_bytes":7261,"put_count":2}} +{"i":98,"series":2,"stats":{"get_bytes":9269,"get_count":3,"put_bytes":7298,"put_count":2}} +{"i":99,"series":2,"stats":{"get_bytes":9493,"get_count":4,"put_bytes":7523,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png index 03dd6c5c2e376bdeac7f50e46940aa6a84050718..bf73af71ce94bd85f0dd98a34a93221c29d321f7 100644 GIT binary patch literal 16081 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 literal 16168 zcmb802Urtd)94|9H0e^LiimU&qzO_)I?@S5YN%4ANs|srl`8d*NRdtm66u`?3R0yA zNR5c}-kTwJ6aC)zd!PH==iZwKahW}5&e=J$XJ_X(iPh6lqab4@!^6X)&`?)4z{4X% z;NcP6ARz!Ggz_$T@bK{E^&T0iU@#a!lAoWC{SS$U7luLNVeq!Mk&1XgnfdPByV}~?4h{~Xp`lq>Sv55^ zNF;J#U|?x!2^eBmOe}djx$^t+E_n9`2D3f)0#pC7YI_?qi3f~^X9xTay%~2?IL-w3 zcii>s9>7%a@CKuYHt?`h#NZ8$V6LTNLiKjT!Z0d$=V8y*G6Hs&wqIAZ&n4?Af?Y(yuHl7UyB4Mh{Dg%;kzr6w7_35;2uF)I@yP@1FQXmXql)O4M2Nj^}|M1aco`MGzq*Sqt5r5ur^A?CO zDw$cAygx2iQQGD8NFeg*U`E#!54Gv{K|iCBV%5PEg4}7M7LGnn-!vDeqPH%$L4B_0 znLb}XbStP;tZ|kT<8AJ7B5Ffg2{X23^(&u+O9U$?I(^$w_t6E9#TyVZc-%FRDv+Hs zRPyzQrXON_n?_yvWT#eh$K=4T>SyX*Pf}F7Ev{DW=lSi4J^ds?W4YqDdFiGcRK}L{ zD;P?l`|L@=?T~wz2Y$`Ggiqp-+Uir^RcojDtvOs>TBshK!acJ@-NYAt{q`q{qvBhb zESKa_jbEu)x-#zs;Uh$3or2XR~BvTXB|6zC+9v~-gmnSKZPwEi0H;tdFASoy0RMk{mgHv^rpqGw-218^T`sGYM9}3D=?;NF zkYmD#6{ugX-{;$OZzg&8cuqhRzBt~b6a`$E?%Xmyi5&HTS&|_DBKb%urs~ZJqLWUZ z(D*9fg&ZO%sq%t<@j>@?2Ka>zg9rH-a$n;YacxEq;{1pI^#W$gBj2kV!O*PBMO`_# zwAc>&$ph%hsj z_pIiv5yQ=t+OjNmps>3^l!AWUrPo)l=tYUh34S&L{dYglx7%yycN?efC|-P-S<8^{}q*;-(F?`B#$CFm#`ZX1;7rW5Tf9c);O4)H}B6nS3!J~wJ9+l(% zuz3&B+1X|)_!MX;+r&-VzP3|wbFhnKuFB2n{PR+-q>h~C4_I^UO~k|o@!Z^2U-tLZ z^#Jfe!dN2p>F18qQeO9@zI2B0x_P@{3rRwT^^>odD)F7fJ|S;aXeFat(I^TNYp7(7 z>clI4=qy#k=V*8ktvjxohL=T zUr=pSZ}#B5yTrMH5NlWWP^*Zk?%h`elsD1B0{0%Z9}xIvRY#|9--#{^m@f8V_8)eY zOVjK2erMmEod^k)lAb!(e!>7bM?24M2f6&y>fZ^Q>k0;&Dnu0Ja>EPp^>e$+;QsB_zLT%bye#g@*Pr~NV!Xj| zDO9A_n}kRN<|z`GS;xA3Q?^aTkJUSGPI}VHf6L8cEqCi&PZ*Ia5*%9^vIh=^=6L8d zs&FfG%KUq*pMg&DCG&Z-&DxoA+b^t7Fv5sO3R1}C%Sq`Fw&<2~B+Sm&;Xk^#x2NQR zpz)VX5Ko&_av>?VB?5s&w;2bq2)4kd524@>n@0c9zTZZD3M>Z~ZjP6&AMV;e5bCS| zZ)yauTBFfFV|I?@7~JrDznCbN=w^JC6Fk|@XXy$Mugn|y0okZ_J4& z`bJWmV62p|Lo0l>IoglZBB|FeYkET4ethEeeMWA()@rpYu#Ag-q~RA|9xIlli)1cG zRLPSHj(qJ3_++TgW&cm~l6?7-14MUk_DN1O(X3h%(cw|G(!GQ-%^?zUH}`x{F+(!C z-Qd7b6+HTpzrHP;|C*_yX)P-7QL1%Cs0Nlru1d0^qEMUtA|{Dg&qdoBeo+YNrqknR&F!w_o;_`k(2t z&}>os{&i;Db8VQN6Y&$QRNqnHXl)y$a%@ZJeEiJCm&`HAL#1I@v+=R@ZOa-t6qA+b z=xY!-pjUv*F;gB_nn;`ZOA=fMC;)>%$)rg?7>OtMmv7Iyee36VRHmUH==E}tEE|X zS$GEQV04MJyr{`CGT!{bsJg~@z3!^{&!6;v_@OSqTx5g!04o3)(ybXA1cL9J$OC{yowS4Lk?>VO#p8-YyTgre|3zwSCLli0ID|r&EWs zGX9cE;NnkO3gfm1xWi7$M=*AxG{&t2Ety&~i9bS{XDVPt?CC;}4m;f;>;zjK-P5+* z!8UBN)tC6V_62CU_QMZDny`5-dC4rPdW>qsDgLR6*bop*DOwv z5H59i5pc>SrtqNFlhj?+u*S4a zeav8CqrC2iTaJ)}%jti}L$)!M?_ScHIc*wF+K$g{SJh|ET&@| z-COmim4xjJ!j5CY$0ZZIKP)vH0FU?hsxP6*yc`;J3`DhR91siBCL~#==ymI5mPLUf z=|^dCZsYE}`Ep9orjRsp_;BjFJl6ZMedzSDaB^ zY;70J1KduC&EO6_-5TfNV9&=K<{TR7gpoDTRDv7g@3WUO>pf0(|8c)sCB35P$zQ;K zoy_s>OwOK!L!)ia%h>eFW>V}pvJ&Uu1||HheC0)qYf*BDmLE%iKd>Y6bV0}2 z^cAzOPemaOl(M^Bo!_}~4U7UCBF_VZIbfp#oPrwC?pHjW1h`5Y-5e~iFzk3$mo!IJ z9=@aL^Z_B-e)ELWl31UJ%rOrL&)2OZwj|^493}HpOs@v<+u5z4_PInJjzF;GPv+G9+ zF_7_6HVrq);DsOkxJEtG#*i$k8Ksj>Z%V4*81KE!28oB;mQI44BUhu%;mOm^52OUj zTdpu@MBZ8^vOBj-r{6{`zo++2mX+kukF;YTQ@>c7+D_Z0dE(BM9!SK1=D$^ae{nk6 zaDX>dQXN|ClZv!Kko7$0&-WNHyH9?&!yqYk!D%r~0g5Z?^voqMJEBtqlL&F8Sr5y7 z&?07-<7eGkO4B>aTt2mA$XqV+8$sTLCHJV1mq8exx)+e0nIpnp`4%5L*s)Dnl!rNc zBplx^_egQMrYE=V4xHzKb%d=Ocm%c6OG$iSh4V3&1Zp*)|W(uu&z)5?|B|s+F4Ceh>0vgq<{aB|{aqoez}d8QYg-{0mZMicnEVWPqz+eq0{;G4s1w(1~kjU``Gz z0X0d zP^yV){Q@U8vQSQvFtc_MnbKs=$^YuB`L( zmcR|4E#!Igqj$ho7pP|%Gz~-&!|z6`I)yd`^5aB)ocLrZ0Wre^;?T)WDwe92N6{bo zXM5X0VkYc0Rv&N-1Q1HU1?nJXNj26R+&abjg9GYAmf}R0tGX z0{ar3GxzYkrKJTo)(GT{ZB2xWNr@WOa!huvmr69o z70w(l0{5FXapxDAAFdSeBt!nHE?&NG*_?vd8*cd^Xk?B~O@kk5vytE8SUa+Q9=jx_ zCHOVTwW}#dZgZ5ifb97PpSmc$2tE;V;WSs%kv=-t3SmLeUMS-|d}XH)hHZ6`KlAE0bN@AV#EcA4H>*jgV4iJqSBnxRY?m-oY=S zP9v$I$4}jx!AKdiK*~NUX&&><-?uHNv&%psAI^+=n1hdEArs9fBr|?v%#vBPQu;){ z*MAl+%R=~OUldlS^SO8OUh2WU6&zUY4WUyZtNc!=LUGM?)!J=YSCNp4fWslmM?u4w z`1)(f331|O0w~>aZ3&m41EwsuDnf?cR1H(PdivxP&yBtv+Ge^3Tyw^K;0_?X&RKa6 zcGtpBn0#aIUm_b$^wyTIJiv_1ku{=y|5YI&Lo#ZnjO>_bqBsz;-92$Bq5;K^z8J1z zcIC_W{YT`@CT$C-j&AK(6N)dwKwL5Z-9XXw@FgTc-REVf9X}xIOmH!6!wCaZH zGpC`iB!b9`cV-eFD#L2MO=v?b<(afd1ZU^#%gE{(lIs|A?*`lekvG)K!9dtY0?Wx^ z|Ce)O>@!4a`?j{h5%$sHKC97LMrKJ?_WEl>%xkeV6N2!_K=o-L+G0#jnO^N}YhmV0 z4gDO*h4AoHtaV3 z$p!yHTKomsIm?{8!0qmilJ%aP;qmY^j$j-=L@i3^RqUx~;Nwdmz5^OwX z{_z9X28ZCaWXh9wlWreq2A*{yIz0G8M$_1J8FG6_wXsk|<1v}| zRqEz63wJpW5RVUsRC06~JYrP=^Tz>-G{dyK^wQV=D`w|`IU?36aI4Exe*bUyUR}}t zHz?;S6!;sIbEQ`NJz8`;N`i;tqJ>3i6P>?-ysf$XUoR=2%eRui1tzZtiUB9Y1wOAo zZKc5e1f-=bz7@A|uJhD)=Wmq%RO#>gLWoL-^9x*TD)I%!h;y|GYos$SKuU0rfl=W) z1}PZ@<6atUN7$ZJy8n59AksJ@$6W}W@^}@l+4IAS`ghoXYA5j{%mORgk>x7)EyRgp z-M}|-LDL*HSOXUh`6(H3 zQ^y&Snp&EXc}7(uHsJkGw{Fg=M2j164~{{=^cN|sCQCr@#Hd5ulAG}->d9}biu?#0 z;LV?d4{NHA`&35+H+BpHYc94G%yw&>6ng>)4Mq;-%HNwvpqO$W1zO?H!N&9yTON-b zy=PCjfp7~elerEoy`Sdk?#j!zw3aOTU_3HMg@r3~El*7)R@rmB%D%sN4MdpI7jblf1!1&5M&G);vjPia?bhO`3|_+- z$%Y0jfm^lyuIw;+oCUR%48YOYbrB}g2N!38oQq;ys9Le_UhspFD%f{y)YKJ!6B9mM zLUYU5fN%mEilPkhA1m0a_wJ57-MhD`2F-~mYL1Eoy|L8zStb2IhS1p8yW;(?y0Cct zXV18M3&Ho2md{Dx@iDLsf1!Nin6>m<3053Lz0{B_YVf4E$PKO@9q>V#<@Y3{3^Uh8 zX1$(bz$&I*=f4NqU{W5}zE7_Q>2Fw5DDAB{B!Pch zHJR)4!mkVYSw{AbJKa-91`&4?kz$QlPsHA*p%OZbkP?L_zg#=hNZ_}C># zZ4MGvx3>nN^uA4j_{G+d9a7UCwzX%f-YY<$hM37A=gq12YH~YK0k7|v>V7` zXKTV}H%9x2j3H*zBzFOa3TQaK4NLwuN484xo?{6l0Bkx*%Pd`9JKyG&=~G$^@!3Al zJjNV{{R+yfrRs7DF2`rZCmVzvEwV4cTZi`reuLuy%d`N*Rx)bOt>s}E9#T}m5Su4I-pg!A736W1$n zCR@G9?+8Qyye33M-hA8*QZK>0mc2yYD#i6u8j<{IrivzD%fPWpC6zvbd>4W8wpf0n z(jrjvO9SA^u9U9Hm)|1Y)^`1|+Mtd|e)hc&Z5sUFl8}8HMSyw_3PNMcshR8$`u7pY zJ^`!1yO2oon~43svH4r{!mofe>PcAbO7hsa2TkvAD&DS6 z>4Mv4Mst)LJrU6?!H7*xeu+M^;1gnyyb`C-8BAa-aals&(yvKblW5@}Gfc9F0MtI(A7hwpr+Y0(&{V zPY-sHw&i0EMQ?&B^Y}P41awF-vBR5~OLX9U*VfB_wfXtD8Xxyw1rfXhl0lX0SNOx{H-wiCk)I5r~ z$f-&<{3$GJP%>w~p>`8I8vI6b&(DqV$|FD7pDT;UPNQ31$8ovMDG0mz=>d5l%3t}% zqvN3N+3}9pd;FBc8TW{wTR>OTW*y&&Q*pKFZS)YDWz;SBMVbPKt@h76-7z$C`Ddbx z`hPG5JnwG_W>-qqBuymkdOd1Hod_&Gl>oHt&op#a@u!^2a!96Zj_rKXey(xx^5o$4 z*w!e9lKb-PG`OCBM1G}XHCBK$Zn6Qv}onT$9O$zLzUJ&GuXTX$4~Ld zbmIXuuopk=TD52nHs!1v*YI0Az56b$#YicF6Ykw|wN27Gb?e}a5gn%4Y0e!!eoKe3 zrxMrG`<4&Xf9~<4KBPf8(1|FYc6*enxxe-UY&O5~7GmAFhi{?Z^q$6X*74zwcg4ZC z6u1u`^>x+Co2G2<(^NH7w12c%h4X&2A(lt%EB*$kt7D&L}iGT&J9EkB=9*WPCs z=cDo}t1gPZKF^}nphbrv03QZQU2V==@Tto}pZf)lZTYnu4_PiU-{|#q%IWs0p$%D) zHv#u`U6u8e`;M3cXZDVJZ&LPq4Ju|eo=;lojI6-yPo1n zwT#zl*F?r3rUFl@JSmckQ0-LkqRf$^j!mw!;(H$+rg>+onZFrB513^;=aF};n!mwT z5&XY@%yk-f&dD^}-5zmV``WU1qLo>>R&R3b@=~ssPVEuhd|eAc$mIv57N79c!E(J_}p z%8V)m*zmd~{Y~9oPuqR&^SfV){Ybm6&tD+dDf8EcM7M4srsWalI|+13KO}QO{n|IE zZLSI))Y80E>p_Yw^kONoRqiey!Y=5p2MIh`Vc$o2t~tb1_RN_R(R}yooscU(t^kBD zQVEr?=f5Cp+|{i_)qh@l-BM!5I+7>oD!g(0r01{&xv#t0{^fc6hZg(v{8XaZE^$N< z?HPXMk|zLI@}cu!7QA=~k~cSkAmuUZ8_UNsT^ zcfVKc{TU37Z)xswU6o#W(vJUwM5N;G&m4eI0|`G18>e?&uNn_1@YEb5?q5*zi_V&S zwldui-aMHTJQ)chK;pw8&84?8B9X5$BO>T|>mc9pJR*FbY26B1Bmw({Me&z@o~zohJ8Eb*Ose{E&FvJswu2pyP)KOhBKjpssJDBi4 zwKgq%tu{B=g^C?*%uaX&>Yt;0PyXSOA^iY3x|6x+%VXZPIrzKZm+Ay8s&rjy^)mS9 zYRCJwR377m4yVt5g9(4igGR&7%X+SAv`5v>V`%xwgI|`C1M}ijJ9*->CiX|g_b@?$ zr8;ou&@s(fd+ul;m=U3cY^@;48l1_#>Q!nH9mJfRN{+E`e~x)2RR#Y!#QVD>z3opB z0`Te`_`3CxRM$88gytSJOJ=9G_j>M(%G7g#K56a_D!9zNVA(GU&s7{XAWZb$gwl-`ErX(SK0qol`&3U@1r7#uKf2xh?$8-0|$GS2%rigX`VvUE5J^7*6w&P9J>Y&&vGtZcOkW04NbIShjjnU%#;yrF&TDHe5yCT3 zQ`slMKxIf$DyaV}oY}!h8>Dr3fQTw(;p@6Dc%mVf9W605-@0{Q@P|B^aDG)LeV>!^ zxTh-QSs=lZKz-QeN%5OJF<2s{aT9=~^S^;c3()hnZ?2l<)R=bdXu7)RlU*mrBHE67 z!Twn&8VHi|9)Zi}buoeXjls9Atpm5Qz;^<&%3LRA8M~nMn_OrCf*!d_#9i1YY2RyM@`f|sH2hg^Vh74$F zj%VxSNwhvQO6doUm3|N0O1C^Hcw3!L!d^9m+h z{^E4}*Mu=H^#Sk(vpoPy^NI?}iWFa%^<1OOmJ~JDsV%C=q zJVjRPEH*z5IU{MfJOFYapYIWqrLB|}tpj8!0l2914MdhS8T=p+NDB=+EKY|fHF^+c zF5_7}T{B6*<#uMr-Cu$M=mTJ^#zM5lcpzVXV_Gwvj{iF3OAP>bx4BgsoPRcjrF~PS zV*J8Kw-fZr2*_=!0B`=eWM9tr7A_@AyAm{CQ-?>&O5cFJhy=YFiz0y&P(vDPZviR9 zR)FEaI<`;`%Z#Z_V`(6bWiL}^N+4IzC!VEY|mMd2gajI zbdAC7)st}f@@qIqT+npmJXy8rE8deoASazlNB|3RBtYcc7lG$1Yp-hx`Jdn8HbNKQ zd!K1Potb_G!4V$$EFt{xWd98Gm(c6Oop>W7`tDb^gRohK1cSC;2G-bgBbMVw(N7sd zW&S|@*Eu`Ll^RY5_MD5Dq63NW|_vNa4skopi|gkgSs$F!e!+`DqcA7C7pjY zngrGnYy9s{b1~mqc)qa!NlENL7Vjl)9?9bTINQSgzqqqx({jwW9o)kGULNH#=)g_n z!$f6(jtfo=LpMLd!;EgeNN2)aALDK`iPl6Ieh127^KIz9Z!DX)e z!OrkC-F?B#Wh7U5B8~=IG0(vYaTZ}zFpznl(gqh<+BQda1 zoapmSm@_V`n;CtN{NGu>mEtgFKD(OVkBStb=`YG)qc}jyaBITzZ|ctw|8d8gs+1Mh zJ|Sudj|~DALQW*!)=N&yh0swlu;{|(@rxb@-8Q&^sBSTDShxD^g%k)u`BA181^{V3 zlD1v4TS`!S@20YK^Z`b8nZ9F3HhQlNAfvwr6+Ewk1X5j8!8@o9tGo7ST)!@Tp=-&$ ze&4%9N#KPRp>}91k1x0rX#KO9P8(s}vuB7nl(m5exXo1uL$ zm1dkSQ0Aa>Zzw>tI*|wPHF-p*vKiMvG~k#ktd5cD{DQcq^fG!DPu9D-Phb5ee$4p? zXj4PVN2)s2fKG2|Z+AiICxDjU?*Mt;XRVO{t7ZWFO`-CmMbH{U#w9|u^=EvSAmbeW z@2J1-c6Q$?Ew*rn8vOkQXc|tj;+vu66{2EfEkMRw6^PAPeP5d=N<&%9WEUyX`U&ON z%zgkeTerM=Kr&-((?BYWw#5WLXXiFH^O)$F3gYjkn9c?WvzwlxN|QH`x#}DdwooK$ zb_5c6Ult39jNs(;K+-En7^0}sVi0(@I&t&8k)lMSYGA67bgu+i8T(NKaGq}5O3|mh zscp?4yOhTT*IpP>!(wNaUBERj*XFr4|U(HqcP*4GWHhNWAMzU0%M zVVcUJU)Rc|9j#J|1fZ|x_;aVEk~^v-YeQs5C#CPff8lY?gvZv%ad@5NmR(IM0RX_~ zBpH$)-IFReH*IDqhDD$@ox)Wb$V>oyEgT~J-6dkw0$RsECk#AU3&4|?CLElW)oCoD7(v+<$%XFI*{<;EvV6IK2o=^Wwe4tX8` zcH#1d-76*XidQN;c*u!<3Fmnk7rk!wg;tuEAYOOtM*A^pwyf;|yX}1`C&1E;+1Tf7 zVubVO%;Dx!P>4$=pV30A=%2LVqEBYvNM)zoAbA-L8j=2#hrqOdART3A-uCDnhxbl_ zoEES+jWv()CB9~!OZL+Gh>*2QCKG?Zcg1 z(yV>{7Eoo}VzQdW!%AlFq!1Zu)d^28cd}A{i>L=8#1nG07267;hltr{eQw=7qpF84 zRB=V1+qvZGx(9uX6#-_}$Ai}@$@P_L1tvP3(M%bd@}6mBAj{T)u6-|+1ditFf{Pzp z`4`Q6>Em}beFt~99`}k2zOy0{4B)>pmUr7!VZv=X(g!As;1b50q{8IDh#mdGlO*O}7&yW@jEb(si zh#@TEp8SKj={@lT9*cEsMq|0PPpsV(lgpbptKawipMV-VYz1e{ldypsg8JDz@a)lH9 z{^676_rC#dsz$I}I8ji&G@qvybgEJPW%EATXwIkxVI27y$DUC^&kusMpiN8tF9kZZ zR&@vyyv_Jb&^HlbRPBQYQwDL1M=~b4YdTQ;>y<5nu3AEgFNaMmJ=2h&>eD5%>ELjm zHTH~_4|NzVS_o1!r0tunmQa0H(1=~;clxp%^#&6V`EI>MMm;ye+x!@9kM;|iY>@Jg zyqNYP+A67G`)_!Ac51r<-9Yixaqayr^O~SLxj-OSs_bIu#->|;-07uCr3_tgOA#&% zT<|AVxry-L>74{f^*(S!MO>+^0bNpoF6<|^rjv!IGXl}x#f7XGoE0P>0wSMGTch*= za{7b}?{9Amij%@y0r>o7;YrwCoKToE)QAXo^5 z22f)PeW$si25ci+Pl~Ijxic81CEfhHfIuGlPBKy%D__YJ6))-FiN0`br=fwA^*jcm zQ)tNCqhquEiS0eqk;E=7H61(EkOKQ$uy8ZKc*K?KC*(tSQHU^;SJh#T20 zN+l>~o0&2gK@F+w3uS6pn7Kfq7E!k6D@RuByc^*+DQ(WeJAkh{?VXgrEtFXe?=gT% zT)wbi<*GgD*d1e%vcxQfSF`i}ykRN7@B(&WWv%A3NdSBmgam!gS2UIfWMmlMU}*l` z>}S-)(Ozko3L7|s*de^Qgt48o$wQMVo}~)2ulShinkTjAV%3+zzP&K3wO>uS%=_z* zL&SPee%Q;^b0Z>l@G`Mw0OU=p%s=is9o1KGFk*#0f&+douvIqn94bkrb$ungFv75N zQsIxrw|>-|6+`afV(ia>uK|&6Sno?Ncq&nCY!)h>2?zNPvYY>Y3pgqkl;f-!q+}cn z@Fo?gHO(B%2PbMh<*Eo##K~Tiozr6-n<&X`ToUW76LX$d8p*LDpC@+XZa9$~4LBCy zQ#262E@TP9B88; z26i97saTr%(HNkaV}Pq5o7oHaxj+I$0%<{y+}_HeV$1^%`h)2QAYT%Zn(tg^x2h}u zaPu38_u)k{q~Ym`SmF?G^%rJpLcvLJ89Kl*H`IV~Qn~VNY;m(WsKUW+e2oN-Ha!A5 zCVYe$P(xaroHzhj&ZrNj1-VF#qgX2ejyZppjq+sSE-(YLlRc5b7K?JXcy+1G2)imD zB4CO}>93HQT{*PxbEGvn)8gU$*lVJKr)MtfQAFOX=mXfD3N$@N3cmtp-t;Iq47W#s zDGOjqSVI_-N)y2uaHf={T7ZP1pef)qYpWF zt2V(j!n7Fu9NTbIGOvvC4vLPNirFFDZu-5L0 z$#XyaRC(~~wa=z9bgNVukdeExSZ7EU0Hu`rkw7H^`p;`%fh_iKI}*4u^BLkV61T)g zU%++|TMNsxbBV}uzpC4!0rk8RV@nNj68~4N>D8|-51_BX(v;!p%3Oypfig)CallEJ zxYoK$Bi@2;Qh@}*LwXmYUTTS$btEso zf!K!$Bfjvb0TjBT0GNOtJ3y6z0!c%~G=M@^oMJ^{fJhiYrcKZcE(17p=BYqcoQlyq zX28pei5MP7!hjnVsC87P=T`t zl>^}_#tWu^Q#~VM0C4s#z^ND^J+}d4l4G&ABrq*=R-mhXxQfvN1JEvocmnX7M!+Gf zim);_Ko$hu(!~}J0xnrKhZPP1LSO8xS^y=}3=r=MJS=pdME=xPguVbS$$baB_s|Gm z1ZX!w1Qe@6X@D^@kw33epGF{MGBT9bdwV!QE-uDwdtCRSc|Mze81_9%!`Rzs#wHP< zax&t&!rpDf@nHo&T3f&?Go96et>ymZorI^P@U$Wq?eWCCm@T>3lD5(?@MTGQ^PkKz zd+0x=3MB9d_>TFS2A2Yn@tXx0KairH`;(%sVZ9xmo=h`=k;|n6{K3txZ30z$^AGjb z0#!)9bGNsJq7SyvB6}@ibpSPQ`lFT>Qs++!7yj@G)$z@$=s)|^71?bGbF^KDGtZX!%OuJM~&@OStQKjApa$ z*zL=qN+wm;zRR=5;5XhEZ_x#%08!2Yi1JrNqiQFb|DE>Y@f){1P3OX~?dma-Gxg?v zmQPw8C z$4=-_Qj3J*-^Z06fsZb8s8}?P*Zh9Y`{@2ZaOs&Zzb>B6)V4g$-oBB#Eq2AYGngHr z)kE*xY0-_;Z0tu=s20tv9EhCbr$;oMIp1wL%d_1_`(m6PHS7A(+NNw*YY8wy`26^c zm^?HabOmZ@w{4eBnlKM@Z<|XJf^)vBGPRZwmj!>1|8fX)qPY%P}E17p6L(ANn zdm_*rpx&g_KnJKuGN`vc6rPi>OrBmZYVrP^rPpV{&)G?B+-wnLnT_Z@KKmUPqR4Ii zJ_YGaTN-EEoT%Dq(LD0{TlkXC6Hz__t>XFO#gO}aerv>Y_R|oso1Qr>#HV60ZMdT; z^*6*Ym;HWiZPKsOh+GZ-eg3v)0Z)!ofcM}&r#KQ0>B$0U|LUS;i1 zenAh+y)Kzv=@$t&*X(@pX>2a)vjpw7$@yBU#>v!qY$5+q!_JEvOSg1Kf>&Zo2LLbW zEgg7u=cDY>*PaCHV)PmB+Wd7L339*7xS>xY_eU-DhD$mgb+LM^MT4Ww-z*Xctl5jN ziCn0MIO%LsbI&c&WG;Iv983UXc8bUVdENiv6V`Idk(=JDqEIz)1AAISMMwF)67CcANkJ zjHK#YP1%>17wDe+{QN)vx&VNnmoC5y;QYKx0x&cLkhIx)>7oJzNpuCha2f~20ss<0 zfR~_`z?az)W@czriOor9JrWWU9v&WLWo1iC%fP_E%*@RC`ueV}uJ7N!@9phDo4DZS zPB>5avbcYtb+P&Ka=sSw(%4;de*UrmfVKuOhkggr#L}?G>ipe~B_?);b_D zpemtd#}R}N2*Sq?l7N1Kf{eR@j9-HO^k3p7ANnzV$#;I~>Ux2m&r8?Xx7>@Di~Pp1 z?w9VdE{NZYME(m^M@L6qULGYS<;KQFXJ@C8k&(E#I3U+e`|Htu9-z0kx2&v8-1$o) zbO=zK6?EMI0L;EWe=w;mn4|yz6+lr&QqwEv>@!*#xExn@#zl~FaLaS9Mf3&cYknbY zw+i-by-}xrT18ZGP7E^j?Gx0{|MQ}XYk20hxj0NZh*edR(OA{;>uc@IJFU66-Znlb z=YhMn)SE?*(`&Nh9KeU!%c^%ZUO_7ORFn7e z86n2uXUM+UeK6(Br$Hr(Ko|gk6tUZOWrGH^YCu}nI}q^QRz{f?54^LqRxM{Ywc~f_ zHCv7og3wiUwA_+qVYZvAD_%{Zr!}E(7Py!ZpNVx=X|sM_c9)`89xWLA!^)I~zY)lK za2c}XvRZ0e<-en&v(k-pQ-}a0>uVb@sRF!M@V7Aw#sb=stE;Ta>}?-vj{|3BIVeb0 zggL_(1&;lFJ?1Rqux)9^O&{zUjqIa<=vAxE-6WN{FTzn6)F6NNs8k2`ExqsTAhw0c zy^Kpx@Ll>{(cLQ@BtpOiU54z8&JGRfXs%0xTSn9==}ny|setJ3<(B$Efjs(OKqUJE zt|p2GgU^;%O7>Bv_iW(t#gOyR@&&}$xvM6T1;)!u-s*748h{tQNKBum)X)HLwQuH= z+v?^@Jwwx?ta_BKm|bF9?TP?qI!IJOpxgFUCXJz#cA1Zd@I$Dv@{Zn3b%Zo>7f=X= zkd&=U4#)ltUk*S2mW@C+Nm}T)`o4f8fGmWUy}%g76{9{a6Cswo|LwOURhZ||(^zI& zdH~?ZCL^dG7R-A0#i6R{%lGZR zHY5?Di&O}eP=|(Ma(WP=y_G1Xxy{NQL60_HC_3|D5gUDFQa#kzA@Qz4Kisv7UxR~P zYfym*u*sLh3a&hlj7E$8#kTcY*yz2B7+Eqc z5pm!fgiUu)vMyYAUpk#mIy0xec_kBoqrcGrS+FO`KnemFLWCcUZ9Lw>g-yU{3ah^O zu<8aEjVwu_&Re!TXRZF1bser%~TZulVQX!#T!Y(~uO zsp7YJNF?ZtKcU|&D^?(*nPdXmIOIOFmAMS3t4L)a=q9nDHZ6me?R`RfeF@*zt>Jdj zma4ytrzAs99B~uJK12SUH`JJ|ijR$dO|TXfqtij{$5s>f^lP;h(^M>dGK7CEb91dQ zH|31c8w3cW&bLrlj?P&(4rgG_)u#@g)$5M@YG4@Dx}JAENk&YYpzTo7tXsrY8}7Rv z)N1~g-&&{1ZWNx5P`5PoJ;F4kpWfzlUI37N^6hg2sy1TY1#bK;v)ZsJP=+3_9$C9} z2>%SXPPtUn{+{OHg%#9$Us`ANYk@*}rN^+!SkwW$_+R#CS$F6o3iwdxxl!#i^9Q%J z4Iqs}dPbD6w%Pr!;j5lRqvicl%gt-)3zI6AWGX%DAeKiYpfy6YDl1m2_-uL$2!w=k zdi4vzcLt=cyodWSNC_e@Dxt6NkSVCsXPcJ93%$xv?6jHAp_4iSq3fWjc4Kz#-F*63Vtwt%t*jv~9O5AEtWhP}L2thN8u8N>8#RD(b3B0O?Pp#-tLA%2NToa3yptPs zWVQZ)N$uw^+?aNhf)OqtKwF;cL!7&wiDycAnRgh1fCyku-L~^czADypy1a}S)Xy&h zpx3bOw5b|)-%KJNzP+AWl@U z)JN#RJ)v#9s;+oZUP-YjP*vc7lR$hXbnG}bd>UYD{l17HN{Z6?2#ybFh+|v}Q*4uc zl<~z`3JALOK!wAA|@^uSO@t!B?r=O zI(c0NMmKO<%+r^^#oDawa%gFgMT419{P_nF!yfd%U65$=oyhyS;ZJ9;yRvp`)B+jY z22%VFoXmnDh^PD}n{2GY} z(@Wm5fC9L{Jz3;j;6ty#&h4;=ypJhtcFoyQTwvCm2W7*CULsHT8tJ89r`$U&t6^Ti zA^w4SV`U(-c&_09ZTnl@vFEsNVFL5Ze;R~eS>N(J@GE@@o?Wv?lJN%;k){JbKVW>* z&cK*)s0PW9)s047>a0uzWCe1w;fC~IjXm@O8u?KkWCQY$|mkWSWw(szx7tUTR4oulnq(#@-` z-OG|3Ih$qq=&cQUYL8#7R9>Xke^RUZVPVHO+*fu&bCa0FtW2Xb?c-Ve#iQE;Pxf99 zw1qU3jw#mVgXpk2!d98)d@jIxXy}QB1GaKoqGyFxJC+hnu%P`@$()z<(W4?4NI|J$ zS*S?qk2q)i^<6BvdM9y}ZO+^=3j0=X^D!S65TcxrjkNGaaEiLdKryi8@r11w#&|utD{$uM%11pqmv;pp!xv4_^eIJV;{@r znx}W^Ymij!BS!;HpIbYM#rMPu>GIy>uz|gPeJxd(_@UqybZ|v|e*R;@x~9Zn8zTM5 z#Qo$LYpQHNh^J!J;m7-AD(kKHylT2{9O;let2c6aE|qZU)g3eavVCVGKkT;Ev>W#D z8I%g=3#IM)+pvS;eq||3fBwj+^*V{Kty92duBXNM^%*OOm9phZZVYX0;e(7py z0{lJtOKct+7%76p?#lX{B?ki8d z(RH~yk2rd1NZo~iK->7L_RILvbIeSjYlV0MOzI8Bs2+<$z_dGJO8uo=nz1Vl>AtIZ_;cmtVZfzN1~us0%;)*Hn!gVP1OvC(sBLV$^!Vn*KaS*i zk5T(bQ?EL^md8H74Q)8^QodYRYn8>IKZ(mE%139_<3mV$Y$ouV>C*z;1?QV)^W{ z^Rnwd4C-79r?2Z0kn<9n#vE)n-zR0uNkR%Wc&Lv{=`-lLw@CT>GJ(CqkyMy@2L7LA z+-c(_P}L%Xlv-_rS_zX}-yZYSO4~44xu^Qe`x=5NiK=&h849L@DHx3;=Mv=t_v=El zb=38cl+uU1(f}du;Vrhm#{k`R_e#e|9q-_Dd5>rl7aHffJEzazr`cJ^M!Om+h>y)v z%)@>0EYX~QvGa6z^%Hj<$f8B%q*Fx05U=vG#ydEoBzua48{*E065<|wNhQ&1!Y7ZF zKAAU4nAT8ijK;DFKTPPtPQ40CkHPRTihKaH7hOF!G#vPid#;tgEPGJ?`PXOmDYAeV zk3@GH;n1IaSh7-(9-+Mi$EtOX!;UTQDX$5?FtNZpb)~O^&0(XqRS~68JZt1Fy#&wK zo&-N?Fmzw(c+M5rJG34SM&}Zos7zI@^-beXJzh(d`of@eqn55+#~h?kRP|1iN5sW- zMf~<4c?8zAid3J6KdD!XC2emdyepmCQ+wnci7r;U?hI>~?=E*F+7tHGk?e@QDXY|R zCI6Hz7+%(g@ZTwVeEXFAqCM~mA^yZqXC`^Snz<^4PQuKM-mJD ztfW8?Xp_&N)q1IaPg5vPYUHug4u9QLN|u-`eo-A#P1i2oG#qy-J+cE_z+yC_SRq1IJ<4T5!d5mU|2-)K@!Q zu4A@B%iS{t%3aA99P+*nA95iAZx$LWdI>18&Q6pBqItydC};k)e)~r z_b)VzLh(!S8veOm&WLg5o&`TTr#uJ0)=CnRnu~;*_69BEqb+=_{6(fwtl?dJ>dg3z z9mz;jBDvsiRZ;SVGWX&B-JP6!<%xrz z;_;^az#8@ix8snF)%}<>>N_05sBe^H4=^m{EQ@Fxz2OOfst2^`18Dppp4>|&5u znQKti`q|#5##1R>$J_-%RNts62vsb^Tu6fJ+sIT^{7rVqt$J_zW`yVsTfqY_2}j<9 z7XTiO5r5%A(bv_n`DZ?(tXp2CpDtwHCPKLgGn!eUX5>V`sGu!EB@=w4=m?Tbbfk*h2?WAhZn~rVG9BjJW&0DP@ zy68#uvo z8EoHPqo=qCWMz&VY)o9b=zm2EL10g++1gP-VeNhGO>jIJ@ZaI)!p%2VO7dx6KV{~^`EaC% zl_P6!)291phUgRi$dLeLQAR|hv<{IR9Prl$se{K(#O)aZ{)|%xA_)bUclLsryzZrX zx7#))pAb59NElcFWn`Mi01u3qmukgt46RW=2*3e`Rggg%{NZ3@m=k_LlU0C}fZhgMV-xV5(QVjplRjkjgHA@BANhi(?=i|` z6({uYQ&)fEF*WF@X{|?HRAOjOs|}Bax7A#-CPejKdG47CB;ec$WIVw$1?gNWwLatN z&4tOg2dz30R)hdEa#jqbmf-sIrK}iTrYx8|SoSTw>cc^80&CMd;L z3C_ebV@EWQKNs*5>3uZIdCBAiQ&j->s)(X-YA6jm4ZW05s?s1=h^6%DnkEP zGq48y+I5}u@SP5CKFZn#+|$}ifB!5FN1^akYJ`BM;dc5_OL$hxU)3``4x?#h$tBwv z{Cc=(d@5y{_aJj@llf4fJ8B8=o|M%3_EZofnubrALFMO<$%@1L4TGZdUjq2-q_Dhx zj;o0D)@h{$6M~)mVFvVor+t!{rg#{aC2WSmEw>#y(Cnn@gRk_E7Ve}IyV`^lo!nB3 zeH72sMY9XBtSp5z`9@TC!4?4=;-pixYP}~uHdua|D4?Fg?Bg`;P%id$HrhvnpiNvc z+`w+1gOOq^xTn`S?io_XlbuOB$&n9LEM=rcU()BC;xpeMxZXD*^8 zLmaZ{j6Q^QugtkN#G_=kX^nIHTEdw~2cexAYFQO=EQTCGWz1Ws^v2TQHLMhb0`^|{ z_2dN?*B;aSeN3>1gM{Ox6L)Y#JPO~I)6xlNtXw^sj~30LK&C{md|!$?h;-GJtAz?g z9}SKQ8B2u(9Ygj$KcV8o{q12eQEDF>Vbsina50A@t{-aBg3e{tl!nFl4(fNoC>5zl3d(0ge;1+oWc!c z2ZU%bCmFjtY-rzfV86{(@`piA!_;EK)Nsnt?Ax3c+BaP&^#p`dp%9LWyw|}7UrRtV z`6X?Bd1<(F%*O)o8n_)i-s;lLqK!#(ZMd zAV6!Guno`mbroA~j37$oRWhVV-DIX&z@P(fYjGX~^ez-s3XfH4QgLtKgKw0{Gi^9V z_}HP&E_1qDCvl~Vv7{$Dneuof!burksuvARvo}vhR&sPaGNI_IfzK;1phMTrokAy5 zd^~R(wc9#ZX@JJRd*pfB>JwCxXxp7STdawt%&fY@OFcbG+QLWT9=H@wNr(h0bTCH!yHi_8R&*hyT#`!&C~20kb&3Y)#Ad$? z2XKA>!f+RzmL2aU&|^zD&811z+w>UaAs<{_l_4n>WH}7n`#qR=?y@SJ^R+SkKI{8VxnAgRFK&P<~+P5o7G(=QpJ<5LJ#)RY$Lb41&O zEPo`FIyXQ{J<1QpWH+s91?*4DeC7vqA=)@g>$r~9)Bq=mI>>Id{O87xBwtzu4*Fdc zG`yk)7qry7rv;tg(dGxVh9h)cO41DV9Ktm<_E`HJkzrBe8U#kcTMT(}m;J z@gQP^wuapsV;pdRP})JDmGtaK(rwMW7;A1NCfo>ulx`%!-=qyEYDAHm9g!xdm5C6Z zhoz~iNR!6gfnfRPX$-PCI$|-q#W{fhfJmrD=hO$$kh^>j>%reFgfn0zaWRif!mc`@ zx0pPx)o;h9K3=VQRzT0WW^dJr7kahtb(3tmEv6PU_}nk?2z`*#9FHmdBrg=DH0U_U z1r@hbLk{)%*}r%aU;h~8>kg3%cEZiVvb!WqE^cIrTsqQ+16`&rwM!`9GaVA#G8Ql; zwfY=J-tQ&nVbvh(|IC-50s(IEY=u73;9wy4)NZ9N1S+g&rXi*b-#@{(zImGL@Tbz= zsP_!R;{$JzXq_VgtQ9W}Lr0^2?Cq&tp2kYpA>`m;RResZTOFgrw ziY-e-og&*NF^At|D}HG?qL5}A)n<8^GLv9j-B+T5vEP? z0GB3Gm@%8Jv3iM#vI2b@N8n|~l&}OknjapY*B(zKp3?$P@VBtdWCJor45&Z7 z8xi>~Sl-#)6bYUMua=Y|m&yzF5}|4*RFW-LHKhnP1%7Q80zJGl5y2IHEn9&h=1*g(`vF~!a6daB~(X)qc&&888^ki9i@@Y8Dzrx2E zr4Q02-NXoT!DCc>veagI4T3ZBbPiIpW!DUsb`oR`{EjhFiK_x$t|FD>I8YY^MMers&SB;0K z$}HL}iLM$BYEzXl45ehC?w#-_yLtL#7Q@a8F6V1ZDp0;cR>Vlzy0y{W(R44J0cs33 z>)SC!40tsBC^~WVk1_A}t{eN0|N3A^d(<`cdkOuwoAK)`W75KRZ$6+wH*AipLNYLldV5?$Q=VUALId!=e{K5UKVKoVX_&BmwO)G?0F5C(^MsoGD26v|RK& zF2)qtx1uV91Yo*-h?7_{l(a~Y35Q@vo(&bcf0-91K@R2r1_X~)lO+#rdrC&o&7B;FiDXn&6(6ym&Y3bY9RTg^oxg97kR6_{op_`i4E zD}>$S|5U_Is2cpIi(}ZeZt!z?2!G28)WuBeW^z7XMgO%QNIF=n)$(~$@}DidI^B{b zl-z&fm~Z)`HGpOx&cBv70612itt3YWv}$o2L zB&y#JH*Ywf*ydo8QsH2OQ;ges^r5ap%Y#Y9{cgU)3`~O$M^*q&TxY>?;L6Ai6$&=1 zN|aje!B!p-`n|_&fX0Mr!=kn*JPSECt9TPC-5!=AOlcFsFk5D>S zEN0b7j$PduB^i$q6u1(Y2fKXjT^V7oaz2dWup<84=>*dZOL`D6@j$ zuOvH-?3HQL7Zd56>+Xn1-r!L`$VPENH5G^%2@cg3f#xDEzzwc`1}R-ticU3h1BvD` zJirajVeSE8?M4IRJv=XJ-$DD+o><1^HEC7vBcOL+V1<;z6+!*t7qp^3AEvtL(IK3C z9&Z*51(gC4w3EHNoU{kqvpJC;UxjN1e}(EwTPqhT8HsSUuoc0^7CkfceqA|MLj(#~ z(8pzbBjke`^AJH346^oX3WDA@RWmkTskeL2dcjz*-6MUW>$u!pZ^kvX#pH^~R}r8! zCTwo~O|mFinG!M&uCvoGGa}^qMOCrmj|7~Gb|ak7F{uDTOwAi@S*D}`Zchm+g?w7!7qd=)f;vxuCfYDXE4wwR1n%B{ah*#}tGGb-bTAHE;6uQ&D9GrszL>-@{CV z-Pl}pD((L>gBXOk*r2W4q9SWpY`dbPQBd7dK^rvi10tuw=D|a=t3V8_W?0HUc~-PP zYk7axCL`pBQ2n5>|7$4{3K)+IcPdckg@acV0RS6p@5Lw5hYG-i4)=b9IEGb_9t;F} zHNr}^`h(PnbO5K&i)vzbYoUZWiyA}=ZR-#^)(8uV6A3JUchF;!ylu`1)H;CjKkIyt zS)aFRP!v~@x<-SI_cr4N9yiWmP_3ND0{QfH3Mvv|ytuHztdC z$D~01sOlr9#YOOhe!)K=Q^5xoY$SK4GmTz(6hqMqZqD_T4N*Vl~0W|dM1qT_`XEQ+bjcuo>U1AFS|U=W!zJcMU)WevO%cXbVx| z!{nEwU?F&b4AR;k!1nK=05sq51s@Of&$}tQ&(iFq4;{8-$i90VyCbUY+u)S#I1BLa z+(>^~gV01;%Q9mLhUk35{e3ntsXY3dxMs1|HZqHV(TgpLmiNw9w4=e}LuZswV^KzRuFgRREcJM32HiuZ;ST5xcfBxX{ zsC9qUI%E^0zpXjp{t>kM8bqHyWa7{FJ~7+4pT&i#qPmd4JD`iJ@9F*aMDX^8)v4)$ zcmh|fdBAtBb!UYNS5W4Q8-3%KiS#b}ynro~1L zwx(y7)1Mn;{`nY0tK8czA_X}f`uWqWdxpz0npFl@2O?Nci>C$|7@~Jw)WNG&p!o2k zu=04QaGM{H^fVy$-4)J74jy$gAAV7~o=5pu2@ zol9uue?t0Y(c@u zd%cewv${`LqR#&N?N7cZR(Q=6g_vb0x5y*VIr9hQdssiofRlf)?Ko@Nv50IQI^3~n z?vik|LOtkz&kG&zUAbzdTuEbQ%}r7BGZ6E%K!fsuUDfLzO zB?7?O^U{$O`k$a2F^0nruY5^JgrgpO)tBXt>WJP50}hsgZpEgAp-;zHJ5c_!@M-d`3Z=ZBwh)fON}i2boNXU*oFBTf`g!UqK|y#x&P zb?WUuuJW7=&Pi7td)u92kzR3K6&Ui3tvvDOsiTSFT3tT9Rqh+`h~5)<&nf(AMbn|d z>G9ZB)QbQ-%m|X&%77EHUpVOe9-)I^g%gxM?S6BbqU<29{RGB&+AgSVhf9v~FIxX* z_0540g&MTY+SqCN&>zv%2RX)g1ui(g>kM5~g&&2&Lk3dmy@NY92S)qx`$h&&I)JXC zj8v2PFqgzQi;1SwJ(@bF+0kG2Ypl4_pG75!<19%P?+u6|*O65ftvApo4ii}<8w_(`) z(d`H*m}Kb3Y0N-%#%$ZYtcn!fbEnPd3I|1ihsE7$yM&Iqj9h zTrWL56{$0d;nq>p0U|0|3-nEjz+J}-&cksYi>uXz$P$LZFEnf$C1bsE-N`N-w1dYh zM&|6y;|n%wY1&-&_I-iziJN1{%)0i`<-3i&jcsazen-Q>U)Wu-SsQ6qSxrx}QY!yp zj-R6w&3;>*=iYWraXD`?=6-XH^^=M&r|X5Zslm^?%e4$cn!eG1XxU^=dAVHeoBaFf zasvgtY%%|LN$u{e?;#D~`LASL@4#}sPWp4cg6wf$6=xgu_UZ}cyRWnz|30AK#wUk2 zR7Wnk@@0uzz0sVod_Vf>bBVo?pQ|CYA0BIM%PNmopHB2&Tso729aO*hRj&JkUEI{=I|Mmk{?(6|ethfsb^_-tDgt!^b*spP5{}T6(eSCYjes6|L|5 z<-AOIu7_}9653C3sa2TNpBAN58rey|YngxzmNnbW7uH~Wt!J>3VeC-Q&>tKG1I z0OgFM{);m{%9Kg5!4PjCfnuy+-4;ac=Kh{z;>VzXRaU0u9$P;Jk5)_0JTgGwPvrSZ z&6Q6@##}40{g8m0K6%~?BJEbc#jEZnujA-q4(r7Dmxm^#21IKVS0R)YC5h zf)i+1-qDvTbuir}TE~UG2;fE`q|~LC(%xBJ)J3s8kB@@nwKljj>M;fqPm!)aHp(`z@MOgm`)WajRn)v$&eIgP9J zqXJz$8=O%z0`7|U!t6_$9}jwQIt&$MAew+t11Fl@X+$nYs8PVYeD|_k3aW{e4>3R_ zn#AiqB-~<{z5e_tUBbs5AAH0xE%5lROh|cb7>}A4&TbC9sa0RX=L4t;kxRw*43 zAK6YQy_iqAn#3b+c+ArktxWc!u}LoMZ^;l%ZRC!8hnDKr+w;ML62}6kdt^U;<1FMZ zIuEhWpj|GRf(%mMv>V@*q%$d1Kfb8%mIs(9|DyTXU?22|e?#;iTG-dU1|$7{!@_pV z;+@yr7EhlqJWzOuqWXzTUbm5_i|=(lC@j`BwqACw^iYJlb#4uy@|d)Y;l)Nlxo)m! z+lM8UE0t0x;N;ao9HHZr*(2ft0@YqVhtjDTY~r0m^E`g7I*n*4_g2I_EADsfzW{}$$og7Twh#RZ1}zExz)bLSsk zc~w>S1-Ig$_k&^_P*D5HRG*;JnQ(d(|I_1OGvnxXUATMb(UxEIlf<^8QeCowli$Od z>XKWh3D=Jn-;c(knJUO>)4r%NYcf$LYn#9~MyA3EZP`gQYP%b#F0afjN&3{_z*k}NY0AKU zC28G<omHiE6uHQyy;*PPDy2ybt@s%<&$mO8a3E8)$=9=8pE;8 zF!2Wkbp~bU629`m)p;SpCr*a=tE!hM=CFK`G1y+KmgXEZuz7(4BPF^2?D(~AjDgc@ zI|NkV0y`27RIBhfxK8x|=am}whSu@z&{uH0fsQs8a1`xJ?bUHTUwfp-e2j6D_| zdX231wLY16>Qy1j(IYH-RyaLSBIfd>IP$>Wgs%$hH8|0@VTcgh2>`JAvqZFu0kY_rt$B+hw=cFKXX_pA{Qprzv)c*M}%_ z8_f5Dl(lo5G@jdWUX&Q2gddc!jW);l@nW?;Cx^mG*sYD$Tm#Z%H?-eO@}TU}_JK92F@ zS^N7Z`{LCDT%iY+aW$Wo!^87T^$JpZfmZh-BF!mQ);ru7SoLq;PMt7GC3#mGNdKj8 z{}-LC&g~y&=Tvu;)NlxYp81j=cH)@H7EpZr^<)G7_Zxe9=GUZm^iNM`u6W1z{M*Z8 zKd#Tt>FxfE!1gJwI$=lWQBB5uI{B++^Lv0!V0ee&F|O;><#`NP^VZPID5=9?N#4~Z zOXP$~ltXE+Tq!orxw~+6`i23Xgb&T^F_QUA*@8*j(UEiJ1G2v2Vpew1y|!{s_|uuGOSVh2yC|oqT>geEPAO znC@B~+Ylx$CUEBrurZBZNj~7to3QzGHc7S&7ebJF{?)v&MZU9EvYr0AF&25IcSRfR z%}>9qEsn|$`Tad_G`H+S!xn)Z{KS{7TkyYuY8&T#3)5Tb6z&BcWWNL+$}Ic!lsQY- z%XmocR@MTfAltNr*LP=3m&&BD9Tj5q9b6g?hADO|sqSCUyitA- z(gRB!SM+pCERJxuwbU!o?uvDRvs7q`W^FP>&6?7WG^t=UR;a+gxcq8P@X|PKXvTIC zn}HppCToo*WAC)EQd~ z$_ueXS4(kUA;IbT#>>OU9D4@V33};h3k5eh_*xK%7Ai&QDDDBjKF3i3Q>e;VPS*7f zJT{Br+ob04u%@@CLCrQCO3qWyqxKlU@o>$Vq{MZ0r zR~KU>rM28F3P#fVp;Q5WSvX&2-=)v#sj%K=- z!~Jizw-@P?kMmMb%X{@OyHQHeiBzWed3w^sSKXWs(U^$yd5wL>f$Lsuojaea^6g&D z@a7cp71tNfpQ5*LrvZ<|Hu?2tx_Vm1A?&A%FA>8ga-S@0Kk?u|@it(Lv%?*n6~~7S z9I~C2J{hv|Q;6ZR-crM$URxEBS77up)w>q^62tE^h_YQa&Hj8L?@+a+PmPWz$xf(l zvK@TLw8s>c^NqcQF?OT7>1CT5AA?(q>Ym=c$(&EZ7mstLsla{WF!|!nx!|vGk{AhG zEES6F#z2n?l@G9K#KLq}ou@yIOm>6Ag^D=tglKnR!UZ*o*KZ<~);CG?MK>2gFLYqu z9!nRdYt1FLT$%T#ohj>GdVW&V3Dai>Q&m@d6HlevJnL`9pd@hSX~^FBr(uTG==wa% zryvokXNP`Y6r$frW4Z1Vl6B8noingFSxny#fRE&5?0EOmDaI^5P|^2T-wQhvc6nZC zlP%Ag;ogn}oe4_PJb;$bArB(}Om)y+xR<^R0a6CoFa8>hOB}vL^|{BkzYU zQhVodSnCDy&;Bfb7^%uqVN_A;C~ed9hiX==zktw8ue=x<(7(nh0O%p8K_TfK8h=B=)5eR_l~+C6)o568b`;u zKae#h-k53WSIiVk#l~Ba2tLzIYab{C`Sl#@7fjE_tfAn{{imxAoos>pX0-OwD7ZDb z3cJxl={@4E*_^i`U27eiOKVOhOF$1S3~}7pauDBOif+3>H>~GnSOk_ceKXhm41)4e z@^=_NOkU~6r9Eno?X2Tctnwf=)GnIVOjRL4lN~<`BfiBa2CA=?W6ScO^H9Y{7zdlT zRc=uln>1^LO(?{R7z2vxgvZ&=ak!*P4bK*d<6Ij{ka6xKfepW#pmf2K_)(md)I+9Cw|d}KNcG5L+>t=8#HXnkus%&tQrO=yrTuAUJQB1I`uk(9J)4k?_Z1iw2fe zNT9g#lteEatf&7Jdgq(7x^@vis=paPI8m{mg_zFbb{P-mePw@Biz1UdB^<|OJy*GP z)8TS}zZv@cewFtmYx%bWUS0M73e) zrHQ^9P_IB>(6hUGlZO2xm0g;x@{|FTPzBT^oqpaMqk`rtn`<1XbzYwqjaDqNHB#?S zTRc`GAnykB4)S~#AGpgy=_Q$V$u_2rkfr`Q!unb>YE=tDMu#Iv`9Ge;kf$$ zv~lL~P=DP6AB^m4jh)6Y!4$W~0q3>jNw7Yfm zp|n_AEQL^ZzpGx)^L?J*>-lr;cFw(@b3dOu_q@-sUbQvmxc(N`U_@QAi|Ua7qM?Gc z@{N;q7@)lQ-$-$Tx9EA)T~7K#ME_;(V%P})2*jpyP1IEpBl_>YlaY1LDMC}>SOe{r z2E7m8yku-tp|0!i)!76tM+i@$=k)@fhcc{YUNxT9JkS9+gvf~g*bLm88~3JLZ$K-| zNg=*b1ObHNixE&41?pn(30>Ove25{wcQT&lh2m3)H$5G;dYs?0lTtYT#Sse-i$p*F z7%|l{W{|Kq8SrYeBL4+F@FA+?0GxBYHd}|pTphN{Z`}0O$k95qxATCArosW5kC0Nq zS#|;%7%9z9s$b?VF%OB?S1j-ktE-S%{sqCh&XL@jO>n?DY7NeN##J z66f!sSsX3-pRBU;mVNod5evCU=K9ZChnQVr|6NaZFyl8*12i%#X7&$7{D&7_{b_uz zQJX=3$0dCxrBoZl>q+AL!zGfC`Q6V;!tGLCG>otA`-I936d;HsL+Zn4+_@KwKLoI< zNcX-xABNv=@DEXZ-&tU!21^(z#Ha_&U4r-*ha{SOb%67H38_)AtTedChDfUL*xP+r zhOO>*5$iTd&e5u^Y7E=*<|Y&6{u?k(YzXIwGBh>HE57J!DST8|vEDDPT3uMf8=&* z$;zABjVNlpGp(6X0ys~->B!_x@)6PTDMoOwB>SGm@&GGapmtMXye_U~OZ$R_LC5@M1*8umdk8cTVI9_j#bvn&4 zqI^WANr=WD#{Kw>`q@Xer_Sx%8RMWaCa^B3d8h@$BcA(IROfR2WhbAEz;!j;zEN)e z%3DD0D=bo{*r=ZGiRvAi!0BT_Vk4jAxFmI_vO^iJCXv*We~_Q)V>n6+oC9ZiAMG4@ zW+$>DiaHY1-f37C@nnWpZFSM%)YOAeK;urq}kz>6*zC( ztb>qs`8(=n1(u1%Xg0u+I&Z%A2S~ooSLSYm@pV2}@U|t)!AXl= zDJyczbpsvc;P6&%$??^W-Yg-t#{OYzf5!h~in}j2Jz^hm^y%?plf+KY^#xMjugP8E z@e1krdarPa_amh=nN)CJ^S$dZn-8;HT;aG_lZ5}6l6&p|uEO%k1MSXffLTVi)5^DpTw@$I9gdAdmc+I@`8m_o7#IvYc8B&I%lpwXfUAa&x z5%HEF!XmL@q~s#)z`Nl8*|{3@{WRlNx{O;u7%%q@>DuWnSd1Gc#WCnwI$l!i)VgZ!TM_Hsuq=UXiA@5fP$8sE_gBPdem2;l z@%$Nxz}Z#<{>nq=$E%68PrXyk;Y89MzxIi+1+Nrc`7&fhl60M|hA~~BHM+9Tvt4-j z7r=$tG@lQ$bVwb}Ks1z+LyGM+H#U}{sYo97j=?2A1*r2P^zdZ5o%1`veazTbfVFL4 z>2dNcfcW~%w2W@KP)mn+zP)2{TswQ~g1T9|NQJlO`sc_`#<5L|m8fxM?v|D_?jgiy zrMR}&o^y$EZS`u+i|YPgVF5>H4lk6F=#{yp%8xHd#sWX;!6u89$UW`H5#*=P*1u{i zf?wD2A#gjX`3^xV1j@BytCyKaXO--MXb->KwIt^Ujrx)JyN{S!fIrR`Dp+EexCVmc zp7kR1{akmXtYZ;9OR>R&zD!CUs9j4>HcJf6!@o^8jMdg{(eH`s2Iq4a(2u+r;(WZJ&&)w?W9MS3%|%93zH~d zZC#9$epT{{n10_9j2C_yRyUc8o$wGk+F=(wVSh#;_ZskX1mlh_+J}4uIR(HGUCqHn z8KD`5KMMn+vZs=VJuxaKWQ|;QOczU-HRdG)7HC1n>kH3H>zZYIqo{MyY^naXy4U$Y zdsYI?M>Ty)bE+D@2J3&`I&`8rQ0&$XAVavg{Zw#P)0>Tt&a{lVMyKlMv84hG6@I{N za#CGEZdCs(cilomWWGU-wIWSqy)ld_sBOPeO5K3&9bdcgcXemh&5qm6{iNq|fJBb* zpr4}-GSC>~a}sQ|UmDJkzYX<$hSD`S1bW*6b;=BsfkfM)u2dy*>(}CF22|_RJaL6d zaBkAhEjF2WmKkR*o?L1G$I?5!ztn#(1+j44017r3;$48SZu#(ioy>)`6D@N(^DeobSn`+2&MI)NpGv{tmuqHSX$k#l42@ETPlqU{3t zH)nje=*}8&7QKhS1u|`sEi1kf;u$$6PykM~jUup!+>e&l*z$8KCCASz-WR+$!bfCOeRyCyT1?L+0b)Nj}DkQQd}e zc(D<;UHww%&o`-hyWh z73;d?eiCzbCn^konGw9?eOb+B^Jka$so@SBeY+@KV4}K;5&W65ijci`RiS0lEey|m z(r*ZXbDSywhgT>^$7eXYv3~1iox%Ps&|?6uAu$Wr-@XB(2NZ{m&;wN$LF7C$po|04 z0c9+^_e(yWuls-l=0i&MoF^QIogg6xYZ^g7$B*0ND{$iao7ADf0w{g^Ct{y84&zAG z4_&OT%6_pIJd_7T_+;rg?FNk(Y^c2Yv4muxHG*f7KOv`NsBU;%ckW(knj4G-h^r5W zYtFcu`gF8yasI{6Is&VJe$cph=Qt!Q2i8MZ3sU)d9E&5XTswB7{#vBPMvFaRIFw0b z*-APJw>Rm_!waSoa$|Ll=G`oFAdV!JEMBni=3tw;zz-j38l{Qyx1N_1AG_$#6n!l{ zgDJ>uIK6(eRB?6pVnZVH)rGO#%OTvU8-`ra`Sz^OGQD}jB>^dchuX2~$Mau!gjNmp zNi!r&a_zk)f1kf+-=snERIS$}9f?r6n2}};SK=+}=ihwHLt3+7zADt|xn9y9p$+2R zygEK{(4l!^7mnN;x~>^6hqFM}k!kOKVZR>JrrJd+ZEC9-PHs1h%24#)u8UDd)%>KO z`#9^bTsg?=;za+rCoDD->nmZdZI=6vbYpJlyua8=$>Ez>E+gB`FJ;&G?SniicZckb zSjdI$2f6`~=TvX|Nx>>i^39Z&eY2F^6iem|PbNY4k!d9nH>0#OlbgTF10L8{5$grHf?+szMhPvHHCb9le}c#o%x`xT%B~;gS=(f z+-DrdDZf5V_j3aDkGD)svcO^w7e_e_xJ}SOB>8@?@ca(~*cqYtvpjbN#1CS0N5N~t-?(P{8Fj|B~x}rY% z4i++zKHYJd)N-1atlc%Yq*1O^SzjxaAf<4#rn1-#GL;ZcM&s%GmjwkyBs-1i_m>7J zqf#6r-6(bi2cP%Qd9SU-t70i#2BaMHV&Rzjq8(WY4$$@s9P{yei&wMkHmx$*tk)!8 zWeooVHgjXsmKGO%3CO40C&6>u6dF`7^Ty*RiUkj$14aX@KcSe1w=MB^06x}k&79!V` z_AyuKxQeraBq+0m$gVp=o>zebBv^+Uj$xkX%4g34>;4EZ&Q~TFN$c8FgyQMo>ufwW ze4?(foKR3zPH4%wg;vQZIl$9(#gEZedsvo;U9Q+oWN|sO(>ZrStCd={tj95*I1I1l z77NAO7FKrZ7|MxzPki-d#fs{~t}_Eo9emOg4yvkr(Fy+m^bBACj=nr*qm=s)IuXNg z8<=K5GQQ_(lb6;xr-cDoURGelJp6Kn@|kTmFp=;P8WJ=qewc@KoX!~5rnK$@xH48$ zlQ#am4A2}H*pU~oGJlW3_T!_5VHjcnus|~kp3Q&qly6BKXtBSEUK}c`X|>73TTJZ` zi@?|Ed>{#S!3mCrBY}A*BXMSy2dtSB^Az$MXSSXr4n_4Kad%T?3Ob7=AbPJzD*Im! zaOd{va~qIWq^i z?IQcIJ@IFquDBrdHx|g_A<)cEZj!or6($=8eZyhdJU>k>J%|`$>{n(Z+mj6EkCfc0 zyJ~0|2#Vf=nIyPZPSG=mqjQu24z2Km z5#eh^&2Y+@BLsgPWRM}(;MdwO(Zf6w1bOerko*lT#0qrDk8;sDmS9ryo{sS)8a)lt zxDqI{H8VSl1I5^KvMqGr)Vds4m2h@UkQLFA_+>v&LL?rA6>xi|bjaDgdI%Hg`}xI_0}e-~(t#mWP+OA6o_ zUgs}6=EzjecpQ+|Y=SiQ!+*1vGcNA`Xu{ud<<4hs76qf4kP3Q6zm%T_oftqT^}liE zdd{Y^Kzt?8D}07uVl-=s@31Ru9R$dWZF|$kkaP8vw9;Lmi6$^5j@vc(2f7$LALbH9 zd+X zbdA4XigC1Z#7g9@LG3SZBebl0;y>QlG<2-WI-fOqaci1u@90NvcVy3K8DVM#i@4S^8ZI9$Tg=eH&XMG_d^O|WX)aa9J7P7wm4;($)2#ak0aUo~iI<eg#yf@~`St~=fn zBvbhLtFp^a2@(pQ@nm+Cyn8)~|0e^4v9x?e;?(kGIx#LhOIuY>yUu1S>f+!D{!x@ck1}kob1| zO0#1ZNG64GtwnTIh>sN`mB9HNQ;R6_PM?)ZeRI=k{DzrVCfZx5sd)V50FQS|CLttL z4`UvSptFcovMeG!tz*F{Zhst+20>n<2cSGj zu*!~t{Kja^J}1rl3vkjb(YLd!Vjk>icgiJhD4rYUzfuRt@XBs?8-&Sg!-ditWp4r& z0M5NgU5+l>_%)~}=WUU!iAngvB9_Fuh}@O)-3``Lc>FLs8gRw*_eUL5yV*;c(or{t z_#0y|j`koqK&M_|* z^64K0IqQV>pHE6Ao~W`mUO^J9W7o>PY8~Hwi^rBB$lvPBnhk%(X56=0j&|OQRXo4GF-5`e=F+r_wwwEGla-fSryY#GETr|h3AWCm{Ntg{|22!#6Uc URV;W@2lvYjP$uUqbn&GB1EfuG00000 literal 23236 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 diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index 85b48c402..984781ab5 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":16650,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":16871,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":16803,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":15837,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":15693,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":15693,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":15028,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":15609,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":16651,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":15543,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":15832,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":15465,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":15908,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":16427,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":15835,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":16280,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":15986,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":17020,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":17020,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":16353,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":16208,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":15468,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":16277,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":15838,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":15913,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":15905,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":17236,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":16210,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":16057,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":17089,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":16281,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":15692,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":15908,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":16951,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":16650,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":16578,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":17020,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":15396,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":16356,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":16281,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":15983,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":16654,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":16649,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":16356,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":16280,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":16947,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":16504,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":16212,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":16504,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":16576,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":15470,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":15397,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":16135,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":15910,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":15984,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":15762,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":16579,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":16280,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":16135,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":15540,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":15839,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":16578,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":16056,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":16058,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":16058,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":15914,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":16722,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":16057,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":16350,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":17533,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":15763,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":16058,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":15692,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":17092,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":15910,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":15028,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":16059,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":16947,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":16280,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":15398,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":15616,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":16204,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":16574,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":15985,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":15911,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":15396,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":16504,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":15832,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":15693,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":16571,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":15694,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":16354,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":16649,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":16207,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":16798,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":16574,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":15396,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":15468,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":16502,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":15762,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":14959,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":15075,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":15041,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":14545,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":14469,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":14469,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":14128,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":14428,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":14961,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":14392,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":14541,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":14354,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":14579,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":14849,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":14543,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":14772,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":14621,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":15152,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":15149,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":14808,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":14736,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":14353,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":14768,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":14544,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":14583,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":14577,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":15260,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":14735,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":14657,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":15182,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":14773,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":14469,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":14579,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":15116,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":14960,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":14924,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":15152,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":14318,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":14808,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":14772,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":14620,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":14964,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":14960,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":14811,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":14772,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":15112,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":14886,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":14736,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":14886,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":14922,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":14356,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":14317,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":14698,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":14579,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":14621,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":14505,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":14923,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":14771,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":14695,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":14393,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":14545,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":14924,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":14654,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":14657,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":14656,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":14584,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":14996,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":14657,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":14803,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":15414,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":14505,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":14656,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":14469,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":15187,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":14579,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":14128,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":14658,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":15112,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":14772,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":14318,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":14430,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":14733,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":14923,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":14618,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":14582,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":14318,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":14886,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":14541,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":14469,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":14921,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":14470,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":14809,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":14960,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":14733,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":15035,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":14922,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":14318,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":14353,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":14884,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":14505,"get_count":4,"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 index 25631c5f463e7b48878286ff7be0514b56948d94..fd1d2baccda7f910adb7c65b8cb56065ff6d5668 100644 GIT binary patch literal 18077 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 literal 17700 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 diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 8dacfbb21..91a8ebcec 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":266405,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":257750,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":246442,"get_count":80,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":258645,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":269806,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":277809,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":253602,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":244032,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":261152,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":267164,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":247450,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":260030,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":251356,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":249430,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":266077,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":262109,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":272145,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":265082,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":255770,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":268920,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":250101,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":282665,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":251984,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":260574,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":260074,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":239497,"get_count":78,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":264938,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":266585,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":274356,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":255936,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":277797,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":266896,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":261984,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":256362,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":248936,"get_count":80,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":266790,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":245329,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":268970,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":264186,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":253289,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":258005,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":243361,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":249055,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":251802,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":264088,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":258035,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":241486,"get_count":79,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":254276,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":246910,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":261852,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":269895,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":258666,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":259337,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":263451,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":257529,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":260663,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":255512,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":278511,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":253631,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":269607,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":258014,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":251673,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":278146,"get_count":93,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":260445,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":261782,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":262610,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":272337,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":270248,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":261320,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":220747,"get_count":74,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":237874,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":251711,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":247361,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":278684,"get_count":92,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":246296,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":246214,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":253002,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":252822,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":259824,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":267162,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":259539,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":272772,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":255008,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":281462,"get_count":93,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":255746,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":268703,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":264048,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":245071,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":257449,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":245974,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":250998,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":259655,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":256654,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":272883,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":262928,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":257661,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":273048,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":266666,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":254289,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":271913,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":144643,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":140207,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":134295,"get_count":80,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":140720,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":146426,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":150587,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":138080,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":133102,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":141964,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":145117,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":134837,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":141358,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":136866,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":135863,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":144612,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":142398,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":147675,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":143998,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":139148,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":145959,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":136244,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":153101,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":137209,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":141682,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":141423,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":130679,"get_count":78,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":143941,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":144765,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":148809,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":139227,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":150582,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":144914,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":142435,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":139456,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":135586,"get_count":80,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":144920,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":133780,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":145967,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":143537,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":137860,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":140322,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":132747,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":135717,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":137142,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":143480,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":140360,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":131733,"get_count":79,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":138412,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":134600,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":142344,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":146504,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":140664,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":140964,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":143207,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":140089,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":141773,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":139081,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":150936,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":138088,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":146393,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":140315,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":137042,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":150801,"get_count":93,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":141625,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":142273,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":142659,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":147746,"get_count":89,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":146665,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":142109,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":121008,"get_count":74,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":129956,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":137093,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":134835,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":151063,"get_count":92,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":134319,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":134182,"get_count":81,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":137750,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":137632,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":141254,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":145112,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":141191,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":148027,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":138725,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":152527,"get_count":93,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":139126,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":145861,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":143454,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":133630,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":139991,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":134091,"get_count":83,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":136741,"get_count":85,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":141201,"get_count":86,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":139604,"get_count":84,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":148025,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":142932,"get_count":88,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":140170,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":148164,"get_count":91,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":144790,"get_count":87,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":138350,"get_count":82,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":147557,"get_count":90,"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 index 3c6bcb3aa0ae001ea3d63a6e20ced2d27abf1f82..d1145d77ef149b7d32bc2500c754056ea17bcf93 100644 GIT binary patch literal 19729 zcmeFZcT|&Kw=ftqNT^Z-l-_FqMS2Gnq=}S-fPew%y(1kXML-b21nDA8LcmCsPC!LE zhy>}Pgx;iAnTOx|-np~Zoo~LGKW2Svee1G99-e*LKKtym&)JRm`}cHcso1DMAP}wY z-PXi=HV`Q4tPOMqT3>I|0Db)m z(sY?UYvTb$X|zS1T}MVGfTM78X7t}Iz~oD zW@KbkRaLdMwe|M)l1Lr<$+TEhGF^|MhBFdE1n_>SaD zgsuUc{4LJ zpjG<%%AmP4KCu5?BF zTHN7x1+9Z48r%hqVNHR(eT$m^|0RW+Q(LEHiDD(>V0FENwV`+oerk)^a^7+rC>D}fMwFHOMh z@|wqw<`Ba2 z^on3_14hjHdx?|JI#RyI`djOkk!Q3z+x&p;cekgBE7)o6Du)#O9`e>}B)VMh5F0J@ z$ijLJ2aLoBO!ZBH7GzUN2<4zeRapFSWZ6tj*FV!V5QV<1yx?);-LgmTo+SiRp!Z`~ z#+OusDW}Ga^<0^%){AY*x~z+qXw(C>4PT>%bDntkd|pOLAa*HrD9c95*?(h620P9IeGpq_BtK_e*JvhIltR8K`32>G{wn8OKY_RGv#7QW5Ta>L z3T7u-;n~)PLS8L)HC7afqeg0aSKf|vN^b1NSqh{X=YoC;8t5Th;G}WaXWJ6CC5qd1 z(S3WBYQ#k|r$-Iuk>1un^{P~Y0yx$hWxtEU=8L3{hFb-*ponv!+d;TDAr#56w9*md)Q2t@PzVzo$|=l#BSoWsucfvX+{p>DLUqq2b#aq~2& z5t_yfy1ow&?@crA9nal)xxXOGy*kKsWJ`8(>GZ4S0)2b;E34`Ts5}THj-$quuA-}* zFSe=v5U~|KqWLfTXfitxhIYIg%O=RNf>U6zA_MXEMwL~>c^l)_kIHt+u+C`t;q;w& zlD9=CN*u&<8-od!*EhoOAtZ~-uu!J7062!a0+1zgj5X3-VgcnhC@;N0N|c28^`2e= zY03N0e#h)G*kavUWG@itu0ba1+)g%7J1gj&yM=my4+x%mT4#fd7~T@h+0MCQr1-kYhMf=P8oPi^+l8)F;%a`DwNo6 z+s0HcHD!dCbZGo$Ei)726vV9Hpe$DLo9)-XD1UXbPZMvg>pB>NCgHv>+opT>P`$jP zYKGlvVkr+pJS?nqBQQ5Cd1fep%gk&-i!)`Hn?zrJw)QW>n$0&DJP6H$EGcxS1$w1j z?wY@8@MWMF%X}w5NzhafI-&InUd255h59)ZtY7*mP7$}!l<~0D#H1CQV6E`k$|?_X zHM8UYY-*EY2rFzz16?DCY47GU6I@x`T*Zv^*p0uY;aH=GQt#&64(KtX)8-;G8gDjK z6UZ94e)!5gx%A5#(i)9FlwrXID)tsrHcFP2&A)bS;s_dDoE9P8#-|qRr;RClHNs?a+7tF*56stBl3mGHr~}$fRpwaK z__M4If#C$R9Dxrk9}{0;Q=OZue9_aN$Q-uLPo#EGrrrA4nkvGCh`g>o%{A&2?{hY>AF# z55}7@o3&$$#N{o%)kZsnn#^_vPEN;<+?}%b+WQ(CxrMx}Kiu zwV^j7_c2(UKtP^z$AtsyUlhQbv=ll=|6{oRqO-r(G-D=XnuN5Ahf)OwR$*vd#?dM$ zGfoVco|E8hGxE^{jYXi-@)i4Zbx`QqQ+#R#RR-&w;mbYlP}WoYQGeDB(5s*)t43A_ zm5$PW6Wi?R3GL|C=xOMsx}NpE3-88r(}Uj{TYm1*zv4OX^)Xxhc;?Z^2Z0;R8l>yj zzYe?`Th_0V&HicgH&$wMKS-GD$DHF>61N6fRnO|%p|bb)r2H;`S2_>f4g%%0>a7C{ zDDHnzsovW6q|b$xr7ii%MXw3)`=q3 zgsDn) zaRx3jjkBesJCoJo^KN;>hv1GQo3Z`DavkIchur3xCM4yU9 zL=cws-SdIMANq8_iS}{yCcSc$mScK6qW!4ZQ=oEk#Wu#v`@IvAMPpvpdRkBbE=U73 zUjAI-)=^m^nT;CZWL9eKEuW(zpgMhkdTbgNmh*vF6_~K}%K)=mj_Jm0zD%bNw9@XI z62A4BMFa+Ba1Qqj#s(yWs^T_j*LP_QE`HT}1uwrZ^74rkK>Qt2{d!Q*%8Yhm2%&Pj zPry1}>~YAoX~x{&HkZZ2o`$MkOar;HMOS)GNE|B|Dt{dwRu#+3QZ3&kU*ClQ;$Zc* zMp;i)RiD}0>LU}mWE4_U=GS-O^D=BS79G8sTa@lo|6+{o7YeDGC?Zk&@B#jFmrRtw zN)TK|3$5x-R_+vq)V$5t#MC-=^Ex#Tx)^Ja^}Px%5)Of|+~(JN-;t$qacM4TDjH`Y z&()kjh9YM;tYL(^n05OLiVT%0@F3`4T=hW6hhB2@K-|eKYlpVsw3!G|9OuouAGBww zUfh<0DqGv6mV^%&(32BTufNW`2cQE~Zx*(pku$5dF^2R|#G7&Q>*In^sfF%Nk8Sj; zReEORLlGN&5>_!t!tLR|u$UrwOX3JIbH!_auYf z{XlIi_|LgJ7)1@c(F!Lau=b4ZuzT6_g^XWKxk+ zcF$E%HBY?>35y9=Q2}KS;*gw30YpHN)IIcOI4U5HI^piYE-yk@15bCI5hq=~C);pL zk@MnEU(MtxH2K9SKI4Ex**Eas_L5&oDV9XZy8jHtoc}j49~q;9g%HWu02%!G?u12F zjg8?>+@;)K8~k$5cMqQZ_?>~s-X35Jy-BcSs4~7gy(e6ck7nL{$A<`Ag0GgEpykD> zKQAgAF=UI(;YHKppI%=1l`a3k(~t|SAGKBjCXDO1+P$!*b9TGkPsmq9nSS7u7koUo z#!`)@ASfArH3f)7Er_c_At^N|S3JRWwp}%c+0GrQ$|iWj^y%9 ztbd?8H_gZuK6`ML9yh#+*^B@m9gm|9nCm}U1aSq^W0t}s0`<@|35 zQIH2Q$~eDaboRT92cfKimn0)JVDcDnhdy4cxXqPY`2OqEZ*X_1C!|8BXBq4d90&~r z=Fh+TPyr?E^0aTD5`H8OlMB*32q|(xL#zvE%(&T7ah{Lj8VbB}^XSm<-m_ji?F(SH zvs@kDKc=XW-q_Uo;yYy5B4GISy~#wTb#d?t4IM#D$M0LXu&LDiF{-9u;rJRMlk;)i z!gSK)yC`P;Iu_iW7XHbPr#4)iv+o$O)t2|xsHIowagP(2m2eviV14hM!ch}5qiiYL zzAa9Y*Fr+NsuNrC>q{xp9&6+%<`?V`_`xSiVY?ntgwcKIhYX1@jHe``Dy*~KD?;kH z|2y}Me>NUm92@-s!hjoCt%E8T7b|f+NOT>z3D)<#biK9cH7cN`R&gNc`El*M!~V|D z8bfaV=PH!;zH`_^yrihDsu@1lrakpB^D&7L^ZwqVq%B&z#2`KPv(+3=2Q66qW6u1i zjw3TdgDyKUqu;CK!Bp!+EJ+PrM_cyQ4`$}ik@RJsT>ONyZ-bJ^>_zz$ys}xAT&0$~ zpsa@;R3FPKq3u7$;Ndv4i4o-0Or|&x;Rwq3EqdQu<#nRNPnBEvWG0&-YH?f6@DZQs zPSX$1{b#}wR=uKEjYQONLY`E@?RR*xELS5WTs)JN#f;ag-g{KH!a(bKD03!nXv7xB>U1+rXKsPou9 zZq&w(3L}3eED7bB$Y8vJD`q`-_^!ErjRHc{{(PCxz+i$3u>1bNpWv%9*TV?Z?36M9 z?~^9{D$iv4 zf;zeamCiI#HGKLIViJ8BBUf(y5*LcpcNS00LEI}7KeXNK1nZnVNXTB4ArO29-;9L6(g!maZ5)Nsk)zDtAmngm}ZVFD&mHA@~P z$nu6Ck;cE%)*5$yc1Pgj-N<_)Yi+4fHQWxYtd&>poZgzKFNR5H-f_=xW&k+CaqU{u zJv`mDrl_YkjTN)j=%|Q)bbdN>MtHXS)S(`T(Pi78x>wOch&EeV`zUk~9B*cGGq)rl zV^ISiXsLg)rpIu=;otBEsklMNwNkl_Cw3b{Fgy+!{wP>cHv~Sklx`@prsuD67oQBL z;CPW^+R#o5h(TY5hxmKbmp8bzz~YQK$R#K{HN2HLD1C#Y!S<*OMap#d4U)8y4lu!2 zlwILNm{LQC;?Cq{aB*=&>9`&ez{>On1r;b+;Jr+?R{4H4n_Ulu^DUX+>EWgoY5&VXwst#f+zD1%p!!wcP}ynKjz9^=9Y zNnxu3gpN^70S^ip1HrUnZ_sArzOHO(DC$LB6gNTh-uw)0+`?g1E#4{R*N6{sH@H5Q zVNMmqBKwRS;gWw9?)_CpF>`HJRcsi8%anC`iy9UdKAPbt%M7H3#ZeB4v||EfH?a+G z(@7qJ17?(EW~AC7>o>m_-Fq!38Hr(0lMf{j$!XNo#9CFS8{1T$$9;-RH#k_ADOYOP z4#TQcJDoZgyD#Q0uLp-FXCM{*LXR%r^$I7sT)qOGAlld$fO}mo{rd5xw3b$U^e=wiMXt^0jrVDyxsap+|u`aTTg-iNasT4knuVD>oX)KmBok!t{ZW zC`s6gj`yc@vsqLYLU4)yVRjoOZ>@}GmP819c7DAgh?_{Om|m>0JPjM&M(yrr)Y#S= zMHKSLPi(3Ys9ZagL=k<8YCeD=VPdMjxZaz&kDT0X&1FCQwQFjvp}FfTkOdI{GdQEksU$*Ct z6(O^^*ygZoo*5cD|H-i+y5>*tQ6HTkrPxZZ`_Y`EpiZvgZ2Cx8Xe42aqh1G}T|zlk zi%GH`%bVWj1ms8Hfy{9VQq#T#iYU92V~Qe8cI}To?x1|nfEy9l!PDc8tHcn>7eY&r z!G*t#QOplM)VjLy#sTLtof*-Q)OHwvQ=`@K>Wm<;l`!DkbVt{>gUH=%;x+Mm+#wW% z@w8JHNmj0BfX*(ch4+k4LH2!iU~WZ9a3{Kf-A3j*k2yMFxZfz!SGy=PbyNJkuSZ+g z!7F=V`goPdKjvV!g|v92VJZq$v!S-}Lp?*C`53(Nb4>%!^;wOuwf-C#^FcZAa*mDx zep2|aE@3MNLTF&QM(`~gXc&93^302pn9%p)Ofe!CS$-Px@d+}R#7scN`jgb+I%^(6 zPD0}-iFk)RWO?j3E4f?bW=MaPjgXZ91Fqb!qR9MSG}1cp0lq0GgZ22{9!;p7_T?E} zf~7^p;qUAQmp_x2m2q8r2aPStI}g*R0W;QVfcf?Ar~D88RzcGyP_o?4Kh?X+fjbS< zI@gbmo27Vu|03atE0+;2zdMvoD*lt(2zE2}&&Yy^*aqv=1avS?6?A9|F9kuEakI}_ z?WL{FA^AP#y@rh^)pnv*_+pbL@A)9|o-^q4pqym2$rl9&O^=VvFJ0q9jQ%QP%=B5Y zjU45yio!98ApCrM;FQhVmkdf^nVd&Ht5>0jj+DQHy3}84FFYO1SVQV}g582uTTH<= zpF9bfQ!CdATT{zZ!kpm>Kq6Nx->bX80qxziFan%v^X`^K^@js?o#@^H zY{eO&@+P>mR3N@$~eI=uhyaQiE-+8Y;vj=s$FE7_hf zVyZM35*RU!2nG>^us$L`pq-ZV+R62BO=fBH*VgZokS1EsQEJTZFS~4k711RBXJyf( zKfFMDF2eX&Ea{XO^9H%*(P62!U%;tSpXb(5FNeF%tytBJDJ9hn7nZ2qY{t8=6T6R} zJfI}LqjlR@^R}-LwNsn`*jV`|3+}TUHW`@BNrgLbG_uZWVWUH57$tL$|Ipc@WP|k= zcZR6)VOmbLPQj@xxTOc}BcmBXjJQ7!+NY#E(lc&yPkB+s0jD499~h--R!-y5GCLQX z+|Aa#S?{WMDruf`Gz(DX$U1QjuJ6;DxSL}+>S;x7lY+*-&b7Ef0=}utJb|@0k8S1o zp9H%kG7>otu*jIJjrZ_y$#o2h*`G5G>~=FT9YU1$g0NUgA@U=e7L580!ES;-JEVaN zI5qRuMwkZvf>IqHLKQ-+K}0rs`M*Q1nWo++Y*Cs22woRQ#5MUN@GJ{fJ(2h~)7tp{ zN!o!MHVb@+AY=)$UT)o#5xi23^guh0QuB#Bqz(`!Jyx$Q76yM=7ecH9s+e1S4pM@P z+L&PZLf7l?k1t^hW>(CtGVZ5^zd+&C$%^|VTWSO*Y_Sy5QNddg_bM!8g)c=AsNf&o zQ(=VL;j+5-@q3@Y`VcbRy~Un*`HWkmm_uacnQ<2rNa49JC^6DK48=9~VKG>}EBd*e z)EMP%Pf3^SKf6F4?~e&FcXt3^BL>Gr(x zUegn#MBE~dlBjp7$rs9p2$!q7Y{>vE`RT7&%OO=w)0)KS^VQ(-FBC~(>D$l)MwZv_ zyXwQ*{qN%$(Z#it;u76h15roHZS&&^`NNFkd8z$LQN$BM!{c#teMF+wlnMT*e*PCF zrjrT4-d;%Mkdb)ghkgupD==g6mb4{QCanhq;Pqgqq6Gs_Wg=Z}vVQkP;2MMwXV77}mnASnEYI_!FGVd~15E>4<4~`vwr7$EYcjW&Wj$cGIJ;fH zK28Rl8vi}Qs^8L|Ph145OiHOa>^_9Jr%M96g-Py{wZz%4u2w&4x|KVxTVPr%_Y7HY ze9O9Cq<5h5UD+Ued~9tr>EJl9B|0Dn8U7)z=w4D|G}?K%oL9VkYx}`m%sRQaeo|t0HBP!nZ*K7H{Hxm)OG>eiteIwyXDrNN`Rhj=ZxA3ZCylj$Q~NDxurM@bET>%oqZjuA%> zFRm9t)B10~TpOZf!Zi#AYS7U|k@{Dg+K0b9XwSfLFyV@?-%z)!(fjziNg@+@n1CeP z0jR(P^>R-_6V~D#3Xcbvb};v?@Yx%s3&dJ-aU4`WWITKq(poi2o|vzw>5nS9X$mQK zr|hmh31;fZ?|V<8-J0s&mAGl~$IL_ShXg{u5x9tKK5b9B*Sz}d-4w7#h@8jZ>FsjP zXt3KVlMew!Z%M$809WSW@98CnfX$=&lrv;Sey9lE0E>W&&&PFDT(8GZr~M_B%}*^QWwu5AJn(*pO=QJ@x_FrY7~aoz-})4@hD~i zmr4{VSi^jc2O*6hh$V`>EMuIu#IumN2KGIF4}smn12M-C;`j)M;?lg@022<)&w$&O zdGu)_*?p`@YrDLqP-Fy2j|Bv>{#0CyOKj@8zV1R+zMYGG|k z3qK#X%7hEtSSG3Jd8k(G_;0^oPOu>wGGo?O6qL61w{8SIMIP=2sr3C-iDCV+_kpHe z7y?H;@$k9U38=JpSJif&BSq0-`hPqL(h^1v)*gQ8`~jvVz)f>PQD$KukkJ(CJP2`f zrm#%aMjixhkzPPYV_CFPRY1Lg*G5B@Gm5lyy}ABY)i7|SsJs=049GHY@>sB3>PHWl zo5LPve_9dmG(h&S?^tmBp}hL-UBw_h`eFF+{wY*V z_U}iPvwQ?sqa#BC_LU@5c*{G|-%;p>K{s_-^a6QVPQcgJ>)54&j!CT6Mj zjh&Ud4B=>HI`%N|a%NDex@%eQt_R`Bz^2s@5%yshGgxF!@S@}-7RNLWF|5Gad!`v} z1Q6@OzT8gaE2IDd1`2bKKv>?vH$T8zIdFC^-1$9FJjJ|6MO17a7f1Y_d_2%3^!~wd zu6=0nw~KQ7Yn3gbDg<9nzk7HVoed^j<)HdttV2jhW(DmH8$j_CSIM3hLG<^&H+!25 zSf}!$hflD`dal&Xsu)K*Cp7-Inb3Oj(r2z_Zor$VpavZN>1(n}Q}jC{40aHBTRs6L z7*d~w5`-Ul=5rHMYolRbBy3}}PeMi*2;+NG0XgJiU)ymnqb65FeDT{@bl7Aes#aKQ4b-G$ypSOU0Os5G9=Wo zLSLvf8idI4Ahd3?;K-AUmKz0Gz7;_O(qx+&hnj^3_;(dTi;;rKrO(0SWjFD?4Z;Ug z=8ZCoGh2-)<~7$=XCtEif5ZatBKpiS;qS&}_p{#xQNw#(sEWwluKh5l#OUz9$o2LV zu0`rDuMz7T`!-l{F##^kTh#EX`P{5@qo1sPne8Q1n2v9(OQKwiTUn|d!phl9xLq~U zF!j-MN$1bH%Gwm}TpuD)KbMBs7sekOw6`d007j~y;>3)dceNkt0kUWhyUB=8t4P5o zU8we{iLnK4g*wDIL)zZQU2d{Rb9G3(AX>%nnFROdraZeA%w znx6+HabeqsIM!bYsB$wIbR0?R#k$sIp9B$ot8I5)83*q37IOd>u|e!Sq>~1mx64`l z#r#%8_}l2Nl)s;0t7VX+{u;}thF|>5JhJU?9?$Bm=JGuD-6Yn_k&C>PFqk1PFy7MmrSnWgEb^-~fTttx&`hF0VO zy@e*zv*Nny=RN#+F9{%6aZ#NjDW2g?@km+mhs??Uw6Y`tL9GWTBX=qGfUQ^89w`-O4Y^_+T#=I#RDdjly}%-d`BUb0l5X z98;f+9rp%CEk4lNcUWb_4b|6Tqtg!G_S;8}GW>#R zZ9Hg~f?$pLSEQSjKI;+^9=~)$G0#>jFLbz$2fQ`#2;%l1mqm7o-$aq*f!oS@!L%qMH!BJ$ zYkvo09=x|j7$`80{v}2InY@Sj$AY+WIyL473VEoBw+v+A&HPKJr^emdE{FXV4ZirgWMpr8HR){@--<4 z(U*HhlUiQwAzrN|>!+x1C-M&7E_;vEw{TpV@ZCVca*?l_@3{`x&H7q4isU*z-tm;Y zC$v|YyvI6I8&3n9@MkVr^^~;T=miIaqULPu34E*Oyirbe}*-^3+ zO-fEr1boPQ9^THTJIY(F#xR?s@7w*2y5FDEBZa6?51lre&45h%MH~%ubEq#U3LxG~ z+GfO*uf1qr%W4`-LYBVm?fKpx_APaO3 zW4ICHvD7q|AM9}7YPZME=5P5MkX^k>S3U)S-F%*KJNbV8`|)UNk&*cBS)&jlO{l*| z5Am{lUm09xFwtO$`I0%k&ilN0bSQ)#*UeJY>u;C%$$Rm;37Ei`5g_tR3b@8yFoF;} zCh&XIaGV@2UPw+S;vt~=EaRY6zZT-={ZZ(JZW;}p!7!R=ayOT0Yr*V?KPx@D-MnkFGFW>)aNNIQ*CaS8ff$-*OqTs~GjrA;*Sc@P z!PYXpz>xdosuJN_se~KLKjvH0RIEO%=Nz%rCZu(jYb_WTXQ3vE$X{>0=!PPBk94Lb zw>3pG@0b2KYH4NX>K;OTO*Q%1BLj`aG#^`0H%8){lZQtebju1A$zX+Iti@KEwShV=N zq3~Ku22_n*cnQ*DZQa@L*k;LJ>M|3~S@;f~4oO}nDxYB+2d^~C!SCKWp7wsZcsVCS z3vku&BQCB0jxw>?BGibK)(xAa3t!9v?nw3cReefXQot_Tz3TQTIDV2`03l}XYV;&y zr^h35<(=g_Uk=s8?*0s^1nI<;bGLsmYo=61%Sp8$HE+_FR8YdP%)3^{I&Z)EMSlQb=kH@*HLEwQkl9>RK;klc!iPmpnXFEJ}N)U__7m>0@vFfEal1 z>lG+s;CtCFBWBo~TintveWjT@GS5+C9{==5qj{$yXVUuG1R;<67e`FN)ZL15My@Qx zoN|T+p0V}48tPQ8X}{o@ul=@rkKeru?<(0wcKpnYS&|Neb9oR1Ki)g#jO*DcOX=h* z3Bk=x3yUpR*LdJ$ADEQIK7ntxANNQ7PEY zvnnwgR~PL~CbhlK33j_Y(7hHtu-ut9>pn8iHAy2znR#Uj^sy-{;@dR zQEq}pc@FfPBq6OSP!5kRKvTJOhsWl>=c#!Rm-exWx+X+Q3^tvDfO;C%#2pdu0urr~ zw9U+jG3!bAoW~!9Bxuq@J7l7s!$+`af!fzaJLZ6Yh#sl{K{ zSoQY;mecd!@@TVHH1RaUpzZ{fuHnkRGq}TSQ(^xU)&>SB>{YO0DNs+T_-{mnk$q`f zaEtKUr^suLk$+&Ba=gxJW)RHNN_VQaXbDBbUY zQOpB9_-6Nhq>V03T)#oHr_8DC=BG;#j4h)E%$15ZAk0|Ms)z}vVqQx(rQ{}fgG1@Y zPWxd39ZFd4(1W0-{DnR1E4JXd_CD-f>>3}mC^V#ArOV0SyQ>WJ!)~hkX2VjM?ECa3^eZ6T2IMDGNX^cAn!61GL~y z+L7P=g2pm2Iy_+Q5t4%gN~-=Z+9H@L&y4h47%ARRd&A{WrFN2SG{ZFMGhA zyk#K}L4RS7=Tt^enXbu`HD#gLCkuF#O>_mk^f4p!v<4!3(?k*4FyfYv0{i;1D_?&KTWhz$T zJtM9ph%@pk1pW)cGlDxR{q{--2Wis7=c*}e*zzFa`J*!J&>sE^viT|-jJPGC_?-0L<6m%Ay1y6Sw7_eOzom3+O zdM~~!y=2m!@VU`SkDXW)Ce#{{77VKKx>V!QVUq5&unP?OSpTsEgvb|#tq{L>%kS5w z7y;#`iT@WJu7VTK$w?dcKL;E>kN7JF9*i(khBmDn<*%iX!AiPA64zKO4XS}fr$OBT ztqDu{^CQG0wtEF?q?u~qj?Nku4vuES(nC*LXRkw8$g1-y-HYYjwQpp6NDsd}v4qim zdoi*D2rs*^NMDfAUTH^C;_&HCA2BgS{$qOkzvsp-}8C zseZ90$UkK~hz#RGE%_viDJ#1}z>6H>1E0Oag8&cmh7h`AT{fzKn5U2byekn*_yw_U zk!F;8s=ddL{ssaig2`oz;k%c!6_gDor2Rjq^#l z0z7w#z<18d9X@K5sNFT1ldAwrK1CW{e7BtCkZM)sdXEO+C_0oZHP7+5K68yfGgizP z8vGB81mT$&8|r_Sy3lhv3q*-=P!N20GJ!R zGI8zT#-@BW=$wXtK%qP*DzK6NE8yqCZo)rf0?}jtk~A02kzhHY{l5pCz6%UU#yKxE zORe0lf9V|mbK~lN#I;I?$dv__w>v7fqhKvob1|M^{UV3l4EukQ63XJ~a;rnL!tToD z*-A&q`8{=w7Sm^dvO0`GZ%#mGomp~eb^SLBrzYlZRUXo$joMeaQh{Mudg$c>a`vjX z0f>wW!Pr~;bK28Dg)a2W#GLGyd(TCsN3%}c{Df~}T^`IzM%(C5-pN3Z)3kj96PBxi_heEjJ=svvs|M`50%P6ik96rqkL$;H9dB6e4HjXwte48&H+{V zjvlIRt&UsY{gkU61X3VCS+dyjU0Zt@y$279R;ljAU?ZKIGeHVQ58F}6#VdghUlW!m z>g)Mo)Yx9mS>kg* z75$KH@8%R_-1=Uo-?Y8)d=ajzFw5}vU}3FSxVUZ>EFW7*-@Fx}#oTqAW?sK}+O^zy zH@;c;q1s|oL~HbC_nQldet$o%vt9+A($$ArOpj92XZnTs!b#y;pNeJe8rz;tKc3*W z=r0V9jox@sQ;JUMLyP9{@bx5blziAXo?L(Yjf1xuBRkeB#OG4U1l7!9FkmxyLzTG- z)*rNgB<7d2tMGM4^98*w-d?eKQ)W*sVPZs&SX}yP?ybv$!%u&ekx25u);aycaWE=uq+1I_?pO%rTANH0?gS8d&&6 z5qjf=gurzc85i~`Y6}Zkv2l|IKDX~CHo8jJ@3wn4{BrEr?N6$i`i@oCUGXL63T}{Uq z16G3MWf~$c4uiy%Ck&2j&HLAVzhzg=O|kPUrggaUq0Bu4V7iV~H|+FKsq!u1yCMs{ zEeWEBS2IFAeZ?I7_TKd9Fe=cmuM-FS`JIClUHVsy*7zZYe{(uoXib;wwy&1V;x7{|`rx8L&fl~7;3 z73SRUtc4D}+*i6$Us33;#N{75|6g)1{=WcJUOKw$PQ7dYAmQPDXs%f&Uc{;-uj^3< zxHG?D0?ix0HBV81kXja;O75bF;Cyz1*l=a{x3E`>Lw9Tn`f4@t*bq~R`@z$50!AswZqQHI6$cD4A>X|SXW~JE_AS4{ zYR8AxFN0#r)qHk8o$}e9RsN}UC{oxvZ6|6t{IU83| zh1Q0JusgoT&6KGtxR8q*i67tGV4@~iO4y>|I$2XA2i1=0M~z3rA1tAuNfv%87?$m` zbL~6FvXK|OFJ+e+ki*Y9@8S)*69(_+nrt^WJWcbQ`Gz&MZdzu#Rg6Q9B$!q}FdkU* zJ)S9#8dt2+qebSn2|VpxZQ78}gV-t81hwDMhNH5C;=3}{_pW7pod~fWnQ*S9VI*?7 z|6qY0%MHSzrzlvSOqpBG2ry84#Je6pGYHvq=WV=!(Ub32>MV1*h2!t}b>G8p*)BA# z`QG1^a}1RL_ZSPVe(<7XUt1yXiBM&RR!za#VbbW4qlFn*_m+xzprum3ITm zh3}ZqOug4W#fS6_xfs)(zR5IJE*|;1`eWkt9+B;Ph1;qnU7rsXMs{+yG_y(Y`;=RI zlgQTJ>iJuzoTqqDL~O*|8$XDxLxQBGHHIA&#Y`mcG27G2tS0-(<^BqPCFjzA*+#iW zb(>TmD>8;4RKzSZ=p%@4{mN;(ILMpq_N0Y3cN?1Cr=%?$WUloo=fXaj6(vIu)g*#6 zLve`Di{#pms4y0>((=?v&%r%7I`c$Cof=6CuScqJ!rWgDO?EkpNFS1F`u6AN!q2+r zY9eJ;*`O)wGrV@tOZnoN>MsJI{=7m-Q_2m{x`@565>*)jOM ztwt4%l9ySul#bnUNh?rUVmavVEE5P|94WU_yjo9d6iye>zxcE=VW}yBQi<`!N z19V#04_Vojf(Gk}0!u=}tsL)uKhLoj?B}S2Tr-TR5=oOKNb44;I+8uyZiVma6qpwG z!qQt~FV$Ammoej(W2wKXE?`bS4pBHT7o^+HFpk^ZmqH|c>1Cfv(z-TOIqnE9bF(a~ z9BTI~AE!kHv@$Q{`t?Li{tNxVaBc}x04Ema&gSb{_DB2?aHC7KjSCm3dL$HK4&?0A3P|d+_u}gnR@fSBuy!hJ+tT2vE%@R>x14}i6VfE6Y%w3 zdB4Ub2bO<8Vu-!b6*h5E8`R3jWU}}w5E97ngff*5e*S04h#y8jNr&~o<3>)+^ z4;^V*m6~bVHhilXF=AWY{!hIkoE{ODLteR#Y)qW|tyyvaMWmOJpJ(I*>W|12mOLVS z(^U_@C9Mc`cJ6<;zhOcT1sPXF=zmmeG?*UUYoK(vM5tthKPPt^-I0#mYfaR|gKpO! zNz5LtN?VJTW_W%7N=!hmWdpXA4PPK68)RP(1Zr4hSPR)uuDqW;fvsL3T%ae3qewab zk)?w*`$%492)-wEi<0{1pRhalld7I~f%Wl!CGvIOl_Hk{#ahG9Gjhl`!&>+e(_;=t zf6b>h^hR5gSawkk3t2Z-Gi_q)qCrwWyLdnENQ2bBq}@t5pV)JqxGTdwpc9s8GlNaZ zD3Sku{6CZ6yKXn1M7LbI=~K3rPmQ3ry;`oaCrI6Y6plWM@~uqQ0~TQM9N0`&W-0U& zaDVf67CV59Esa+$cgy3rq3>`F>E)~p(5R0FTCV&jNX=aMo|CDFA^M)M%`JdUGNQk0 z9}rO5lV!w5F_PO*&U%{j?ifwjXDO(QxvK2>)A<0V$3ZZH{PV=u7+sw#HgB#d5ph`i zkkICJkKL+Q0PlWOc?5djeoYT(q)!Dq8(a&8t%%)0)AO)YdD4nmP^u%o?OTfVD%wAYiu>$zyJTtDAMk7MT*Ixg>_kYvxa?tTVuT=?(D|1uKzUN795 zH%Ir7=912rhJ&Vb506dh>EbM1KHIKT3R6L)MYwi@K-9M9{}+Iqr#nBzL=1-cH3j6d z!%T`@LOycD1MBE3QmBVsjtw%W48RD)=t+lv2cdw07THaID?cj zyME|i){{b!do$6+nicumzU=Gk8?>SbY0QlJVcUhw6GnOf2VOS{0 z!p%-4t;+^;O;eXunmkOl0>amxNmG_OeDZiRErC!j5tX@E)Hh%obUrcNyAu4KPl0fW zIv`v^d#S8HJd)Tpv$-Laar&r3LN4t1#2qq~(hGKD@tK(OlGIwwE%sh-X&rBjG;CLD zKI@AO^*Go&9HVcT#Uo{#wX1&hZQHE@b&NGm^&d!`ot_#-RL$D3w}eN?MSB9ot@axD z_Uk~TQ3r3D>|_{KlQw*GHn8wM|37=G&1c9#KRXWI>E9Kma-d_gPi7?E`ZpriH;8Xi zbw8CGlQtx6M&8McgfLi|({Z*qF=I5l~pMsWnG7p8ou#TD;O_2;$Cr zOuA-PRFP;7`{6A}Ar*AFZG=O@R(4IPv!pf@thC04rxUw*PW&O!)I{j}tyR*~Je~KD z7k%|l&(~k3@Kw0}JAtY#t#kgPi!(!~du=>}!R9&hr-#aI0qZUg6)&kk%;e^*bAICQ z#OvqFPZwi0B4?c=5<1BZc)hWG=u3g8LI1FmKq1yM_0sPAu9=~ERc}L_Lkq~W{bhhdBEJ+OXA%%blsaEneqLFc>0m!2V3mIca!{7$GaU8ZOa@q}?sqn_xgpwtrM6a` z3Mk~_pcE}{_)g#re4+6|!S&+#)bHL!yG%So7vUJ2sm|vH?7PzbZfWtWnN>hiDNpx@ z6fx?XsotYI1a-=P=AMGZvhH7FK5rb8l0Z34{@;)PpGlykYf*S-tT@2+en5=|4tcr~E&Q*36Hj6kUR#z(k$PFcesd$l-KCxc4tf{`x1dQy``P{rKOH1fs@fnMvx}eTouxHDz9?sJC)VXlrpx2j4mI1@z{d8G+=zJw4A|yE)7Y` zy)8kbmHp#qVoTNsRcdU?8ZEMMLddE$W8St)KLb@NY}7tb(s#-l)k@6h^1&C6Eoh#t z`cHwVQG4IyEoVgpqgqQF)mfcUVWXX3iCT?f)L>#%)MzJIl13X_St@CC`rt>2tzRd% zy+d-@a;v>>@|LxdHfky_xvB}#4(}pK$wMvV!Dus<2Y$Zt6 zs7lkO2}{suQ*28sL8H?LKT2%@Pj4vQMKW4r37O46um`ypi!99zm}wGS`htTwO3B#q|XAFI2+$f40a zKBm>xyegBVeY-1Yx$OQ-2^!72*F=&=D??_AS)z`$+Nxx;+L$q=LD^@ZefcQq)V`7_ z`b?SIZ!#-qboSuKh^?PTymMPWmxiR}!gib#HmYsMQ6=w#OuFM}mDblU-f=dstw!iL zn>KyBW$%<}!&2hCQFB$ub;eyj_;ehNf%NbJ9bs%n9%aBsr6LW@$;5}|f5g?IirpTpP@Mb000000000000000000000000000000008hS{0EQS VCiBJJ%G>||002ovPDHLkV1iZvU{U}8 literal 19391 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 rlj-|cJ?)9gt6BC8V Date: Fri, 11 Nov 2022 16:03:41 -0800 Subject: [PATCH 108/339] FVM specific EVM precompiles (#792) * FVM specific EVM precompiles - changes randomness functions to take a raw i64 since this is userspace now - add precompiles: resolve_address, lookup_address, get_actor_code_cid, get_randomness(beacon/chain) * leave context as TODO * fixes after rebase * rustfmt * add new user randomness functions (that dont limit types to domain seperation tags) * add some precompile docs, use ID addresses instead of ETH style ID addresses, assert reserved bytes are zeroes * fmt & clippy * Update precompiles.rs * kill old expect randomness functions and route them through the new ones * rename user get_randomness_x functions to be more consistient * rustfmt --- actors/evm/src/interpreter/precompiles.rs | 120 +++++++++++++--- runtime/src/runtime/fvm.rs | 67 ++++----- runtime/src/runtime/mod.rs | 16 ++- runtime/src/test_utils.rs | 164 +++++++++++++--------- test_vm/src/lib.rs | 18 +++ 5 files changed, 267 insertions(+), 118 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 2ae3771fb..70de04c06 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -1,14 +1,17 @@ use std::{borrow::Cow, convert::TryInto, marker::PhantomData}; use super::U256; + use fil_actors_runtime::runtime::{Primitives, Runtime}; use fvm_shared::{ + address::Address, bigint::BigUint, crypto::{ hash::SupportedHashes, signature::{SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, }, }; +use num_traits::FromPrimitive; use num_traits::{One, Zero}; use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1, G2}; use uint::byteorder::{ByteOrder, LE}; @@ -23,6 +26,7 @@ lazy_static::lazy_static! { pub enum PrecompileError { EcErr(CurveError), EcGroupErr(GroupError), + InvalidInput, // TODO merge with below? IncorrectInputSize, OutOfGas, } @@ -49,7 +53,7 @@ pub type PrecompileFn = fn(&RT, &[u8]) -> PrecompileResult; pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec /// Generates a list of precompile smart contracts, index + 1 is the address (another option is to make an enum) -const fn gen_precompiles() -> [PrecompileFn; 9] { +const fn gen_precompiles() -> [PrecompileFn; 13] { [ ec_recover, // ecrecover 0x01 sha256, // SHA2-256 0x02 @@ -60,13 +64,18 @@ const fn gen_precompiles() -> [PrecompileFn; 9] { ec_mul, // ecMul 0x07 ec_pairing, // ecPairing 0x08 blake2f, // blake2f 0x09 + // FIL precompiles + resolve_address, // lookup_address 0x0a + lookup_address, // resolve_address 0x0b + get_actor_code_cid, // get code cid 0x0c + get_randomness, // rand 0x0d ] } pub struct Precompiles(PhantomData); impl Precompiles { - const PRECOMPILES: [PrecompileFn; 9] = gen_precompiles(); + const PRECOMPILES: [PrecompileFn; 13] = gen_precompiles(); const MAX_PRECOMPILE: U256 = { let mut limbs = [0u64; 4]; limbs[0] = Self::PRECOMPILES.len() as u64; @@ -83,7 +92,6 @@ impl Precompiles { } // It is uncomfortable how much Eth pads everything... -/// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/common/bytes.go#L108 fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { let mut input: Cow<[u8]> = input.into(); let input_len = input.len(); @@ -93,7 +101,91 @@ fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u input } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L165 +// --- Precompiles --- + +/// Read right padded BE encoded u64 ID address +/// returns encoded CID or an empty array if actor not found +fn get_actor_code_cid(rt: &RT, input: &[u8]) -> PrecompileResult { + let id_bytes = read_right_pad(input, 8); + let id = u64::from_be_bytes(id_bytes.as_ref().try_into().unwrap()); + Ok(rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) +} + +/// Params: +/// +/// | Param | Value | Byte Length | +/// |------------------|---------------------------|---| +/// | randomness_type | `Chain`(0) OR `Beacon`(1) | 1 | +/// | RESERVED | must be zeroes | 7 | +/// | personalization | `i64` (LE encoded) | 8 | +/// | randomness_epoch | `i64` (LE encoded) | 8 | +/// | entropy_length | `u64` (LE encoded) | 8 | +/// | entropy | input\[32..] (right padded)| entropy_length | +/// +/// Returns empty array if invalid randomness type +/// Errors if unable to fetch randomness or bits are in reserved zone +fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { + // 1 + 7 (reserved) + 8 + 8 + 8 = 32 + let word = read_right_pad(input, 32); + + #[derive(num_derive::FromPrimitive)] + #[repr(u8)] + enum RandomnessType { + Chain = 0, + Beacon = 1, + } + + let randomness_type = RandomnessType::from_u8(word[0]); + + // 7 bytes reserved + if word[1..8] != [0u8; 7] { + return Err(PrecompileError::InvalidInput); + } + + let personalization = i64::from_le_bytes(word[8..16].try_into().unwrap()); + let rand_epoch = i64::from_le_bytes(word[16..24].try_into().unwrap()); + let entropy_len = u64::from_le_bytes(word[24..32].try_into().unwrap()); + + let entropy = read_right_pad(&input[32..], entropy_len as usize); + + let randomness = match randomness_type { + Some(RandomnessType::Chain) => rt + .user_get_randomness_from_chain(personalization, rand_epoch, &entropy) + .map(|a| a.to_vec()), + Some(RandomnessType::Beacon) => rt + .user_get_randomness_from_beacon(personalization, rand_epoch, &entropy) + .map(|a| a.to_vec()), + None => Ok(Vec::new()), + }; + + randomness.map_err(|_| PrecompileError::InvalidInput) +} + +/// Reads right padded BE encoded u64 +/// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found +fn lookup_address(rt: &RT, input: &[u8]) -> PrecompileResult { + let id_bytes = read_right_pad(input, 8); + let id = u64::from_be_bytes(id_bytes.as_ref().try_into().unwrap()); + + let address = rt.lookup_address(id); + let ab = match address { + Some(a) => a.to_bytes(), + None => Vec::new(), + }; + Ok(ab) +} + +/// Reads a FIL encoded address +/// Resolves a FIL encoded address into an ID address +/// returns BE encoded u64 or empty array if nothing found +fn resolve_address(rt: &RT, input: &[u8]) -> PrecompileResult { + let addr = match Address::from_bytes(input) { + Ok(o) => o, + Err(_) => return Ok(Vec::new()), + }; + Ok(rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) +} + /// recover a secp256k1 pubkey from a hash, recovery byte, and a signature fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { let input = read_right_pad(input, 128); @@ -136,13 +228,11 @@ fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { Ok(address) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L206 /// hash with sha2-256 fn sha256(rt: &RT, input: &[u8]) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Sha2_256, input)) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L221 /// hash with ripemd160 fn ripemd160(rt: &RT, input: &[u8]) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Ripemd160, input)) @@ -153,20 +243,18 @@ fn identity(_: &RT, input: &[u8]) -> PrecompileResult { Ok(Vec::from(input)) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L363 // https://eips.ethereum.org/EIPS/eip-198 /// modulus exponent a number fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { let input = read_right_pad(input, 96); // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. - // We also need to try converting into u32 since we are running in WASM, and since we don't have any complexity - // functions or specific gas measurements of modexp in FEVM, we let values be whatever and have FEVM gas accounting - // be the one responsible for keeping things within reasonable limits. + // Since we don't have any complexity functions or specific gas measurements of modexp in FEVM, + // we let values be whatever and have FEVM gas accounting be the one responsible for keeping things within reasonable limits. // We _also_ will default with 0 (though this is already done with right padding above) since that is expected to be fine. - // Eth really relies heavily on gas checking being correct and safe for nodes... - fn read_bigint_len(input: &[u8], size: usize) -> Result { - let digits = BigUint::from_bytes_be(&input[size..size + 32]); + // Eth really relies heavily on gas checking being correct and safe for client nodes... + fn read_bigint_len(input: &[u8], start: usize) -> Result { + let digits = BigUint::from_bytes_be(&input[start..start + 32]); let mut digits = digits.iter_u64_digits(); // truncate to 64 bits digits @@ -231,7 +319,6 @@ fn curve_to_vec(curve: G1) -> Vec { .unwrap_or_else(|| vec![0; 64]) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L413 /// add 2 points together on an elliptic curve fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { let input = read_right_pad(input, 128); @@ -241,7 +328,6 @@ fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { Ok(curve_to_vec(point1 + point2)) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L455 /// multiply a point on an elliptic curve by a scalar value fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { let input = read_right_pad(input, 96); @@ -255,7 +341,6 @@ fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { Ok(curve_to_vec(point * scalar)) } -// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L504 /// pairs multple groups of twisted bn curves fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { @@ -319,7 +404,6 @@ fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { } /// https://eips.ethereum.org/EIPS/eip-152 -/// https://github.com/ethereum/go-ethereum/blob/25b35c97289a8db4753cdf5ab7f2b306ec71794d/core/vm/contracts.go#L581 fn blake2f(_: &RT, input: &[u8]) -> PrecompileResult { if input.len() != 213 { return Err(PrecompileError::IncorrectInputSize); @@ -824,7 +908,7 @@ mod tests { // NOTE: // original test case ran ffffffff rounds of blake2b // with an expected output of fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615 - // I ran this sucessfully 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. + // 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!( diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index baa792f7d..9c6d95e59 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -217,30 +217,7 @@ where rand_epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - // Note: For Go actors, Lotus treated all failures to get randomness as "fatal" errors, - // which it then translated into exit code SysErrReserved1 (= 4, and now known as - // SYS_ILLEGAL_INSTRUCTION), rather than just aborting with an appropriate exit code. - // - // We can replicate that here prior to network v16, but from nv16 onwards the FVM will - // override the attempt to use a system exit code, and produce - // SYS_ILLEGAL_EXIT_CODE (9) instead. - // - // Since that behaviour changes, we may as well abort with a more appropriate exit code - // explicitly. - fvm::rand::get_chain_randomness(personalization as i64, rand_epoch, entropy) - .map_err(|e| { - if self.network_version() < NetworkVersion::V16 { - ActorError::unchecked(ExitCode::SYS_ILLEGAL_INSTRUCTION, - "failed to get chain randomness".into()) - } else { - match e { - ErrorNumber::LimitExceeded => { - actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) - } - e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), - } - } - }) + self.user_get_randomness_from_chain(personalization as i64, rand_epoch, entropy) } fn get_randomness_from_beacon( @@ -249,19 +226,37 @@ where rand_epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - // See note on exit codes in get_randomness_from_tickets. - fvm::rand::get_beacon_randomness(personalization as i64, rand_epoch, entropy) - .map_err(|e| { - if self.network_version() < NetworkVersion::V16 { - ActorError::unchecked(ExitCode::SYS_ILLEGAL_INSTRUCTION, - "failed to get chain randomness".into()) - } else { - match e { - ErrorNumber::LimitExceeded => { - actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) - } - e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), + self.user_get_randomness_from_beacon(personalization as i64, rand_epoch, entropy) + } + + fn user_get_randomness_from_beacon( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + fvm::rand::get_beacon_randomness(personalization, epoch, entropy).map_err(|e| { + match e { + ErrorNumber::LimitExceeded => { + actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) + } + e => actor_error!(assertion_failed; "get beacon randomness failed with an unexpected error: {}", e), + } + }) + } + + fn user_get_randomness_from_chain( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + fvm::rand::get_chain_randomness(personalization, epoch, entropy).map_err(|e| { + match e { + ErrorNumber::LimitExceeded => { + actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) } + e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), } }) } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 1b2f2676a..61229416f 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -88,7 +88,7 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { fn resolve_address(&self, address: &Address) -> Option; /// Looks-up the "predictable" address of an actor by ID, if any. Returns None if either the - /// target actor doesn't exist, or if the target actord doesn't have a predictable address. + /// target actor doesn't exist, or if the target actor doesn't have either a f2 or f4 address. fn lookup_address(&self, id: ActorID) -> Option
; /// Look up the code ID at an actor address. @@ -114,6 +114,20 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; + fn user_get_randomness_from_chain( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; + + fn user_get_randomness_from_beacon( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; + /// Initializes the state object. /// This is only valid when the state has not yet been initialized. /// NOTE: we should also limit this to being invoked during the constructor method diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index b7bab9b2f..e72efd6a7 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -181,8 +181,8 @@ pub struct Expectations { pub expect_verify_post: Option, pub expect_compute_unsealed_sector_cid: VecDeque, pub expect_verify_consensus_fault: Option, - pub expect_get_randomness_tickets: VecDeque, - pub expect_get_randomness_beacon: VecDeque, + pub expect_get_randomness_from_chain: VecDeque, + pub expect_get_randomness_from_beacon: VecDeque, pub expect_batch_verify_seals: Option, pub expect_aggregate_verify_seals: Option, pub expect_replica_verify: Option, @@ -253,14 +253,14 @@ impl Expectations { self.expect_verify_consensus_fault ); assert!( - self.expect_get_randomness_tickets.is_empty(), - "expect_get_randomness_tickets {:?}, not received", - self.expect_get_randomness_tickets + self.expect_get_randomness_from_beacon.is_empty(), + "expect_get_randomness_from_beacon {:?}, not received", + self.expect_get_randomness_from_beacon ); assert!( - self.expect_get_randomness_beacon.is_empty(), - "expect_get_randomness_beacon {:?}, not received", - self.expect_get_randomness_beacon + self.expect_get_randomness_from_chain.is_empty(), + "expect_get_randomness_from_chain {:?}, not received", + self.expect_get_randomness_from_chain ); assert!( self.expect_batch_verify_seals.is_none(), @@ -391,7 +391,7 @@ pub struct ExpectComputeUnsealedSectorCid { #[derive(Clone, Debug)] pub struct ExpectRandomness { - tag: DomainSeparationTag, + tag: i64, epoch: ChainEpoch, entropy: Vec, out: [u8; RANDOMNESS_LENGTH], @@ -677,8 +677,7 @@ impl MockRuntime { entropy: Vec, out: [u8; RANDOMNESS_LENGTH], ) { - let a = ExpectRandomness { tag, epoch, entropy, out }; - self.expectations.borrow_mut().expect_get_randomness_tickets.push_back(a); + self.expect_user_get_randomness_from_chain(tag as i64, epoch, entropy, out) } #[allow(dead_code)] @@ -689,8 +688,29 @@ impl MockRuntime { entropy: Vec, out: [u8; RANDOMNESS_LENGTH], ) { - let a = ExpectRandomness { tag, epoch, entropy, out }; - self.expectations.borrow_mut().expect_get_randomness_beacon.push_back(a); + self.expect_user_get_randomness_from_beacon(tag as i64, epoch, entropy, out) + } + + pub fn expect_user_get_randomness_from_beacon( + &mut self, + personalization: i64, + epoch: ChainEpoch, + entropy: Vec, + out: [u8; RANDOMNESS_LENGTH], + ) { + let a = ExpectRandomness { tag: personalization, epoch, entropy, out }; + self.expectations.borrow_mut().expect_get_randomness_from_beacon.push_back(a); + } + + pub fn expect_user_get_randomness_from_chain( + &mut self, + personalization: i64, + epoch: ChainEpoch, + entropy: Vec, + out: [u8; RANDOMNESS_LENGTH], + ) { + let a = ExpectRandomness { tag: personalization, epoch, entropy, out }; + self.expectations.borrow_mut().expect_get_randomness_from_chain.push_back(a); } #[allow(dead_code)] @@ -952,31 +972,7 @@ impl Runtime for MockRuntime { epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - let expected = self - .expectations - .borrow_mut() - .expect_get_randomness_tickets - .pop_front() - .expect("unexpected call to get_randomness_from_tickets"); - - assert!(epoch <= self.epoch, "attempt to get randomness from future"); - assert_eq!( - expected.tag, tag, - "unexpected domain separation tag, expected: {:?}, actual: {:?}", - expected.tag, tag - ); - assert_eq!( - expected.epoch, epoch, - "unexpected epoch, expected: {:?}, actual: {:?}", - expected.epoch, epoch - ); - assert_eq!( - expected.entropy, *entropy, - "unexpected entroy, expected {:?}, actual: {:?}", - expected.entropy, entropy - ); - - Ok(expected.out) + self.user_get_randomness_from_chain(tag as i64, epoch, entropy) } fn get_randomness_from_beacon( @@ -985,31 +981,7 @@ impl Runtime for MockRuntime { epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - let expected = self - .expectations - .borrow_mut() - .expect_get_randomness_beacon - .pop_front() - .expect("unexpected call to get_randomness_from_beacon"); - - assert!(epoch <= self.epoch, "attempt to get randomness from future"); - assert_eq!( - expected.tag, tag, - "unexpected domain separation tag, expected: {:?}, actual: {:?}", - expected.tag, tag - ); - assert_eq!( - expected.epoch, epoch, - "unexpected epoch, expected: {:?}, actual: {:?}", - expected.epoch, epoch - ); - assert_eq!( - expected.entropy, *entropy, - "unexpected entroy, expected {:?}, actual: {:?}", - expected.entropy, entropy - ); - - Ok(expected.out) + self.user_get_randomness_from_beacon(tag as i64, epoch, entropy) } fn create(&mut self, obj: &C) -> Result<(), ActorError> { @@ -1205,6 +1177,72 @@ impl Runtime for MockRuntime { } self.tipset_cids.get((self.epoch - epoch) as usize).copied() } + + fn user_get_randomness_from_chain( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + let expected = self + .expectations + .borrow_mut() + .expect_get_randomness_from_chain + .pop_front() + .expect("unexpected call to get_chain_randomness"); + + assert!(epoch <= self.epoch, "attempt to get randomness from future"); + assert_eq!( + expected.tag, personalization, + "unexpected domain personalization tag, expected: {:?}, actual: {:?}", + expected.tag, personalization + ); + assert_eq!( + expected.epoch, epoch, + "unexpected epoch, expected: {:?}, actual: {:?}", + expected.epoch, epoch + ); + assert_eq!( + expected.entropy, *entropy, + "unexpected entroy, expected {:?}, actual: {:?}", + expected.entropy, entropy + ); + + Ok(expected.out) + } + + fn user_get_randomness_from_beacon( + &self, + personalization: i64, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + let expected = self + .expectations + .borrow_mut() + .expect_get_randomness_from_beacon + .pop_front() + .expect("unexpected call to get_beacon_randomness"); + + assert!(epoch <= self.epoch, "attempt to get randomness from future"); + assert_eq!( + expected.tag, personalization, + "unexpected domain personalization tag, expected: {:?}, actual: {:?}", + expected.tag, personalization + ); + assert_eq!( + expected.epoch, epoch, + "unexpected epoch, expected: {:?}, actual: {:?}", + expected.epoch, epoch + ); + assert_eq!( + expected.entropy, *entropy, + "unexpected entroy, expected {:?}, actual: {:?}", + expected.entropy, entropy + ); + + Ok(expected.out) + } } impl Primitives for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 45635ee0e..59770b977 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -964,6 +964,24 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { Ok(TEST_VM_RAND_ARRAY) } + fn user_get_randomness_from_beacon( + &self, + _personalization: i64, + _epoch: ChainEpoch, + _entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + Ok(TEST_VM_RAND_ARRAY) + } + + fn user_get_randomness_from_chain( + &self, + _personalization: i64, + _epoch: ChainEpoch, + _entropy: &[u8], + ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { + Ok(TEST_VM_RAND_ARRAY) + } + fn get_state_root(&self) -> Result { Ok(self.v.get_actor(self.to()).unwrap().head) } From 5e40e06ec1148fce543dc4a1f164b28de9a48324 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 11 Nov 2022 17:06:30 -0800 Subject: [PATCH 109/339] EVM: use more EVM style parameters for FVM syscall precompiles (#837) * use more EVM style params for precompiles --- actors/evm/src/interpreter/precompiles.rs | 57 ++++++++++++----------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 70de04c06..0c3e3d425 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -103,50 +103,51 @@ fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u // --- Precompiles --- -/// Read right padded BE encoded u64 ID address +/// Read right padded BE encoded low u64 ID address from a u256 word /// returns encoded CID or an empty array if actor not found fn get_actor_code_cid(rt: &RT, input: &[u8]) -> PrecompileResult { - let id_bytes = read_right_pad(input, 8); - let id = u64::from_be_bytes(id_bytes.as_ref().try_into().unwrap()); + let id_bytes = read_right_pad(input, 32); + let id = U256::from_big_endian(&id_bytes).low_u64(); Ok(rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) } /// Params: /// -/// | Param | Value | Byte Length | -/// |------------------|---------------------------|---| -/// | randomness_type | `Chain`(0) OR `Beacon`(1) | 1 | -/// | RESERVED | must be zeroes | 7 | -/// | personalization | `i64` (LE encoded) | 8 | -/// | randomness_epoch | `i64` (LE encoded) | 8 | -/// | entropy_length | `u64` (LE encoded) | 8 | -/// | entropy | input\[32..] (right padded)| entropy_length | +/// | Param | Value | +/// |------------------|---------------------------| +/// | randomness_type | U256 - low i32: `Chain`(0) OR `Beacon`(1) | +/// | personalization | U256 - low i64 | +/// | randomness_epoch | U256 - low i64 | +/// | entropy_length | U256 - low u64 | +/// | entropy | input\[32..] (right padded)| +/// +/// any bytes in between values are ignored /// /// Returns empty array if invalid randomness type -/// Errors if unable to fetch randomness or bits are in reserved zone +/// Errors if unable to fetch randomness fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { - // 1 + 7 (reserved) + 8 + 8 + 8 = 32 - let word = read_right_pad(input, 32); + // 32 * 4 = 128 + let input = read_right_pad(input, 128); #[derive(num_derive::FromPrimitive)] - #[repr(u8)] + #[repr(i32)] enum RandomnessType { Chain = 0, Beacon = 1, } - let randomness_type = RandomnessType::from_u8(word[0]); - - // 7 bytes reserved - if word[1..8] != [0u8; 7] { - return Err(PrecompileError::InvalidInput); - } + let randomness_word = &input[0..32]; + let personalization_bytes = &input[32..64]; + let epoch_bytes = &input[64..96]; + let entropy_len_bytes = &input[96..128]; - let personalization = i64::from_le_bytes(word[8..16].try_into().unwrap()); - let rand_epoch = i64::from_le_bytes(word[16..24].try_into().unwrap()); - let entropy_len = u64::from_le_bytes(word[24..32].try_into().unwrap()); + let randomness_type = + RandomnessType::from_i32(i32::from_be_bytes(randomness_word[28..32].try_into().unwrap())); + let personalization = i64::from_be_bytes(personalization_bytes[24..32].try_into().unwrap()); + let rand_epoch = i64::from_be_bytes(epoch_bytes[24..32].try_into().unwrap()); + let entropy_len = u64::from_be_bytes(entropy_len_bytes[24..32].try_into().unwrap()); - let entropy = read_right_pad(&input[32..], entropy_len as usize); + let entropy = read_right_pad(&input[128..], entropy_len as usize); let randomness = match randomness_type { Some(RandomnessType::Chain) => rt @@ -161,11 +162,11 @@ fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { randomness.map_err(|_| PrecompileError::InvalidInput) } -/// Reads right padded BE encoded u64 +/// Read right padded BE encoded low u64 ID address from a u256 word /// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found fn lookup_address(rt: &RT, input: &[u8]) -> PrecompileResult { - let id_bytes = read_right_pad(input, 8); - let id = u64::from_be_bytes(id_bytes.as_ref().try_into().unwrap()); + let id_bytes = read_right_pad(input, 32); + let id = U256::from_big_endian(&id_bytes).low_u64(); let address = rt.lookup_address(id); let ab = match address { From 0baa03db7806905da54f043ec240644b35340b04 Mon Sep 17 00:00:00 2001 From: raulk Date: Sun, 13 Nov 2022 21:24:55 +0000 Subject: [PATCH 110/339] upgrade fvm_shared@3.0.0-alpha.9, fvm_sdk@3.0.0-alpha.9. (#838) --- Cargo.lock | 200 +++++++++++++++++++++---------------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 19 files changed, 132 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac04a64bc..4b51fd9aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,30 +60,30 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ - "concurrent-queue", + "concurrent-queue 1.2.4", "event-listener", "futures-core", ] [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", - "concurrent-queue", + "concurrent-queue 2.0.0", "fastrand", "futures-lite", - "once_cell", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", @@ -96,16 +96,16 @@ dependencies = [ [[package]] name = "async-io" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" dependencies = [ + "async-lock", "autocfg", - "concurrent-queue", + "concurrent-queue 1.2.4", "futures-lite", "libc", "log", - "once_cell", "parking", "polling", "slab", @@ -116,11 +116,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" dependencies = [ "event-listener", + "futures-lite", ] [[package]] @@ -309,9 +310,9 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" dependencies = [ "digest 0.10.5", ] @@ -418,9 +419,9 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byte-slice-cast" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "byte-tools" @@ -469,15 +470,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +checksum = "406c859255d568f4f742b3146d51851f3bfd49f734a2c289d9107c4395ee0062" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", + "thiserror", ] [[package]] @@ -491,9 +493,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" [[package]] name = "cfg-if" @@ -503,9 +505,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "num-integer", "num-traits", @@ -536,9 +538,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", "bitflags", @@ -639,11 +641,20 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "concurrent-queue" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "constant_time_eq" @@ -1297,9 +1308,9 @@ dependencies = [ [[package]] name = "fastrlp-derive" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e9158c1d8f0a7a716c9191562eaabba70268ba64972ef4871ce8d66fd08872" +checksum = "d6e454d03710df0cd95ce075d7731ce3fa35fb3779c15270cd491bc5f2ef9355" dependencies = [ "bytes", "proc-macro2", @@ -1309,9 +1320,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ "rand_core", "subtle", @@ -2049,9 +2060,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.8" +version = "3.0.0-alpha.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bceb744ba412c16af1040379e435f8a775dc93911f65d26ca4a7c67b6e3afb1" +checksum = "15c35ecb57fe0544b997b4378b93b53e8896f4aee50544b3b4e4a17f0ffbdca2" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2064,9 +2075,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.8" +version = "3.0.0-alpha.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0722cf399cfde0e2c01f9069d74da758ed01aa859c2f8be0e153bd557ecc9" +checksum = "d8d8c7088aaf2f51e4373bb708b97d0fd013807e565c68e5642e206744954df6" dependencies = [ "anyhow", "blake2b_simd", @@ -2156,9 +2167,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -2260,9 +2271,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -2390,9 +2401,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" [[package]] name = "itertools" @@ -2457,9 +2468,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.135" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libipld-core" @@ -2571,9 +2582,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", @@ -2614,9 +2625,9 @@ dependencies = [ [[package]] name = "multihash-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate", "proc-macro-error", @@ -2725,9 +2736,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ "hermit-abi", "libc", @@ -2735,9 +2746,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "opaque-debug" @@ -2753,9 +2764,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" [[package]] name = "parity-scale-codec" @@ -2871,9 +2882,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" dependencies = [ "thiserror", "ucd-trie", @@ -2881,9 +2892,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" +checksum = "d5fd9bc6500181952d34bd0b2b0163a54d794227b498be0b7afa7698d0a7b18f" dependencies = [ "pest", "pest_generator", @@ -2891,9 +2902,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" +checksum = "d2610d5ac5156217b4ff8e46ddcef7cdf44b273da2ac5bca2ecbfa86a330e7c4" dependencies = [ "pest", "pest_meta", @@ -2904,9 +2915,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" +checksum = "824749bf7e21dd66b36fbe26b3f45c713879cccd4a009a917ab8e045ca8246fe" dependencies = [ "once_cell", "pest", @@ -2967,9 +2978,9 @@ dependencies = [ [[package]] name = "polling" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" +checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" dependencies = [ "autocfg", "cfg-if", @@ -2981,9 +2992,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" @@ -3000,11 +3011,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "once_cell", "thiserror", "toml", ] @@ -3104,9 +3114,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -3115,9 +3125,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "reqwest" @@ -3160,9 +3170,9 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ "crypto-bigint", "hmac", @@ -3756,9 +3766,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" @@ -4194,46 +4204,60 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "winreg" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 8973fe581..4a01488f4 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 4efe94552..c1c200dc7 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index cf1eb3ad9..7e5ae3c7f 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index dc3ed4bc9..6e178401d 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 374dc9407..4a2a7d1eb 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.8", optional = true } -fvm_shared = { version = "3.0.0-alpha.8", optional = true } +fvm_sdk = { version = "3.0.0-alpha.9", optional = true } +fvm_shared = { version = "3.0.0-alpha.9", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 46d4e2af7..804f78666 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 6a84116e2..cf5c00515 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_hamt = "0.6.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 6a007e80d..80e9cc87a 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -23,7 +23,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index baac65d03..e15be984a 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.0" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 90858e82e..9d1df63b3 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", 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 89626b871..b7758d43b 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index f21505a8b..7ea9b084a 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_hamt = "0.6.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 5dd3d32cc..0159f6a9c 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 9584a2649..905347527 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index acb456073..d298d3baa 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 4e87423a8..d68470316 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.0" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.8", optional = true } +fvm_sdk = { version = "3.0.0-alpha.9", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index ebe56f1aa..a6000e262 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index fba076f9d..9c5be5fb6 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -37,7 +37,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.8", default-features = false } +fvm_shared = { version = "3.0.0-alpha.9", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From e8d71b28c3e32ac74cba17608881605bd6dbe82b Mon Sep 17 00:00:00 2001 From: raulk Date: Mon, 14 Nov 2022 16:57:47 +0000 Subject: [PATCH 111/339] upgrade fvm dependencies. (#840) --- Cargo.lock | 12 ++++++------ actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 4 ++-- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 ++-- actors/evm/Cargo.toml | 4 ++-- actors/init/Cargo.toml | 4 ++-- actors/market/Cargo.toml | 4 ++-- actors/miner/Cargo.toml | 4 ++-- actors/multisig/Cargo.toml | 4 ++-- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 4 ++-- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 4 ++-- runtime/Cargo.toml | 6 +++--- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 4 ++-- 19 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b51fd9aa..c35145c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2040,9 +2040,9 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48ac0db0c676fe9663f34782d448320b0c71d5e0afb7df38914983ae4d46faa" +checksum = "0c942494dde990aeac314311bde34c787be99cab7d0836397a75556cbaa2c3e7" dependencies = [ "anyhow", "byteorder", @@ -2060,9 +2060,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.9" +version = "3.0.0-alpha.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c35ecb57fe0544b997b4378b93b53e8896f4aee50544b3b4e4a17f0ffbdca2" +checksum = "55159c44e404ab1fc9e0e2d15aa8513f71e5e4c3f38363a3f7644d5b7a7a906e" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2075,9 +2075,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.9" +version = "3.0.0-alpha.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d8c7088aaf2f51e4373bb708b97d0fd013807e565c68e5642e206744954df6" +checksum = "fcd5f36e5c1628da1a002931f96a8cc3ea8fc517a6984e8d42457786528b88f0" dependencies = [ "anyhow", "blake2b_simd", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 4a01488f4..6eef660a6 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index c1c200dc7..7596ecded 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index 7e5ae3c7f..c47233250 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -22,8 +22,8 @@ frc46_token = "1.1.0" fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" -fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_ipld_hamt = "0.6.1" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 6e178401d..28d78ec75 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 4a2a7d1eb..92f5e9c9d 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.9", optional = true } -fvm_shared = { version = "3.0.0-alpha.9", optional = true } +fvm_sdk = { version = "3.0.0-alpha.10", optional = true } +fvm_shared = { version = "3.0.0-alpha.10", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 804f78666..d87524a27 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } -fvm_ipld_hamt = "0.6.0" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" num-traits = "0.2.14" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index cf5c00515..6458cc532 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } -fvm_ipld_hamt = "0.6.0" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 80e9cc87a..5d3629f34 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -22,8 +22,8 @@ frc46_token = "1.1.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" -fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_ipld_hamt = "0.6.1" +fvm_shared = { version = "3.0.0-alpha.10", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index e15be984a..94880dbe9 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,10 +15,10 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_ipld_hamt = "0.6.0" +fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } num-traits = "0.2.14" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 9d1df63b3..e9bc2f550 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -21,8 +21,8 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" -fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_ipld_hamt = "0.6.1" +fvm_shared = { version = "3.0.0-alpha.10", 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 b7758d43b..6eb4a086e 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 7ea9b084a..113539a2f 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } -fvm_ipld_hamt = "0.6.0" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 0159f6a9c..515d88e8a 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 905347527..a06c47a6a 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index d298d3baa..45fc1583d 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -22,8 +22,8 @@ frc42_dispatch = "1.0.0" frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" -fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_ipld_hamt = "0.6.1" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index d68470316..57d718360 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] -fvm_ipld_hamt = "0.6.0" +fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.9", optional = true } +fvm_sdk = { version = "3.0.0-alpha.10", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index a6000e262..644a085bd 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 9c5be5fb6..394449a6e 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -36,8 +36,8 @@ frc46_token = "1.1.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } -fvm_ipld_hamt = "0.6.0" -fvm_shared = { version = "3.0.0-alpha.9", default-features = false } +fvm_ipld_hamt = "0.6.1" +fvm_shared = { version = "3.0.0-alpha.10", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From b1ba61053de2ceaddd5116e87823d20a8f5e38d7 Mon Sep 17 00:00:00 2001 From: raulk Date: Tue, 15 Nov 2022 23:54:06 +0000 Subject: [PATCH 112/339] EVM runtime: implement LOG{0..4} opcodes by emitting actor events (FIP-0049) (#839) --- Cargo.lock | 30 ++-- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/datacap/src/lib.rs | 10 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- .../evm/src/interpreter/instructions/log.rs | 57 ++++++-- actors/evm/tests/events.rs | 133 ++++++++++++++++++ actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- runtime/src/runtime/fvm.rs | 8 +- runtime/src/runtime/mod.rs | 4 + runtime/src/test_utils.rs | 25 ++++ state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 6 + 26 files changed, 262 insertions(+), 51 deletions(-) create mode 100644 actors/evm/tests/events.rs diff --git a/Cargo.lock b/Cargo.lock index c35145c91..a27b088f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,7 +1770,7 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#93f90c3cd340a2396ee2e0344fe09bb476362a92" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" dependencies = [ "frc42_hasher", "frc42_macros", @@ -1783,7 +1783,7 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#93f90c3cd340a2396ee2e0344fe09bb476362a92" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" dependencies = [ "fvm_sdk", "fvm_shared", @@ -1793,7 +1793,7 @@ dependencies = [ [[package]] name = "frc42_macros" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#93f90c3cd340a2396ee2e0344fe09bb476362a92" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" dependencies = [ "blake2b_simd", "frc42_hasher", @@ -1805,7 +1805,7 @@ dependencies = [ [[package]] name = "frc46_token" version = "1.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#93f90c3cd340a2396ee2e0344fe09bb476362a92" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" dependencies = [ "anyhow", "cid", @@ -1954,7 +1954,7 @@ dependencies = [ [[package]] name = "fvm_actor_utils" version = "0.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#93f90c3cd340a2396ee2e0344fe09bb476362a92" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" dependencies = [ "anyhow", "cid", @@ -2060,9 +2060,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.10" +version = "3.0.0-alpha.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55159c44e404ab1fc9e0e2d15aa8513f71e5e4c3f38363a3f7644d5b7a7a906e" +checksum = "2ee761116262c35151071675ec3345d821b0b90143616dec348c74e46116e150" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2075,11 +2075,12 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.10" +version = "3.0.0-alpha.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcd5f36e5c1628da1a002931f96a8cc3ea8fc517a6984e8d42457786528b88f0" +checksum = "d66b85f8971273def3bb366008319f8dc855be3172210c3b83b7b20353709b29" dependencies = [ "anyhow", + "bitflags", "blake2b_simd", "byteorder", "cid", @@ -2295,9 +2296,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" dependencies = [ "http", "hyper", @@ -2444,9 +2445,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] [[package]] name = "kv-log-macro" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 6eef660a6..772dacf3c 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 7596ecded..47c7ca8e0 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index c47233250..269743985 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index 68c68081f..80a1b5cc4 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -390,15 +390,19 @@ where let res = self.rt.send(to, method, params.clone(), value.clone()); let rec = match res { - Ok(bytes) => { - Receipt { exit_code: ExitCode::OK, return_data: bytes, gas_used: fake_gas_used } - } + Ok(bytes) => Receipt { + exit_code: ExitCode::OK, + return_data: bytes, + gas_used: fake_gas_used, + events_root: None, + }, Err(ae) => { info!("datacap messenger failed: {}", ae.msg()); Receipt { exit_code: ae.exit_code(), return_data: RawBytes::default(), gas_used: fake_gas_used, + events_root: None, } } }; diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 28d78ec75..e33134c71 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index 92f5e9c9d..ee1aaf3a4 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.10", optional = true } -fvm_shared = { version = "3.0.0-alpha.10", optional = true } +fvm_sdk = { version = "3.0.0-alpha.11", optional = true } +fvm_shared = { version = "3.0.0-alpha.11", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index d87524a27..b36f73c33 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs index efa3c60d6..fd72ea910 100644 --- a/actors/evm/src/interpreter/instructions/log.rs +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -1,28 +1,57 @@ +use crate::interpreter::instructions::memory::get_memory_region; +use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; +use fvm_shared::event::{Entry, Flags}; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, }; -#[cfg(debug_assertions)] -pub fn log( - _state: &mut ExecutionState, - _system: &System, - _num_topics: usize, -) -> Result<(), StatusCode> { - todo!("unimplemented"); -} +/// The event key for the Ethereum log data. +const EVENT_DATA_KEY: &str = "data"; + +/// The event keys for the Ethereum log topics. +const EVENT_TOPIC_KEYS: &[&str] = &["topic1", "topic2", "topic3", "topic4"]; -#[cfg(not(debug_assertions))] #[inline] pub fn log( state: &mut ExecutionState, - _system: &System, + system: &System, num_topics: usize, ) -> Result<(), StatusCode> { - // TODO: Right now, we just drop everything. But we implement this in production anyways so - // things work. - for _ in 0..num_topics { - state.stack.pop(); + // 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 mem_index = state.stack.pop(); + let size = state.stack.pop(); + let region = get_memory_region(&mut state.memory, mem_index, size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; + + // 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 key in EVENT_TOPIC_KEYS.iter().take(num_topics) { + let topic = state.stack.pop(); + let entry = Entry { + flags: Flags::FLAG_INDEXED_VALUE, + key: (*key).to_owned(), + value: to_vec(&topic)?.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_VALUE, + key: EVENT_DATA_KEY.to_owned(), + value: RawBytes::serialize(BytesSer(&data))?, + }; + entries.push(entry); + } + + system.rt.emit_event(&entries.into())?; + Ok(()) } diff --git a/actors/evm/tests/events.rs b/actors/evm/tests/events.rs new file mode 100644 index 000000000..0391418d3 --- /dev/null +++ b/actors/evm/tests/events.rs @@ -0,0 +1,133 @@ +mod asm; + +use fvm_ipld_encoding::{to_vec, RawBytes}; +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_VALUE, + key: "data".to_string(), + value: to_vec(&RawBytes::from( + [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), + )) + .unwrap() + .into(), + }], + }); + 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_VALUE, + key: "topic1".to_string(), + value: to_vec(&RawBytes::from([0x11, 0x11].to_vec())).unwrap().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_VALUE, + key: "topic2".to_string(), + value: to_vec(&RawBytes::from([0x22, 0x22].to_vec())).unwrap().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_VALUE, + key: "topic3".to_string(), + value: to_vec(&RawBytes::from([0x33, 0x33].to_vec())).unwrap().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_VALUE, + key: "topic4".to_string(), + value: to_vec(&RawBytes::from([0x44, 0x44].to_vec())).unwrap().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_VALUE, + key: "data".to_string(), + value: to_vec(&RawBytes::from( + [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), + )) + .unwrap() + .into(), + }, + ], + }); + util::invoke_contract(&mut rt, &contract_params); + + rt.verify(); +} diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 6458cc532..6e60068d3 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 5d3629f34..be2b4ec2d 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -23,7 +23,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 94880dbe9..5c26de0d7 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index e9bc2f550..84451c7a3 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", 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 6eb4a086e..8a9d7fe4f 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 113539a2f..5377d7500 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 515d88e8a..51b57fccd 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index a06c47a6a..901f2b1f6 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 45fc1583d..6dc89664a 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 57d718360..e410f58e4 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.10", optional = true } +fvm_sdk = { version = "3.0.0-alpha.11", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 9c6d95e59..713aee76a 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -13,6 +13,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::{ @@ -31,7 +32,7 @@ use crate::runtime::{ ActorCode, ConsensusFault, DomainSeparationTag, MessageInfo, Policy, Primitives, RuntimePolicy, Verifier, }; -use crate::{actor_error, ActorError, Runtime}; +use crate::{actor_error, ActorError, AsActorError, Runtime}; /// A runtime that bridges to the FVM environment through the FVM SDK. pub struct FvmRuntime { @@ -420,6 +421,11 @@ where fn tipset_cid(&self, epoch: i64) -> Option { fvm::network::tipset_cid(epoch).ok() } + + fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { + fvm::event::emit_event(event) + .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to emit event") + } } impl Primitives for FvmRuntime diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 61229416f..1a15d61e6 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -12,6 +12,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::{ @@ -240,6 +241,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The hash of on of the last 256 blocks fn tipset_cid(&self, epoch: i64) -> Option; + + /// Emits an event denoting that something externally noteworthy has ocurred. + fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError>; } /// Message information available to the actor about executing message. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index e72efd6a7..25b8f1bc4 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -42,6 +42,7 @@ use crate::runtime::{ Verifier, EMPTY_ARR_CID, }; use crate::{actor_error, ActorError}; +use fvm_shared::event::ActorEvent; use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; lazy_static::lazy_static! { @@ -188,6 +189,7 @@ pub struct Expectations { pub expect_replica_verify: Option, pub expect_gas_charge: VecDeque, pub expect_gas_available: VecDeque, + pub expect_emitted_events: VecDeque, } impl Expectations { @@ -287,6 +289,11 @@ impl Expectations { "expect_gas_charge {:?}, not received", self.expect_gas_available ); + assert!( + self.expect_emitted_events.is_empty(), + "expect_emitted_events {:?}, not received", + self.expect_emitted_events + ); } } @@ -750,6 +757,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) { @@ -1243,6 +1255,19 @@ impl Runtime for MockRuntime { Ok(expected.out) } + + 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(()) + } } impl Primitives for MockRuntime { diff --git a/state/Cargo.toml b/state/Cargo.toml index 644a085bd..a97268c16 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 394449a6e..285ad9832 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -37,7 +37,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.10", default-features = false } +fvm_shared = { version = "3.0.0-alpha.11", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 59770b977..155ebb431 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -47,6 +47,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; @@ -1063,6 +1064,11 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { fn tipset_cid(&self, _epoch: i64) -> Option { Some(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(()) + } } impl Primitives for VM<'_> { From ee12a8c1c4b19442041d7db2560bd0b738d68467 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Nov 2022 07:41:03 -0800 Subject: [PATCH 113/339] fix: correctly handle out-of-bounds cases when copying memory (#847) - CALL: truncates the return value, but doesn't zero fill. - CALLDATA, EXTCODECOPY, CODECOPY: treat input as if it were followed by infinite zeros. - RETURNDATACOPY: explicitly forbids out-of-bounds reads. We might be able to change the behavior of EXTCODECOPY and CODECOPY to match RETURNDATACOPY, but we _can't_ change CALLDATA as solidity abuses this feature for zeroing memory. So we're just going to match what the EVM does because it's safer (and because we need to implement that behavior _anyways_ for CALLDATA). fixes https://github.com/filecoin-project/ref-fvm/issues/1021 fixes https://github.com/filecoin-project/ref-fvm/issues/1024 --- .../evm/src/interpreter/instructions/call.rs | 42 +---------- .../evm/src/interpreter/instructions/ext.rs | 4 +- .../src/interpreter/instructions/memory.rs | 75 ++++++++++--------- 3 files changed, 45 insertions(+), 76 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 5b9b7eb05..8d1b54f50 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -56,26 +56,7 @@ pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(&mut state.memory, mem_index, size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; - - if let Some(region) = ®ion { - let input_len = U256::from(state.input_data.len()); - let src = core::cmp::min(input_len, input_index); - let copy_size = core::cmp::min(size, input_len - src).as_usize(); - let src = src.as_usize(); - - if copy_size > 0 { - state.memory[region.offset..region.offset + copy_size] - .copy_from_slice(&state.input_data[src..src + copy_size]); - } - - if region.size.get() > copy_size { - state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); - } - } - - Ok(()) + copy_to_memory(&mut state.memory, mem_index, size, input_index, &state.input_data, true) } #[inline] @@ -88,24 +69,7 @@ pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCod let input_index = state.stack.pop(); let size = state.stack.pop(); - let region = get_memory_region(&mut state.memory, mem_index, size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; - - if let Some(region) = region { - let src = core::cmp::min(U256::from(code.len()), input_index).as_usize(); - let copy_size = core::cmp::min(region.size.get(), code.len() - src); - - if copy_size > 0 { - state.memory[region.offset..region.offset + copy_size] - .copy_from_slice(&code[src..src + copy_size]); - } - - if region.size.get() > copy_size { - state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); - } - } - - Ok(()) + copy_to_memory(&mut state.memory, mem_index, size, input_index, code, true) } pub fn call( @@ -259,7 +223,7 @@ pub fn call( .into(); // copy return data to output region if it is non-zero - copy_to_memory(memory, output_offset, output_size, U256::zero(), &state.return_data)?; + copy_to_memory(memory, output_offset, output_size, U256::zero(), &state.return_data, false)?; stack.push(U256::from(1)); Ok(()) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index c0290fe79..77bd6dc6c 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -52,9 +52,7 @@ pub fn extcodecopy( let bytecode = get_evm_bytecode_cid(system.rt, addr).and_then(|cid| get_evm_bytecode(system.rt, &cid))?; - copy_to_memory(&mut state.memory, dest_offset, size, data_offset, bytecode.as_slice())?; - - Ok(()) + copy_to_memory(&mut state.memory, dest_offset, size, data_offset, bytecode.as_slice(), true) } pub fn get_evm_bytecode_cid(rt: &impl Runtime, addr: U256) -> Result { diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 093966aa8..695fe055e 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -73,40 +73,33 @@ pub fn copy_to_memory( dest_size: U256, data_offset: U256, data: &[u8], + zero_fill: bool, ) -> Result<(), StatusCode> { - // TODO this limits addressable output to 2G (31 bits full), - // but it is still probably too much and we should consistently limit further. - // See also https://github.com/filecoin-project/ref-fvm/issues/851 - if dest_size.bits() >= 32 { - return Err(StatusCode::InvalidMemoryAccess); - } - let output_usize = dest_size.as_usize(); + let region = get_memory_region(memory, dest_offset, dest_size) + .map_err(|_| StatusCode::InvalidMemoryAccess)?; - if data_offset.bits() >= 32 { - return Err(StatusCode::InvalidMemoryAccess); - } - let data_offset_usize = data_offset.as_usize(); - if data_offset_usize > data.len() { - return Err(StatusCode::InvalidMemoryAccess); + #[inline(always)] + fn min(a: U256, b: usize) -> usize { + if a < (b as u64) { + a.as_usize() + } else { + b + } } - if output_usize > 0 { - // Limit the size if we're copying less than the data length. - let mut copy_len = data.len() - data_offset_usize; - if output_usize < copy_len { - copy_len = output_usize; + 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]); } - let output_region = get_memory_region(memory, dest_offset, dest_size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; - let output_data = output_region - .map(|MemoryRegion { offset, size }| &mut memory[offset..][..size.get()]) - .ok_or(StatusCode::InvalidMemoryAccess)?; - - output_data - .get_mut(..copy_len) - .ok_or(StatusCode::InvalidMemoryAccess)? - .copy_from_slice(&data[data_offset_usize..][..copy_len]); + if zero_fill && region.size.get() > copy_size { + memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); + } } Ok(()) @@ -171,16 +164,28 @@ mod tests { #[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(), &[]); + let result = copy_to_memory( + &mut mem, + U256::zero(), + U256::from(1u128 << 40), + U256::zero(), + &[], + true, + ); assert_eq!(result, Err(StatusCode::InvalidMemoryAccess)); } #[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]); + 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()); } @@ -189,7 +194,8 @@ mod tests { 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); + 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); @@ -201,7 +207,8 @@ mod tests { 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); + 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); From d24598dce1e3b9b794023fe9227204e3831c13d5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Nov 2022 14:25:40 -0800 Subject: [PATCH 114/339] chore: bump cache version We ran out of space. --- .github/actions/rust-cargo-run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml index 643b872d8..9bee5bfa8 100644 --- a/.github/actions/rust-cargo-run/action.yml +++ b/.github/actions/rust-cargo-run/action.yml @@ -41,7 +41,7 @@ runs: CACHE_SKIP_SAVE: ${{ inputs.save_cache == '' || inputs.save_cache == 'false' }} with: version: v0.2.15 - shared-key: v4-${{ 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: From cd7cfce4d1ec222f889f94361bd3c73d598705b4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Nov 2022 17:08:46 -0800 Subject: [PATCH 115/339] fix: allow empty EVM code, init and runtime (#848) We can already handle _executing_ with empty bytecode, we just didn't allow construction. --- actors/evm/src/interpreter/system.rs | 13 +++++++++++-- actors/evm/src/lib.rs | 28 +++++----------------------- test_vm/tests/evm_test.rs | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 4e1593e46..25461efab 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -23,6 +23,10 @@ use { fvm_ipld_hamt::Hamt, }; +/// Maximum allowed EVM bytecode size. +/// The contract code size limit is 24kB. +const MAX_CODE_SIZE: usize = 24 << 10; + /// Platform Abstraction Layer /// that bridges the FVM world to EVM world pub struct System<'r, RT: Runtime> { @@ -175,8 +179,13 @@ impl<'r, RT: Runtime> System<'r, RT> { /// Set the bytecode. pub fn set_bytecode(&mut self, bytecode: &[u8]) -> Result { self.saved_state_root = None; - // Reject code starting with 0xEF, EIP-3541 - if bytecode.first() == Some(&0xEF) { + 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(), )); diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 446809afd..3072edde4 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -29,10 +29,6 @@ use { #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EvmContractActor); -/// Maximum allowed EVM bytecode size. -/// The contract code size limit is 24kB. -const MAX_CODE_SIZE: usize = 24 << 10; - pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(27); const EVM_MAX_RESERVED_METHOD: u64 = 1023; @@ -101,16 +97,11 @@ impl EvmContractActor { EthAddress(subaddr) }; - if params.initcode.len() > MAX_CODE_SIZE { - return Err(ActorError::illegal_argument(format!( - "EVM byte code length ({}) is exceeding the maximum allowed of {MAX_CODE_SIZE}", - params.initcode.len() - ))); - } else if params.initcode.is_empty() { - return Err(ActorError::illegal_argument("no initcode provided".into())); - } - let mut system = System::create(rt)?; + // If we have no code, save the state and return. + if params.initcode.is_empty() { + return system.flush(); + } // create a new execution context let mut exec_state = ExecutionState::new(params.creator, receiver_eth_addr, Bytes::new()); @@ -129,16 +120,7 @@ impl EvmContractActor { if exec_status.reverted { Err(ActorError::unchecked(EVM_CONTRACT_REVERTED, "constructor reverted".to_string())) } else if exec_status.status_code == StatusCode::Success { - if exec_status.output_data.is_empty() { - return Err(ActorError::unspecified( - "EVM constructor returned empty contract".to_string(), - )); - } - // constructor ran to completion successfully and returned - // the resulting bytecode. - let contract_bytecode = exec_status.output_data; - - system.set_bytecode(&contract_bytecode)?; + system.set_bytecode(&exec_status.output_data)?; system.flush() } else if let StatusCode::ActorError(e) = exec_status.status_code { Err(e) diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 04efeab0c..1627fd2d4 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -83,6 +83,29 @@ fn test_evm_lifecycle() { assert_eq!(0, evm_ret, "expected contract to return 0 on success"); } +#[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::Create2 as u64, + fil_actor_eam::Create2Params { initcode: vec![], salt: [0u8; 32] }, + ) + .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() { From bdeb3bc79a359295e8e533d73bc9016ac96f8763 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 17 Nov 2022 11:17:02 -0800 Subject: [PATCH 116/339] chore: update fvm sdk (#854) Includes refactored network/message context syscalls. --- Cargo.lock | 8 ++++---- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 ++-- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 ++-- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a27b088f9..c0f0c240a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2060,9 +2060,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.11" +version = "3.0.0-alpha.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee761116262c35151071675ec3345d821b0b90143616dec348c74e46116e150" +checksum = "8679ff3d1f3c306b4974e53aae3f22bd007201db80fbeeb8ba5d1a727478ae29" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2075,9 +2075,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.11" +version = "3.0.0-alpha.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66b85f8971273def3bb366008319f8dc855be3172210c3b83b7b20353709b29" +checksum = "9d9fcc1caa8878cffebb092aeeab04bf559e03fe4d8e5c3c2d7805b589669da7" dependencies = [ "anyhow", "bitflags", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 772dacf3c..4c0b717ca 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 47c7ca8e0..d318dc8bc 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index 269743985..86483f606 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index e33134c71..c8a6b225e 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index ee1aaf3a4..f4bade906 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.11", optional = true } -fvm_shared = { version = "3.0.0-alpha.11", optional = true } +fvm_sdk = { version = "3.0.0-alpha.13", optional = true } +fvm_shared = { version = "3.0.0-alpha.12", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index b36f73c33..ec2fe9be2 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 6e60068d3..0d7a3ca71 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index be2b4ec2d..5c18f91c7 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -23,7 +23,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 5c26de0d7..446d8112a 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 84451c7a3..7f56fe138 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", 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 8a9d7fe4f..2fed3d2d2 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 5377d7500..2a48747ca 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 51b57fccd..75705688d 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 901f2b1f6..c567ec9ff 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 6dc89664a..2120bc220 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index e410f58e4..4bae6eaad 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.11", optional = true } +fvm_sdk = { version = "3.0.0-alpha.13", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index a97268c16..253a8ae59 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 285ad9832..e8aa09358 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -37,7 +37,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.11", default-features = false } +fvm_shared = { version = "3.0.0-alpha.12", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From 1b11df4b399550753a4105f45f58bc07015af2a3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 18 Nov 2022 07:59:44 -0800 Subject: [PATCH 117/339] chore: upgrade fvm_sdk for syscall changes (#862) --- Cargo.lock | 36 ++++++++++++++++++------------------ actors/embryo/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- runtime/src/runtime/fvm.rs | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0f0c240a..0551e8556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,7 +314,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -584,7 +584,7 @@ dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.5", + "digest 0.10.6", "getrandom", "hmac", "k256", @@ -621,7 +621,7 @@ dependencies = [ "base64 0.12.3", "bech32", "blake2", - "digest 0.10.5", + "digest 0.10.6", "generic-array 0.14.6", "hex", "ripemd", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -950,7 +950,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.5", + "digest 0.10.6", "ff", "generic-array 0.14.6", "group", @@ -978,7 +978,7 @@ checksum = "6f65b750ac950f2f825b36d08bef4cda4112e19a7b1a68f6e2bb499413e12440" dependencies = [ "aes", "ctr", - "digest 0.10.5", + "digest 0.10.6", "hex", "hmac", "pbkdf2 0.11.0", @@ -2060,9 +2060,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.13" +version = "3.0.0-alpha.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8679ff3d1f3c306b4974e53aae3f22bd007201db80fbeeb8ba5d1a727478ae29" +checksum = "6f2af95e6a8d41b4335686f83897756cb984299c789803325a1a62abcb5df2e0" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2233,7 +2233,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -2617,7 +2617,7 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.5", + "digest 0.10.6", "multihash-derive", "ripemd", "serde", @@ -2863,7 +2863,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -2872,7 +2872,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", "hmac", "password-hash 0.4.2", "sha2 0.10.6", @@ -3204,7 +3204,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3486,7 +3486,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3522,7 +3522,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3531,7 +3531,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", "keccak", ] @@ -3541,7 +3541,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", "rand_core", ] diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index f4bade906..ae5429573 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.13", optional = true } +fvm_sdk = { version = "3.0.0-alpha.14", optional = true } fvm_shared = { version = "3.0.0-alpha.12", optional = true } [features] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 4bae6eaad..a05f34ace 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.13", optional = true } +fvm_sdk = { version = "3.0.0-alpha.14", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 713aee76a..9b11a317c 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -366,7 +366,7 @@ where } fn new_actor_address(&mut self) -> Result { - Ok(fvm::actor::new_actor_address()) + Ok(fvm::actor::next_actor_address()) } fn create_actor( From 31acd913040ecbb7b6e2970876e455ed331c2b07 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 22 Nov 2022 15:58:01 -0800 Subject: [PATCH 118/339] EVM: Move `CALLACTOR` into a precompile (#861) * wip callactor precompile (very broken) * fill out logic a bit * rustfmt * pass in context through to precompiles * exit with error on static call to callactor, array chunks abstraction * forgot increment cursor... * actually use gas from call, assert unused bits in randomness params are zeroed * pass precompile context to all precompiles add a chunked parameter reader * improve parameter reader, check for readonly, new precompiles only read u32 for dynamicly sized bytes * update sdk * review changes, fix fn name in runtime * nit: don't comment out code --- .../evm/src/interpreter/instructions/call.rs | 14 +- actors/evm/src/interpreter/precompiles.rs | 555 ++++++++++++++---- actors/evm/src/interpreter/uints.rs | 7 + 3 files changed, 463 insertions(+), 113 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 8d1b54f50..973ea3ca5 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,6 +1,8 @@ use fvm_ipld_encoding::{BytesDe, BytesSer}; use fvm_shared::{address::Address, METHOD_SEND}; +use crate::interpreter::precompiles::PrecompileContext; + use { super::memory::{copy_to_memory, get_memory_region}, crate::interpreter::address::EthAddress, @@ -81,7 +83,7 @@ pub fn call( // NOTE gas is currently ignored as FVM's send doesn't allow the caller to specify a gas // limit (external invocation gas limit applies). This may changed in the future. - let (_gas, dst, value, input_offset, input_size, output_offset, output_size) = match kind { + let (gas, dst, value, input_offset, input_size, output_offset, output_size) = match kind { CallKind::Call | CallKind::CallCode => ( stack.pop(), stack.pop(), @@ -120,9 +122,15 @@ pub fn call( }; if precompiles::Precompiles::::is_precompile(&dst) { + let context = PrecompileContext { + is_static: matches!(kind, CallKind::StaticCall) || system.readonly, + gas, + value, + }; + // TODO: DO NOT FAIL!!! - precompiles::Precompiles::call_precompile(system.rt, dst, input_data) - .map_err(|_| StatusCode::PrecompileFailure)? + precompiles::Precompiles::call_precompile(system.rt, dst, input_data, context) + .map_err(StatusCode::from)? } else { let call_result = match kind { CallKind::Call | CallKind::StaticCall => { diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 0c3e3d425..c2bd8b183 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -1,8 +1,9 @@ -use std::{borrow::Cow, convert::TryInto, marker::PhantomData}; +use std::{borrow::Cow, convert::TryInto, marker::PhantomData, slice::ChunksExact}; -use super::U256; +use super::{StatusCode, U256}; use fil_actors_runtime::runtime::{Primitives, Runtime}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::{ address::Address, bigint::BigUint, @@ -10,6 +11,7 @@ use fvm_shared::{ hash::SupportedHashes, signature::{SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, }, + econ::TokenAmount, }; use num_traits::FromPrimitive; use num_traits::{One, Zero}; @@ -22,6 +24,192 @@ lazy_static::lazy_static! { pub(crate) static ref SECP256K1: BigUint = BigUint::from_bytes_be(&hex_literal::hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); } +/// ensures top bits are zeroed +fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { + if src[..S] != [0u8; S] { + Err(PrecompileError::InvalidInput) + } else { + Ok(()) + } +} + +struct Parameter(pub T); + +impl<'a> TryFrom<&'a [u8; 64]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 64]) -> Result { + let x = Fq::from_u256(U256::from_big_endian(&value[0..32]).into())?; + let y = Fq::from_u256(U256::from_big_endian(&value[32..64]).into())?; + + Ok(if x.is_zero() && y.is_zero() { + Parameter(G1::zero()) + } else { + Parameter(AffineG1::new(x, y)?.into()) + }) + } +} + +impl<'a> From<&'a [u8; 32]> for Parameter<[u8; 32]> { + fn from(value: &'a [u8; 32]) -> Self { + Self(*value) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<28>(value)?; + // Type ensures our remaining len == 4 + Ok(Self(u32::from_be_bytes(value[28..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<28>(value)?; + // Type ensures our remaining len == 4 + Ok(Self(i32::from_be_bytes(value[28..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<31>(value)?; + Ok(Self(value[31])) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<24>(value)?; + // Type ensures our remaining len == 8 + Ok(Self(u64::from_be_bytes(value[24..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<24>(value)?; + // Type ensures our remaining len == 8 + Ok(Self(i64::from_be_bytes(value[24..].try_into().unwrap()))) + } +} + +impl<'a> From<&'a [u8; 32]> for Parameter { + fn from(value: &'a [u8; 32]) -> Self { + Self(U256::from_big_endian(value)) + } +} + +type U256Reader<'a> = PaddedChunks<'a, u8, 32>; + +// will be nicer with https://github.com/rust-lang/rust/issues/74985 +/// Wrapper around `ChunksExact` that pads instead of overflowing. +/// Also provides a nice API interface for reading Parameters from input +struct PaddedChunks<'a, T: Sized + Copy, const CHUNK_SIZE: usize> { + slice: &'a [T], + chunks: ChunksExact<'a, T>, + exhausted: bool, +} + +impl<'a, T: Sized + Copy, const CHUNK_SIZE: usize> PaddedChunks<'a, T, CHUNK_SIZE> { + pub(super) fn new(slice: &'a [T]) -> Self { + Self { slice, chunks: slice.chunks_exact(CHUNK_SIZE), exhausted: false } + } + + pub fn next(&mut self) -> Option<&[T; CHUNK_SIZE]> { + self.chunks.next().map(|s| s.try_into().unwrap()) + } + + pub fn next_padded(&mut self) -> [T; CHUNK_SIZE] + where + T: Default, + { + if self.chunks.len() > 0 { + self.next().copied().unwrap_or([T::default(); CHUNK_SIZE]) + } else if self.exhausted() { + [T::default(); CHUNK_SIZE] + } else { + self.exhausted = true; + let mut buf = [T::default(); CHUNK_SIZE]; + let remainder = self.chunks.remainder(); + buf[..remainder.len()].copy_from_slice(remainder); + buf + } + } + + pub fn exhausted(&self) -> bool { + self.exhausted + } + + pub fn remaining_len(&self) -> usize { + if self.exhausted { + 0 + } else { + self.chunks.len() * CHUNK_SIZE + self.chunks.remainder().len() + } + } + + pub fn chunks_read(&self) -> usize { + let total_chunks = self.slice.len() / CHUNK_SIZE; + let unread_chunks = self.chunks.len(); + total_chunks - unread_chunks + } + + // remaining unpadded slice of unread items + pub fn remaining_slice(&self) -> &[T] { + let start = self.slice.len() - self.remaining_len(); + &self.slice[start..] + } + + // // tries to read an unpadded and exact (aligned) parameter + #[allow(unused)] + pub fn next_param(&mut self) -> Result + where + Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, + { + Parameter::::try_from(self.next().ok_or(PrecompileError::IncorrectInputSize)?) + .map(|a| a.0) + } + + // tries to read a parameter with padding + pub fn next_param_padded(&mut self) -> Result + where + T: Default, + Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, + { + Parameter::::try_from(&self.next_padded()).map(|a| a.0) + } + + #[allow(unused)] + pub fn next_into_param_padded(&mut self) -> V + where + T: Default, + Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, + { + Parameter::::from(&self.next_padded()).0 + } + + // read a parameter with padding + pub fn next_into_param(&mut self) -> Result + where + T: Default, + Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, + { + self.next().map(|p| Parameter::::from(p).0).ok_or(PrecompileError::IncorrectInputSize) + } +} + #[derive(Debug)] pub enum PrecompileError { EcErr(CurveError), @@ -29,6 +217,16 @@ pub enum PrecompileError { InvalidInput, // TODO merge with below? IncorrectInputSize, OutOfGas, + CallActorError(StatusCode), +} + +impl From for StatusCode { + fn from(src: PrecompileError) -> Self { + match src { + PrecompileError::CallActorError(e) => e, + _ => StatusCode::PrecompileFailure, + } + } } impl From for PrecompileError { @@ -49,11 +247,19 @@ impl From for PrecompileError { } } -pub type PrecompileFn = fn(&RT, &[u8]) -> PrecompileResult; +#[derive(Debug, PartialEq, Eq, Default)] +pub struct PrecompileContext { + pub is_static: bool, + pub gas: U256, + pub value: U256, +} + +// really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy +pub type PrecompileFn = fn(&RT, &[u8], PrecompileContext) -> PrecompileResult; pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec /// Generates a list of precompile smart contracts, index + 1 is the address (another option is to make an enum) -const fn gen_precompiles() -> [PrecompileFn; 13] { +const fn gen_precompiles() -> [PrecompileFn; 14] { [ ec_recover, // ecrecover 0x01 sha256, // SHA2-256 0x02 @@ -69,23 +275,31 @@ const fn gen_precompiles() -> [PrecompileFn; 13] { lookup_address, // resolve_address 0x0b get_actor_code_cid, // get code cid 0x0c get_randomness, // rand 0x0d + call_actor, // call_actor 0x0e ] } pub struct Precompiles(PhantomData); impl Precompiles { - const PRECOMPILES: [PrecompileFn; 13] = gen_precompiles(); + const PRECOMPILES: [PrecompileFn; 14] = gen_precompiles(); const MAX_PRECOMPILE: U256 = { let mut limbs = [0u64; 4]; limbs[0] = Self::PRECOMPILES.len() as u64; U256(limbs) }; - pub fn call_precompile(runtime: &RT, precompile_addr: U256, input: &[u8]) -> PrecompileResult { - Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](runtime, input) + // Precompile Context will be flattened to None if not calling the call_actor precompile + pub fn call_precompile( + runtime: &RT, + precompile_addr: U256, + input: &[u8], + context: PrecompileContext, + ) -> PrecompileResult { + Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](runtime, input, context) } + #[inline] pub fn is_precompile(addr: &U256) -> bool { !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE } @@ -105,9 +319,13 @@ fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u /// Read right padded BE encoded low u64 ID address from a u256 word /// returns encoded CID or an empty array if actor not found -fn get_actor_code_cid(rt: &RT, input: &[u8]) -> PrecompileResult { - let id_bytes = read_right_pad(input, 32); - let id = U256::from_big_endian(&id_bytes).low_u64(); +fn get_actor_code_cid( + rt: &RT, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); + let id = Parameter::::try_from(&id_bytes)?.0; Ok(rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) } @@ -118,16 +336,15 @@ fn get_actor_code_cid(rt: &RT, input: &[u8]) -> PrecompileResult { /// | randomness_type | U256 - low i32: `Chain`(0) OR `Beacon`(1) | /// | personalization | U256 - low i64 | /// | randomness_epoch | U256 - low i64 | -/// | entropy_length | U256 - low u64 | +/// | entropy_length | U256 - low u32 | /// | entropy | input\[32..] (right padded)| /// /// any bytes in between values are ignored /// /// Returns empty array if invalid randomness type /// Errors if unable to fetch randomness -fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { - // 32 * 4 = 128 - let input = read_right_pad(input, 128); +fn get_randomness(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { + let mut input_params = U256Reader::new(input); #[derive(num_derive::FromPrimitive)] #[repr(i32)] @@ -136,18 +353,14 @@ fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { Beacon = 1, } - let randomness_word = &input[0..32]; - let personalization_bytes = &input[32..64]; - let epoch_bytes = &input[64..96]; - let entropy_len_bytes = &input[96..128]; + let randomness_type = RandomnessType::from_i32(input_params.next_param_padded::()?); + let personalization = input_params.next_param_padded::()?; + let rand_epoch = input_params.next_param_padded::()?; + let entropy_len = input_params.next_param_padded::()?; - let randomness_type = - RandomnessType::from_i32(i32::from_be_bytes(randomness_word[28..32].try_into().unwrap())); - let personalization = i64::from_be_bytes(personalization_bytes[24..32].try_into().unwrap()); - let rand_epoch = i64::from_be_bytes(epoch_bytes[24..32].try_into().unwrap()); - let entropy_len = u64::from_be_bytes(entropy_len_bytes[24..32].try_into().unwrap()); + debug_assert_eq!(input_params.chunks_read(), 4); - let entropy = read_right_pad(&input[128..], entropy_len as usize); + let entropy = read_right_pad(input_params.remaining_slice(), entropy_len as usize); let randomness = match randomness_type { Some(RandomnessType::Chain) => rt @@ -162,11 +375,11 @@ fn get_randomness(rt: &RT, input: &[u8]) -> PrecompileResult { randomness.map_err(|_| PrecompileError::InvalidInput) } -/// Read right padded BE encoded low u64 ID address from a u256 word +/// Read BE encoded low u64 ID address from a u256 word /// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found -fn lookup_address(rt: &RT, input: &[u8]) -> PrecompileResult { - let id_bytes = read_right_pad(input, 32); - let id = U256::from_big_endian(&id_bytes).low_u64(); +fn lookup_address(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { + let mut id_bytes = U256Reader::new(input); + let id = id_bytes.next_param_padded::()?; let address = rt.lookup_address(id); let ab = match address { @@ -179,34 +392,144 @@ fn lookup_address(rt: &RT, input: &[u8]) -> PrecompileResult { /// Reads a FIL encoded address /// Resolves a FIL encoded address into an ID address /// returns BE encoded u64 or empty array if nothing found -fn resolve_address(rt: &RT, input: &[u8]) -> PrecompileResult { - let addr = match Address::from_bytes(input) { +fn resolve_address(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { + let mut input_params = U256Reader::new(input); + + let len = input_params.next_param_padded::()? as usize; + let addr = match Address::from_bytes(&read_right_pad(input_params.remaining_slice(), len)) { Ok(o) => o, Err(_) => return Ok(Vec::new()), }; Ok(rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) } +/// Errors: +/// TODO should just give 0s? +/// - `IncorrectInputSize` if offset is larger than total input length +/// - `InvalidInput` if supplied address bytes isnt a filecoin address +/// +/// Returns: +/// +/// `[int256 exit_code, uint codec, uint offset, uint size, []bytes ]` +/// +/// for exit_code: +/// - negative values are system errors +/// - positive are user errors (from the called actor) +/// - 0 is success +pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> PrecompileResult { + // TODO Until we support readonly (static) calls at the fvm level, we disallow callactor + // when in static mode as it is sticky and there are no guarantee of preserving the + // static invariant + if ctx.is_static { + return Err(PrecompileError::CallActorError(StatusCode::StaticModeViolation)); + } + + // ----- Input Parameters ------- + + let mut input_params = U256Reader::new(input); + + let method: u64 = input_params.next_param_padded()?; + let codec: u64 = input_params.next_param_padded()?; + // TODO only CBOR for now + if codec != fvm_ipld_encoding::DAG_CBOR { + return Err(PrecompileError::InvalidInput); + } + + let address_size = input_params.next_param_padded::()? as usize; + let send_data_size = input_params.next_param_padded::()? as usize; + + // ------ Begin Call ------- + + let result = { + // REMOVEME: closes https://github.com/filecoin-project/ref-fvm/issues/1018 + + let start = input_params.remaining_slice(); + let bytes = read_right_pad(start, send_data_size + address_size); + + let input_data = &bytes[..send_data_size]; + let address = &bytes[send_data_size..address_size]; + let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; + + // TODO passing underlying gas into send when implemented in FVM + rt.send( + &address, + method, + RawBytes::from(input_data.to_vec()), + TokenAmount::from(&ctx.value), + ) + }; + + // ------ 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(ae) => { + // TODO handle revert + // TODO https://github.com/filecoin-project/ref-fvm/issues/1020 + // put error number from call into revert + let exit_code = U256::from(ae.exit_code().value()); + + // no return only exit code + (exit_code, RawBytes::default()) + } + Ok(ret) => (U256::zero(), ret), + }; + + const NUM_OUTPUT_PARAMS: usize = 3; + + // codec of return data + // TODO hardcoded to CBOR for now + let codec = U256::from(fvm_ipld_encoding::DAG_CBOR); + let offset = U256::from(NUM_OUTPUT_PARAMS * 32); + let size = U256::from(data.len()); + + let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS * 32 + data.len()); + output.extend_from_slice(&exit_code.to_bytes()); + output.extend_from_slice(&codec.to_bytes()); + output.extend_from_slice(&offset.to_bytes()); + output.extend_from_slice(&size.to_bytes()); + // NOTE: + // we dont pad out to 32 bytes here, the idea being that users will already be in the "everythig is bytes" mode + // and will want re-pack align and whatever else by themselves + output.extend_from_slice(&data); + output + }; + + Ok(output) +} + +// ---------------- Normal EVM Precompiles ------------------ + /// recover a secp256k1 pubkey from a hash, recovery byte, and a signature -fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { - let input = read_right_pad(input, 128); +fn ec_recover(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { + let mut input_params = U256Reader::new(input); - let hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE] = input[0..32].try_into().unwrap(); - let r = BigUint::from_bytes_be(&input[64..96]); - let s = BigUint::from_bytes_be(&input[96..128]); - let recovery_byte = input[63]; + let hash: [u8; SECP_SIG_MESSAGE_HASH_SIZE] = input_params.next_padded(); + let recovery_byte = input_params.next_param_padded::(); + let r = input_params.next_padded(); + let s = input_params.next_padded(); + let big_r = BigUint::from_bytes_be(&r); + let big_s = BigUint::from_bytes_be(&s); // recovery byte is a single byte value but is represented with 32 bytes, sad - let v = if input[32..63] == [0u8; 31] && matches!(recovery_byte, 27 | 28) { - recovery_byte - 27 - } else { - return Ok(Vec::new()); + let v = match recovery_byte { + Ok(re) => { + if matches!(re, 27 | 28) { + re - 27 + } else { + return Ok(Vec::new()); + } + } + _ => return Ok(Vec::new()), }; - let valid = if r <= BigUint::one() || s <= BigUint::one() { + let valid = if big_r <= BigUint::one() || big_s <= BigUint::one() { false } else { - r <= *SECP256K1 && s <= *SECP256K1 && (v == 0 || v == 1) + big_r <= *SECP256K1 && big_s <= *SECP256K1 && (v == 0 || v == 1) }; if !valid { @@ -214,10 +537,11 @@ fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { } let mut sig: [u8; SECP_SIG_LEN] = [0u8; 65]; - sig[..64].copy_from_slice(&input[64..128]); + sig[..32].copy_from_slice(&r); + sig[32..64].copy_from_slice(&s); sig[64] = v; - let pubkey = if let Ok(key) = rt.recover_secp_public_key(hash, &sig) { + let pubkey = if let Ok(key) = rt.recover_secp_public_key(&hash, &sig) { key } else { return Ok(Vec::new()); @@ -230,23 +554,23 @@ fn ec_recover(rt: &RT, input: &[u8]) -> PrecompileResult { } /// hash with sha2-256 -fn sha256(rt: &RT, input: &[u8]) -> PrecompileResult { +fn sha256(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Sha2_256, input)) } /// hash with ripemd160 -fn ripemd160(rt: &RT, input: &[u8]) -> PrecompileResult { +fn ripemd160(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { Ok(rt.hash(SupportedHashes::Ripemd160, input)) } /// data copy -fn identity(_: &RT, input: &[u8]) -> PrecompileResult { +fn identity(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { Ok(Vec::from(input)) } // https://eips.ethereum.org/EIPS/eip-198 /// modulus exponent a number -fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { +fn modexp(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { let input = read_right_pad(input, 96); // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. @@ -299,16 +623,6 @@ fn modexp(_: &RT, input: &[u8]) -> PrecompileResult { Ok(output) } -/// reads a point from 2, 32 byte coordinates with right padding -/// exits with EcErr for any failed operation -/// panics if input.len() < 64 -fn curve_point(input: &[u8]) -> Result { - let x = Fq::from_u256(U256::from_big_endian(&input[0..32]).into())?; - let y = Fq::from_u256(U256::from_big_endian(&input[32..64]).into())?; - - Ok(if x.is_zero() && y.is_zero() { G1::zero() } else { AffineG1::new(x, y)?.into() }) -} - fn curve_to_vec(curve: G1) -> Vec { AffineG1::from_jacobian(curve) .map(|product| { @@ -321,21 +635,22 @@ fn curve_to_vec(curve: G1) -> Vec { } /// add 2 points together on an elliptic curve -fn ec_add(_: &RT, input: &[u8]) -> PrecompileResult { - let input = read_right_pad(input, 128); - let point1 = curve_point(&input)?; - let point2 = curve_point(&input[64..128])?; +fn ec_add(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { + let mut input_params: PaddedChunks = PaddedChunks::new(input); + let point1 = input_params.next_param_padded()?; + let point2 = input_params.next_param_padded()?; Ok(curve_to_vec(point1 + point2)) } /// multiply a point on an elliptic curve by a scalar value -fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { +fn ec_mul(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { let input = read_right_pad(input, 96); - let point = curve_point(&input)?; + let mut input_params: PaddedChunks = PaddedChunks::new(&input); + let point = input_params.next_param_padded()?; let scalar = { - let data = U256::from_big_endian(&input[64..96]); + let data = U256::from_big_endian(&input_params.remaining_slice()[..32]); Fr::new_mul_factor(data.into()) }; @@ -343,25 +658,21 @@ fn ec_mul(_: &RT, input: &[u8]) -> PrecompileResult { } /// pairs multple groups of twisted bn curves -fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { +fn ec_pairing(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { - /// read 32 bytes (u256) from buffer or error - fn read_u256(input: &[u8], start: usize) -> Result { - let slice = input.get(start..start + 32).ok_or(PrecompileError::IncorrectInputSize)?; - Ok(U256::from_big_endian(slice)) - } + let mut i_in = U256Reader::new(input); - let x = Fq::from_u256(read_u256(input, 0)?.into())?; - let y = Fq::from_u256(read_u256(input, 32)?.into())?; + let x = Fq::from_u256(i_in.next_into_param::()?.into())?; + let y = Fq::from_u256(i_in.next_into_param::()?.into())?; let twisted_x = { - let b = Fq::from_u256(read_u256(input, 64)?.into())?; - let a = Fq::from_u256(read_u256(input, 96)?.into())?; + let b = Fq::from_u256(i_in.next_into_param::()?.into())?; + let a = Fq::from_u256(i_in.next_into_param::()?.into())?; Fq2::new(a, b) }; let twisted_y = { - let b = Fq::from_u256(read_u256(input, 128)?.into())?; - let a = Fq::from_u256(read_u256(input, 160)?.into())?; + let b = Fq::from_u256(i_in.next_into_param::()?.into())?; + let a = Fq::from_u256(i_in.next_into_param::()?.into())?; Fq2::new(a, b) }; @@ -405,7 +716,7 @@ fn ec_pairing(_: &RT, input: &[u8]) -> PrecompileResult { } /// https://eips.ethereum.org/EIPS/eip-152 -fn blake2f(_: &RT, input: &[u8]) -> PrecompileResult { +fn blake2f(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { if input.len() != 213 { return Err(PrecompileError::IncorrectInputSize); } @@ -490,7 +801,7 @@ mod tests { ); let expected = hex!("0000000000000000000000007156526fbd7a3c72969b54f64e42c10fbb768c8a"); - let res = ec_recover(&rt, input).unwrap(); + let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -501,7 +812,7 @@ mod tests { "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s ); // wrong signature - let res = ec_recover(&rt, input).unwrap(); + let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); let input = &hex!( @@ -512,7 +823,7 @@ mod tests { "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s ); // invalid recovery byte - let res = ec_recover(&rt, input).unwrap(); + let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); } @@ -524,7 +835,7 @@ mod tests { let rt = MockRuntime::default(); let expected = hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); - let res = hash(&rt, input).unwrap(); + let res = hash(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); } @@ -536,7 +847,7 @@ mod tests { let rt = MockRuntime::default(); let expected = hex!("4cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); - let res = hash(&rt, input).unwrap(); + let res = hash(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); } @@ -554,7 +865,7 @@ mod tests { let rt = MockRuntime::default(); let expected = hex!("08"); - let res = modexp(&rt, input).unwrap(); + let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -566,7 +877,7 @@ mod tests { "012345678910" // mod ); let expected = hex!("00358eac8f30"); // left padding & 230026940208 - let res = modexp(&rt, input).unwrap(); + let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let expected = hex!("000000"); // invalid values will just be [0; mod_len] @@ -579,7 +890,7 @@ mod tests { "03" // mod ); // input smaller than expected - let res = modexp(&rt, input).unwrap(); + let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -590,14 +901,16 @@ mod tests { "09" // exp ); // no mod is invalid - let res = modexp(&rt, input).unwrap(); + let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); } // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { use super::{GroupError, MockRuntime}; - use crate::interpreter::precompiles::{ec_add, ec_mul, ec_pairing, PrecompileError}; + use crate::interpreter::precompiles::{ + ec_add, ec_mul, ec_pairing, PrecompileContext, PrecompileError, + }; #[test] fn bn_add() { @@ -617,7 +930,7 @@ mod tests { 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", ) .unwrap(); - let res = ec_add(&rt, &input).unwrap(); + let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // zero sum test let input = hex::decode( @@ -634,7 +947,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_add(&rt, &input).unwrap(); + let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // no input test @@ -645,7 +958,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_add(&rt, &input).unwrap(); + let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -656,7 +969,7 @@ mod tests { 1111111111111111111111111111111111111111111111111111111111111111", ) .unwrap(); - let res = ec_add(&rt, &input); + let res = ec_add(&rt, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } @@ -677,7 +990,7 @@ mod tests { 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", ) .unwrap(); - let res = ec_mul(&rt, &input).unwrap(); + let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // out of gas test @@ -688,7 +1001,7 @@ mod tests { 0200000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input).unwrap(); + let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &vec![0; 64]); // no input test @@ -699,7 +1012,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input).unwrap(); + let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -709,7 +1022,7 @@ mod tests { 0f00000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input); + let res = ec_mul(&rt, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } @@ -738,7 +1051,7 @@ mod tests { hex::decode("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(); - let res = ec_pairing(&rt, &input).unwrap(); + let res = ec_pairing(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // out of gas test @@ -758,14 +1071,14 @@ mod tests { 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", ) .unwrap(); - let res = ec_pairing(&rt, &input).unwrap(); + let res = ec_pairing(&rt, &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(&rt, &input).unwrap(); + let res = ec_pairing(&rt, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -778,7 +1091,7 @@ mod tests { 1111111111111111111111111111111111111111111111111111111111111111", ) .unwrap(); - let res = ec_pairing(&rt, &input); + let res = ec_pairing(&rt, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); // invalid input length let input = hex::decode( @@ -789,7 +1102,7 @@ mod tests { ", ) .unwrap(); - let res = ec_pairing(&rt, &input); + let res = ec_pairing(&rt, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::IncorrectInputSize))); } } @@ -820,7 +1133,10 @@ mod tests { // } // T0 invalid input len - assert!(matches!(blake2f(&rt, &[]), Err(PrecompileError::IncorrectInputSize))); + assert!(matches!( + blake2f(&rt, &[], PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); // T1 too small let input = &hex!( @@ -831,7 +1147,10 @@ mod tests { "0000000000000000" "02" ); - assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); + assert!(matches!( + blake2f(&rt, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); // T2 too large let input = &hex!( @@ -842,7 +1161,10 @@ mod tests { "0000000000000000" "02" ); - assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); + assert!(matches!( + blake2f(&rt, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); // T3 final block indicator invalid let input = &hex!( @@ -853,7 +1175,10 @@ mod tests { "0000000000000000" "02" ); - assert!(matches!(blake2f(&rt, input), Err(PrecompileError::IncorrectInputSize))); + assert!(matches!( + blake2f(&rt, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); // outputs @@ -867,7 +1192,9 @@ mod tests { "0000000000000000" "01" ); - assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); + assert!( + matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + ); // T5 let expected = &hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); @@ -879,7 +1206,9 @@ mod tests { "0000000000000000" "01" ); - assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); + assert!( + matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + ); // T6 let expected = &hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); @@ -891,7 +1220,9 @@ mod tests { "0000000000000000" "00" ); - assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); + assert!( + matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + ); // T7 let expected = &hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); @@ -903,7 +1234,9 @@ mod tests { "0000000000000000" "01" ); - assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); + assert!( + matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + ); // T8 // NOTE: @@ -920,6 +1253,8 @@ mod tests { "0000000000000000" "01" ); - assert!(matches!(blake2f(&rt, input), Ok(v) if v == expected)); + assert!( + matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + ); } } diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 1b178787c..507309914 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -52,10 +52,17 @@ impl U256 { (self.0[3] as i64) < 0 } + /// turns a i256 value to negative #[inline(always)] pub fn i256_neg(&self) -> U256 { !*self + U256::ONE } + + pub fn to_bytes(&self) -> [u8; 32] { + let mut buf = [0u8; 32]; + self.to_big_endian(&mut buf); + buf + } } impl U512 { From 8e937257f235499ee80461b3198cb0129ddb4068 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 23 Nov 2022 06:28:08 +0200 Subject: [PATCH 119/339] EVM: Implement reverts correctly (#864) * add exit to runtime interface * propagate revert data in contract invocations * add data to ActorError * propagate data in actor errors from send * correctly handle reverts in calls * implement exit with panics in mock runtime * implement exit with panics in test vm * fix clippy * fix test vm: exit data should be in new context * use pop instead of ugly gets in ret * cosmetics * add naked revert test * rustfmt * only test naked revert in unit test nested call revert should go in integration test, really * fix callvariants contract to check reverts * really fix callvariants contract: iszero is what you want * test_vm: missing panic handler * callvariants is also testing mutation * deduplicate actor exit handler code * rustfmt * remove unnecessary replaces for exits. * fix runtime trampoline to propagate data from errors * perform contract/constructor invocation abortive returns with errors * don't synethesize exit data if it is not there * propagate error data in test_vm * rustfmt * simplify/dedup identical code --- .../evm/src/interpreter/instructions/call.rs | 46 +++++----- .../src/interpreter/instructions/control.rs | 4 +- actors/evm/src/lib.rs | 26 ++++-- actors/evm/tests/contracts/callvariants.hex | 2 +- .../evm/tests/contracts/callvariants_body.eas | 26 ++++++ actors/evm/tests/revert.rs | 34 ++++++++ runtime/src/actor_error.rs | 86 +++++++++++++++---- runtime/src/runtime/fvm.rs | 9 +- runtime/src/runtime/mod.rs | 4 + runtime/src/test_utils.rs | 36 +++++++- test_vm/src/lib.rs | 44 +++++++++- test_vm/tests/evm_test.rs | 6 +- 12 files changed, 259 insertions(+), 64 deletions(-) create mode 100644 actors/evm/tests/revert.rs diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 973ea3ca5..1899a8ded 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -14,9 +14,10 @@ use { crate::interpreter::System, crate::interpreter::U256, crate::RawBytes, - crate::{DelegateCallParams, Method, EVM_CONTRACT_REVERTED}, + crate::{DelegateCallParams, Method, EVM_CONTRACT_EXECUTION_ERROR, EVM_CONTRACT_REVERTED}, fil_actors_runtime::runtime::builtins::Type, fil_actors_runtime::runtime::Runtime, + fil_actors_runtime::ActorError, fvm_shared::econ::TokenAmount, }; @@ -113,7 +114,7 @@ pub fn call( let input_region = get_memory_region(memory, input_offset, input_size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; - state.return_data = { + 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()] @@ -128,9 +129,15 @@ pub fn call( value, }; - // TODO: DO NOT FAIL!!! - precompiles::Precompiles::call_precompile(system.rt, dst, input_data, context) - .map_err(StatusCode::from)? + match precompiles::Precompiles::call_precompile(system.rt, dst, input_data, context) + .map_err(StatusCode::from) + { + Ok(return_data) => (1, return_data), + Err(status) => { + let msg = format!("{}", status); + (0, msg.as_bytes().to_vec()) + } + } } else { let call_result = match kind { CallKind::Call | CallKind::StaticCall => { @@ -199,41 +206,34 @@ pub fn call( ) } - CallKind::CallCode => { - todo!() - } + CallKind::CallCode => Err(ActorError::unchecked( + EVM_CONTRACT_EXECUTION_ERROR, + "unsupported opcode".to_string(), + )), }; match call_result { Ok(result) => { // Support the "empty" result. We often use this to mean "returned nothing" and // it's important to support, e.g., sending to accounts. if result.is_empty() { - Vec::new() + (1, Vec::new()) } else { // TODO: support IPLD codecs #758 let BytesDe(result) = result.deserialize()?; - result + (1, result) } } - Err(ae) => { - return if ae.exit_code() == EVM_CONTRACT_REVERTED { - // reverted -- we don't have return data yet - // push failure - stack.push(U256::zero()); - Ok(()) - } else { - Err(StatusCode::from(ae)) - }; - } + Err(ae) => (0, ae.data().to_vec()), } } - } - .into(); + }; + + state.return_data = return_data.into(); // 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)?; - stack.push(U256::from(1)); + stack.push(U256::from(call_result)); Ok(()) } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index f2233ef85..14ecc03c4 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -6,8 +6,8 @@ use { #[inline] pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { - let offset = *state.stack.get(0); - let size = *state.stack.get(1); + let offset = state.stack.pop(); + let size = state.stack.pop(); if let Some(region) = super::memory::get_memory_region(&mut state.memory, offset, size) .map_err(|_| StatusCode::InvalidMemoryAccess)? diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 3072edde4..356760a30 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -29,7 +29,8 @@ use { #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EvmContractActor); -pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(27); +pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(33); +pub const EVM_CONTRACT_EXECUTION_ERROR: ExitCode = ExitCode::new(34); const EVM_MAX_RESERVED_METHOD: u64 = 1023; pub const NATIVE_METHOD_SIGNATURE: &str = "handle_filecoin_method(uint64,uint64,bytes)"; @@ -118,14 +119,21 @@ impl EvmContractActor { // TODO this does not return revert data yet, but it has correct semantics. if exec_status.reverted { - Err(ActorError::unchecked(EVM_CONTRACT_REVERTED, "constructor reverted".to_string())) + Err(ActorError::unchecked_with_data( + EVM_CONTRACT_REVERTED, + "constructor reverted".to_string(), + RawBytes::from(exec_status.output_data.to_vec()), + )) } else if exec_status.status_code == StatusCode::Success { system.set_bytecode(&exec_status.output_data)?; system.flush() } else if let StatusCode::ActorError(e) = exec_status.status_code { Err(e) } else { - Err(ActorError::unspecified("EVM constructor failed".to_string())) + Err(ActorError::unchecked( + EVM_CONTRACT_EXECUTION_ERROR, + format!("constructor execution error: {}", exec_status.status_code), + )) } } @@ -175,21 +183,21 @@ impl EvmContractActor { _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), })?; - // TODO this does not return revert data yet, but it has correct semantics. if exec_status.reverted { - return Err(ActorError::unchecked( + return Err(ActorError::unchecked_with_data( EVM_CONTRACT_REVERTED, "contract reverted".to_string(), + RawBytes::from(exec_status.output_data.to_vec()), )); } else if exec_status.status_code == StatusCode::Success { system.flush()?; } else if let StatusCode::ActorError(e) = exec_status.status_code { return Err(e); } else { - return Err(ActorError::unspecified(format!( - "EVM contract invocation failed: status: {}", - exec_status.status_code - ))); + return Err(ActorError::unchecked( + EVM_CONTRACT_EXECUTION_ERROR, + format!("contract execution error: {}", exec_status.status_code), + )); } if let Some(addr) = exec_status.selfdestroyed { diff --git a/actors/evm/tests/contracts/callvariants.hex b/actors/evm/tests/contracts/callvariants.hex index d364b864a..73355bfa1 100644 --- a/actors/evm/tests/contracts/callvariants.hex +++ b/actors/evm/tests/contracts/callvariants.hex @@ -1 +1 @@ -306000556101cf8060106000396000f360003560e01c80600114607557806002146101af5780600314609157806004146101bb578060051460ad578060061460cf578060071460ed578060081461010f578060091461012d5780600a146101495780600b1461016b5780600c1461012d5780600d1461018d5780600e1461014957600080fd5b60206000600260e01b600052600460006004356000fa60206000f35b60206000600460e01b600052600460006004356000fa60206000f35b60206000600660e01b600052602435600452602460006004356000fa60206000f35b60206000600260e01b6000526004600060006004356000f160206000f35b60206000600860e01b600052602435600452602460006004356000fa60206000f35b60206000600460e01b6000526004600060006004356000f160206000f35b60206000600260e01b600052600460006004356000f460206000f35b60206000600460e01b600052600460006004356000f460005460005260206000f35b60206000600c60e01b600052602435600452602460006004356000fa60206000f35b60206000600e60e01b600052602435600452602460006004356000fa60206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f3 \ No newline at end of file +306000556102108060106000396000f360003560e01c80600114607657806002146101e25780600314609757806004146101ee578060051460b8578060061460df57806007146101025780600814610129578060091461014c5780600a1461016d5780600b146101945780600c1461014c5780600d146101bb5780600e1461016d57600080fd5b60206000600260e01b600052600460006004356000fa156102025760206000f35b60206000600460e01b600052600460006004356000fa156102025760206000f35b60206000600660e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600260e01b6000526004600060006004356000f1156102025760206000f35b60206000600860e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600460e01b6000526004600060006004356000f1156102025760206000f35b60206000600260e01b600052600460006004356000f4156102025760206000f35b60206000600460e01b600052600460006004356000f4156102025760005460005260206000f35b60206000600c60e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600e60e01b600052602435600452602460006004356000fa156102025760206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f35b63deadbeef6000526004601cfd \ No newline at end of file diff --git a/actors/evm/tests/contracts/callvariants_body.eas b/actors/evm/tests/contracts/callvariants_body.eas index 5862b9a57..b9e183d52 100644 --- a/actors/evm/tests/contracts/callvariants_body.eas +++ b/actors/evm/tests/contracts/callvariants_body.eas @@ -22,6 +22,12 @@ revert %end +%macro check() + iszero + %push(call_failed) + jumpi +%end + # method dispatch %dispatch_begin() @@ -77,6 +83,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -97,6 +104,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -121,6 +129,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -142,6 +151,7 @@ push1 0x04 # input (dest) offset calldataload # call dest push1 0x00 # call gas (ignored) call # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -166,6 +176,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -187,6 +198,7 @@ push1 0x04 # input (dest) offset calldataload # icall dest push1 0x00 # call gas (ignored) call # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -207,6 +219,7 @@ push1 0x04 # input (dest) offset calldataload # delegate dest push1 0x00 # delegate gas (ignored) delegatecall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -227,6 +240,7 @@ push1 0x04 # input (dest) offset calldataload # delegate dest push1 0x00 # delegate gas (ignored) delegatecall # do it! +%check() push1 0x00 # slot sload # read push1 0x00 # ret offset @@ -255,6 +269,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -279,6 +294,7 @@ push1 0x04 # input (dest) offset calldataload # staticcall dest push1 0x00 # staticcall gas (ignored) staticcall # do it! +%check() push1 0x20 # ret length push1 0x00 # ret offset return @@ -310,3 +326,13 @@ mstore push1 0x20 push1 0x00 return + +# call_failed +call_failed: +jumpdest +%push(0xdeadbeef) +push1 0x00 +mstore +push1 0x04 +push1 0x1c +revert diff --git a/actors/evm/tests/revert.rs b/actors/evm/tests/revert.rs new file mode 100644 index 000000000..4af6fe810 --- /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, + &RawBytes::serialize(BytesSer(&[])).unwrap(), + ); + assert!(result.is_err()); + let e = result.unwrap_err(); + assert_eq!(e.exit_code(), evm::EVM_CONTRACT_REVERTED); + assert_eq!(e.data(), RawBytes::from(vec![0xde, 0xad, 0xbe, 0xef])); +} diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 8ef17f147..d67cd0718 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -1,5 +1,6 @@ use std::fmt::Display; +use fvm_ipld_encoding::RawBytes; use fvm_shared::error::ExitCode; use thiserror::Error; @@ -12,41 +13,47 @@ pub struct ActorError { exit_code: ExitCode, /// Message for debugging purposes, msg: String, + /// Optional exit data + data: RawBytes, } impl ActorError { /// Creates a new ActorError. This method does not check that the code is in the /// range of valid actor abort codes. pub fn unchecked(code: ExitCode, msg: String) -> Self { - Self { exit_code: code, msg } + Self { exit_code: code, msg, data: RawBytes::default() } + } + + pub fn unchecked_with_data(code: ExitCode, msg: String, data: RawBytes) -> Self { + Self { exit_code: code, msg, data } } pub fn illegal_argument(msg: String) -> Self { - Self { exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, msg } + Self { exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, msg, data: RawBytes::default() } } pub fn not_found(msg: String) -> Self { - Self { exit_code: ExitCode::USR_NOT_FOUND, msg } + Self { exit_code: ExitCode::USR_NOT_FOUND, msg, data: RawBytes::default() } } pub fn forbidden(msg: String) -> Self { - Self { exit_code: ExitCode::USR_FORBIDDEN, msg } + Self { exit_code: ExitCode::USR_FORBIDDEN, msg, data: RawBytes::default() } } pub fn insufficient_funds(msg: String) -> Self { - Self { exit_code: ExitCode::USR_INSUFFICIENT_FUNDS, msg } + Self { exit_code: ExitCode::USR_INSUFFICIENT_FUNDS, msg, data: RawBytes::default() } } pub fn illegal_state(msg: String) -> Self { - Self { exit_code: ExitCode::USR_ILLEGAL_STATE, msg } + Self { exit_code: ExitCode::USR_ILLEGAL_STATE, msg, data: RawBytes::default() } } pub fn serialization(msg: String) -> Self { - Self { exit_code: ExitCode::USR_SERIALIZATION, msg } + Self { exit_code: ExitCode::USR_SERIALIZATION, msg, data: RawBytes::default() } } pub fn unhandled_message(msg: String) -> Self { - Self { exit_code: ExitCode::USR_UNHANDLED_MESSAGE, msg } + Self { exit_code: ExitCode::USR_UNHANDLED_MESSAGE, msg, data: RawBytes::default() } } pub fn unspecified(msg: String) -> Self { - Self { exit_code: ExitCode::USR_UNSPECIFIED, msg } + Self { exit_code: ExitCode::USR_UNSPECIFIED, msg, data: RawBytes::default() } } pub fn assertion_failed(msg: String) -> Self { - Self { exit_code: ExitCode::USR_ASSERTION_FAILED, msg } + Self { exit_code: ExitCode::USR_ASSERTION_FAILED, msg, data: RawBytes::default() } } /// Returns the exit code of the error. @@ -59,6 +66,11 @@ impl ActorError { &self.msg } + /// Returns the optional data that might be associated with the error + pub fn data(&self) -> RawBytes { + self.data.clone() + } + /// Prefix error message with a string message. pub fn wrap(mut self, msg: impl AsRef) -> Self { self.msg = format!("{}: {}", msg.as_ref(), self.msg); @@ -69,7 +81,11 @@ impl ActorError { /// Converts a raw encoding error into an ErrSerialization. impl From for ActorError { fn from(e: fvm_ipld_encoding::Error) -> Self { - Self { exit_code: ExitCode::USR_SERIALIZATION, msg: e.to_string() } + Self { + exit_code: ExitCode::USR_SERIALIZATION, + msg: e.to_string(), + data: RawBytes::default(), + } } } @@ -78,7 +94,11 @@ impl From for ActorError { #[cfg(feature = "fil-actor")] impl From for ActorError { fn from(e: fvm_sdk::error::ActorDeleteError) -> Self { - Self { exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, msg: e.to_string() } + Self { + exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, + msg: e.to_string(), + data: RawBytes::default(), + } } } @@ -87,7 +107,11 @@ impl From for ActorError { #[cfg(feature = "fil-actor")] impl From for ActorError { fn from(e: fvm_sdk::error::NoStateError) -> Self { - Self { exit_code: ExitCode::USR_ILLEGAL_STATE, msg: e.to_string() } + Self { + exit_code: ExitCode::USR_ILLEGAL_STATE, + msg: e.to_string(), + data: RawBytes::default(), + } } } @@ -163,14 +187,22 @@ pub trait AsActorError: Sized { // Note: E should be std::error::Error, revert to this after anyhow:Error is no longer used. impl AsActorError for Result { fn exit_code(self, code: ExitCode) -> Result { - self.map_err(|err| ActorError { exit_code: code, msg: err.to_string() }) + self.map_err(|err| ActorError { + exit_code: code, + msg: err.to_string(), + data: RawBytes::default(), + }) } fn context_code(self, code: ExitCode, context: C) -> Result where C: Display + 'static, { - self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", context, err) }) + self.map_err(|err| ActorError { + exit_code: code, + msg: format!("{}: {}", context, err), + data: RawBytes::default(), + }) } fn with_context_code(self, code: ExitCode, f: F) -> Result @@ -178,20 +210,32 @@ impl AsActorError for Result { C: Display + 'static, F: FnOnce() -> C, { - self.map_err(|err| ActorError { exit_code: code, msg: format!("{}: {}", f(), err) }) + self.map_err(|err| ActorError { + exit_code: code, + msg: format!("{}: {}", f(), err), + data: RawBytes::default(), + }) } } impl AsActorError for Option { fn exit_code(self, code: ExitCode) -> Result { - self.ok_or_else(|| ActorError { exit_code: code, msg: "None".to_string() }) + self.ok_or_else(|| ActorError { + exit_code: code, + msg: "None".to_string(), + data: RawBytes::default(), + }) } fn context_code(self, code: ExitCode, context: C) -> Result where C: Display + 'static, { - self.ok_or_else(|| ActorError { exit_code: code, msg: context.to_string() }) + self.ok_or_else(|| ActorError { + exit_code: code, + msg: context.to_string(), + data: RawBytes::default(), + }) } fn with_context_code(self, code: ExitCode, f: F) -> Result @@ -199,6 +243,10 @@ impl AsActorError for Option { C: Display + 'static, F: FnOnce() -> C, { - self.ok_or_else(|| ActorError { exit_code: code, msg: f().to_string() }) + self.ok_or_else(|| ActorError { + exit_code: code, + msg: f().to_string(), + data: RawBytes::default(), + }) } } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 9b11a317c..57c0618d0 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -329,12 +329,13 @@ where // Otherwise, pass it through. code => code, }; - Err(ActorError::unchecked( + Err(ActorError::unchecked_with_data( exit_code, format!( "send to {} method {} aborted with code {}", to, method, ret.exit_code ), + ret.return_data, )) } } @@ -426,6 +427,10 @@ where fvm::event::emit_event(event) .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to emit event") } + + fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> ! { + fvm::vm::exit(code, data, msg) + } } impl Primitives for FvmRuntime @@ -634,7 +639,7 @@ pub fn trampoline(params: u32) -> u32 { 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, ¶ms) - .unwrap_or_else(|err| fvm::vm::abort(err.exit_code().value(), Some(err.msg()))); + .unwrap_or_else(|err| fvm::vm::exit(err.exit_code().value(), err.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 1a15d61e6..2a03aea50 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -244,6 +244,10 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Emits an event denoting that something externally noteworthy has ocurred. fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError>; + + /// Exit the current computation with an error code and optionally data and a debugging + /// message. + fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> !; } /// Message information available to the actor about executing message. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 25b8f1bc4..45165df55 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -118,6 +118,12 @@ pub fn make_builtin(bz: &[u8]) -> Cid { Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long")) } +pub struct ActorExit { + code: u32, + data: RawBytes, + msg: Option, +} + pub struct MockRuntime { pub epoch: ChainEpoch, pub miner: Address, @@ -166,6 +172,9 @@ pub struct MockRuntime { pub actor_balances: HashMap, pub tipset_timestamp: u64, pub tipset_cids: Vec, + + // actor exits + pub actor_exit: RefCell>, } #[derive(Default)] @@ -335,6 +344,7 @@ impl MockRuntime { actor_balances: Default::default(), tipset_timestamp: Default::default(), tipset_cids: Default::default(), + actor_exit: Default::default(), } } } @@ -528,7 +538,26 @@ impl MockRuntime { ) -> Result { self.in_call = true; let prev_state = self.state; - let res = A::invoke_method(self, method_num, params); + let res: Result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + A::invoke_method(self, method_num, params) + })) + .unwrap_or_else(|panic| { + if self.actor_exit.borrow().is_some() { + let exit = self.actor_exit.take().unwrap(); + if exit.code == 0 { + Ok(exit.data) + } else { + Err(ActorError::unchecked_with_data( + ExitCode::new(exit.code), + exit.msg.unwrap_or_else(|| "actor exited".to_owned()), + exit.data, + )) + } + } else { + std::panic::resume_unwind(panic) + } + }); if res.is_err() { self.state = prev_state; @@ -1268,6 +1297,11 @@ impl Runtime for MockRuntime { Ok(()) } + + fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> ! { + self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); + std::panic::panic_any("actor exit"); + } } impl Primitives for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 155ebb431..410901a64 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -454,8 +454,10 @@ impl<'bs> VM<'bs> { caller_validated: false, policy: &Policy::default(), subinvocations: RefCell::new(vec![]), + actor_exit: RefCell::new(None), }; - let res = new_ctx.invoke(); + let res = new_ctx.invoke_actor(); + let invoc = new_ctx.gather_trace(res.clone()); RefMut::map(self.invocations.borrow_mut(), |invocs| { invocs.push(invoc); @@ -467,7 +469,7 @@ impl<'bs> VM<'bs> { Ok(MessageResult { code: ae.exit_code(), message: ae.msg().to_string(), - ret: RawBytes::default(), + ret: ae.data(), }) } Ok(ret) => { @@ -593,6 +595,13 @@ pub struct InvocationCtx<'invocation, 'bs> { caller_validated: bool, policy: &'invocation Policy, subinvocations: RefCell>, + actor_exit: RefCell>, +} + +struct ActorExit { + code: u32, + data: RawBytes, + msg: Option, } impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { @@ -642,6 +651,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { caller_validated: false, policy: self.policy, subinvocations: RefCell::new(vec![]), + actor_exit: RefCell::new(None), }; if is_account { new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); @@ -676,6 +686,27 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { self.resolve_target(&self.msg.to).unwrap().1 } + fn invoke_actor(&mut self) -> Result { + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.invoke())).unwrap_or_else( + |panic| { + if self.actor_exit.borrow().is_some() { + let exit = self.actor_exit.take().unwrap(); + if exit.code == 0 { + Ok(exit.data) + } else { + Err(ActorError::unchecked_with_data( + ExitCode::new(exit.code), + exit.msg.unwrap_or_else(|| "actor exited".to_owned()), + exit.data, + )) + } + } else { + std::panic::resume_unwind(panic) + } + }, + ) + } + fn invoke(&mut self) -> Result { let prior_root = self.v.checkpoint(); @@ -936,9 +967,9 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { caller_validated: false, policy: self.policy, subinvocations: RefCell::new(vec![]), + actor_exit: RefCell::new(None), }; - let res = new_ctx.invoke(); - + let res = new_ctx.invoke_actor(); let invoc = new_ctx.gather_trace(res.clone()); RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { subinvocs.push(invoc); @@ -1069,6 +1100,11 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { fn emit_event(&self, _event: &ActorEvent) -> Result<(), ActorError> { Ok(()) } + + fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> ! { + self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); + std::panic::panic_any("actor exit"); + } } impl Primitives for VM<'_> { diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 1627fd2d4..02ecdd788 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -203,7 +203,7 @@ fn test_evm_staticcall() { ContractParams(params.to_vec()), ) .unwrap(); - assert!(!call_result.code.is_success(), "static call mutation succeeded"); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); } // A -> staticcall -> B -> call -> C (read) OK @@ -256,7 +256,7 @@ fn test_evm_staticcall() { ContractParams(params.to_vec()), ) .unwrap(); - assert!(!call_result.code.is_success(), "static call mutation succeeded",); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); } } @@ -470,6 +470,6 @@ fn test_evm_staticcall_delegatecall() { ContractParams(params.to_vec()), ) .unwrap(); - assert!(!call_result.code.is_success(), "static call mutation succeeded",); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); } } From c1f32cd26c6362bea00da0682e403825cc5b1626 Mon Sep 17 00:00:00 2001 From: raulk Date: Wed, 23 Nov 2022 23:33:24 +0000 Subject: [PATCH 120/339] clean up leftovers from CALLACTOR and METHODNUM refactor. (#867) * clean up leftovers from CALLACTOR and METHODNUM refactor. * fix callactor test * rusftm Co-authored-by: mriise --- actors/evm/src/interpreter/execution.rs | 6 -- actors/evm/src/interpreter/opcode.rs | 6 -- actors/evm/src/interpreter/precompiles.rs | 8 +- actors/evm/tests/call.rs | 121 +++++++++++++++++----- 4 files changed, 99 insertions(+), 42 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index f9788728b..16078fc1b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -851,12 +851,6 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { lifecycle::selfdestruct(m.runtime, m.system)?; Ok(ControlFlow::Exit) // selfdestruct halts the current context } - - // FEVM extensions opcodes - CALLACTOR(m) { - call::callactor(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } } const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs index 734a6d736..15b6a20dc 100644 --- a/actors/evm/src/interpreter/opcode.rs +++ b/actors/evm/src/interpreter/opcode.rs @@ -213,12 +213,6 @@ def_opcodes! { 0xa2: LOG2(4, -4), 0xa3: LOG3(5, -5), 0xa4: LOG4(6, -6), - ////////////////////////////////////////////////////////// - // FEVM extension opcodes - // FIL call conventions - 0xb0: CALLACTOR(6, -5), - 0xb1: METHODNUM(0, 1), - ////////////////////////////////////////////////////////// // 0xEF Reserved for EIP-3541 0xf0: CREATE(3, -2), 0xf1: CALL(7, -6), diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index c2bd8b183..8a8ac463b 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -25,7 +25,7 @@ lazy_static::lazy_static! { } /// ensures top bits are zeroed -fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { +pub fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { if src[..S] != [0u8; S] { Err(PrecompileError::InvalidInput) } else { @@ -478,15 +478,15 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> Ok(ret) => (U256::zero(), ret), }; - const NUM_OUTPUT_PARAMS: usize = 3; + const NUM_OUTPUT_PARAMS: u32 = 4; // codec of return data // TODO hardcoded to CBOR for now let codec = U256::from(fvm_ipld_encoding::DAG_CBOR); let offset = U256::from(NUM_OUTPUT_PARAMS * 32); - let size = U256::from(data.len()); + let size = U256::from(data.len() as u32); - let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS * 32 + data.len()); + let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS as usize * 32 + data.len()); output.extend_from_slice(&exit_code.to_bytes()); output.extend_from_slice(&codec.to_bytes()); output.extend_from_slice(&offset.to_bytes()); diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index bc5a5619b..012cf12a4 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -4,7 +4,7 @@ use evm::interpreter::address::EthAddress; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; -use fvm_ipld_encoding::{BytesSer, RawBytes}; +use fvm_ipld_encoding::{BytesSer, RawBytes, DAG_CBOR}; use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; @@ -205,41 +205,43 @@ fn test_native_call() { pub fn callactor_proxy_contract() -> Vec { let init = ""; let body = r#" -# this contract takes an address, method and the call payload and proxies a CALLACTOR call -# to that address # get call payload size -push1 0x40 calldatasize -sub # store payload to mem 0x00 -push1 0x40 +push1 0x00 push1 0x00 calldatacopy # prepare the proxy call -# input offset and size -push1 0x40 + +# out size +# out off +push1 0x20 +push1 0xa0 + +# in size +# in off calldatasize -sub push1 0x00 -# method -push1 0x20 -calldataload + # value push1 0x00 -# dest address -push1 0x00 -calldataload + +# dst (callactor precompile) +push1 0x0e + # gas push1 0x00 -# do the call -@callactor -# return result through +call + +# copy result to mem 0x00 (overwrites input data) returndatasize push1 0x00 push1 0x00 returndatacopy + +# return returndatasize push1 0x00 return @@ -254,21 +256,31 @@ fn test_callactor() { // 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); - let evm_target = EthAddress::from_id(target_id); - let evm_target_word = evm_target.as_evm_word(); - // dest + method 0x42 with no data - let mut contract_params = vec![0u8; 64]; - evm_target_word.to_big_endian(&mut contract_params[..32]); - contract_params[63] = 0x42; + let mut contract_params = Vec::new(); + + let method = U256::from(0x42); + let codec = U256::from(DAG_CBOR); + + let target_bytes = target.to_bytes(); + let target_size = U256::from(target_bytes.len()); let proxy_call_input_data = RawBytes::default(); + let data_size = U256::from(proxy_call_input_data.len()); + + contract_params.extend_from_slice(&method.to_bytes()); + contract_params.extend_from_slice(&codec.to_bytes()); + contract_params.extend_from_slice(&target_size.to_bytes()); + contract_params.extend_from_slice(&data_size.to_bytes()); + contract_params.extend_from_slice(&target_bytes); + contract_params.extend_from_slice(&proxy_call_input_data); + + assert_eq!(32 * 4 + target_bytes.len() + proxy_call_input_data.len(), contract_params.len()); // expected return data let mut return_data = vec![0u8; 32]; @@ -282,7 +294,64 @@ fn test_callactor() { RawBytes::from(return_data), ExitCode::OK, ); + + // invoke + let result = util::invoke_contract(&mut rt, &contract_params); - assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); + // assert return + + #[derive(Debug, PartialEq, Eq)] + struct CallActorReturn { + exit_code: ExitCode, + codec: u64, + data_offset: u32, + data_size: u32, + data: Vec, + } + + impl CallActorReturn { + pub fn read(src: &[u8]) -> Self { + use fil_actor_evm::interpreter::precompiles::assert_zero_bytes; + assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values"); + + let bytes = &src[..32]; + let exit_code = { + assert_zero_bytes::<4>(bytes).unwrap(); + ExitCode::new(u32::from_be_bytes(bytes[28..32].try_into().unwrap())) + }; + + let bytes = &src[32..64]; + let codec = { + assert_zero_bytes::<8>(bytes).unwrap(); + u64::from_be_bytes(bytes[24..32].try_into().unwrap()) + }; + + let bytes = &src[64..96]; + let offset = { + assert_zero_bytes::<4>(bytes).unwrap(); + u32::from_be_bytes(bytes[28..32].try_into().unwrap()) + }; + + let bytes = &src[96..128]; + let size = { + assert_zero_bytes::<4>(bytes).unwrap(); + u32::from_be_bytes(bytes[28..32].try_into().unwrap()) + }; + let data = Vec::from(&src[offset as usize..(offset + size) as usize]); + + Self { exit_code, codec, data_offset: offset, data_size: size, data } + } + } + + let result = CallActorReturn::read(&result); + let expected = CallActorReturn { + exit_code: ExitCode::new(0), + codec: DAG_CBOR, + data_offset: 128, + data_size: 32, + data: U256::from(0x42).to_bytes().to_vec(), + }; + + assert_eq!(result, expected); } From 265948cabf480c58803be2319328ab3edaa5a55f Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 23 Nov 2022 16:42:25 -0800 Subject: [PATCH 121/339] EVM: Unit tests for some math opcodes (#860) * add unit tests for some opcodes ADD MUL SUB DIV * fmt --- .../interpreter/instructions/arithmetic.rs | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 34a59f04f..88191e91c 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -1,3 +1,11 @@ +//! ## Implementer's notes +//! +//! All operations are done with overflowing math +//! TODO (simple?) add, mul, sub div +//! +//! ### Non-critical TODOs +//! Many operations can simply mutate the last value on stack instead of pop/push. + use {crate::interpreter::stack::Stack, crate::interpreter::uints::*, crate::interpreter::U256}; #[inline] @@ -25,6 +33,8 @@ pub fn sub(stack: &mut Stack) { pub fn div(stack: &mut Stack) { let a = stack.pop(); let b = stack.get_mut(0); + // TODO shortcut optimizations from go's lib (our doesn't) + // https://github.com/holiman/uint256/blob/6f8ccba90ce6cba9727ad5aa26bb925a25b50d29/uint256.go#L544 if !b.is_zero() { *b = a / *b } @@ -124,6 +134,123 @@ pub fn exp(stack: &mut Stack) { #[cfg(test)] mod test { + + mod basic { + use crate::interpreter::{stack::Stack, U256}; + + // all operations go a b + fn push_2(s: &mut Stack, a: impl Into, b: impl Into) { + s.push(b.into()); + s.push(a.into()); + } + + fn expect_stack_value(s: &mut Stack, e: impl Into, comment: impl AsRef) { + let mut expected = Stack::new(); + expected.push(e.into()); + + // stacks should be _exactly_ the same + assert_eq!(s.0, expected.0, "{}", comment.as_ref()); + s.pop(); + } + + #[test] + fn add() { + let mut s = Stack::default(); + let s = &mut s; + + push_2(s, 0, 0); + super::add(s); + expect_stack_value(s, 0, "add nothing to nothing"); + + // does "math" on all limbs, so it is different than above + push_2(s, U256::max_value(), 0); + super::add(s); + expect_stack_value(s, U256::max_value(), "add nothing to max value"); + + push_2(s, 2, 2); + super::add(s); + expect_stack_value(s, 4, "2 plus 2 equals 5 (???)"); + + push_2(s, u64::MAX, 32); + super::add(s); + expect_stack_value(s, u64::MAX as u128 + 32, "add 32 past a single (u64) limb of u256"); + + // wrap to zero + push_2(s, U256::max_value(), 1); + super::add(s); + expect_stack_value(s, 0, "overflow by one"); + + // wrap all limbs + push_2(s, U256::max_value(), U256::max_value()); + super::add(s); + expect_stack_value(s, U256::max_value() - 1, "overflow by max, should be 2^256-1"); + } + + #[test] + fn mul() { + let mut s = Stack::default(); + let s = &mut s; + + push_2(s, 0, 0); + super::mul(s); + expect_stack_value(s, 0, "multiply nothing by nothing"); + + push_2(s, 2, 3); + super::mul(s); + expect_stack_value(s, 6, "multiply 2 by 3"); + + push_2(s, u64::MAX, 2); + super::mul(s); + expect_stack_value(s, (u64::MAX as u128) * 2, "2^64 x 2"); + } + + #[test] + fn sub() { + let mut s = Stack::default(); + let s = &mut s; + + push_2(s, 0, 0); + super::sub(s); + expect_stack_value(s, 0, "subtract nothing by nothing"); + + push_2(s, 2, 1); + super::sub(s); + expect_stack_value(s, 1, "subtract 2 by 1"); + + push_2(s, (u64::MAX as u128) + 32, 64); + super::sub(s); + expect_stack_value(s, u64::MAX - 32, "subtract 64 from a value 32 over a single limb"); + + // wrap to max + push_2(s, 0, 1); + super::sub(s); + expect_stack_value(s, U256::max_value(), "wrap around to max by one"); + + // wrap all limbs + push_2(s, U256::max_value(), U256::max_value()); + super::sub(s); + expect_stack_value(s, 0, "wrap around zero by 2^256"); + } + + #[test] + fn div() { + let mut s = Stack::default(); + let s = &mut s; + + push_2(s, 0, 0); + super::div(s); + expect_stack_value(s, 0, "divide nothing by nothing (yes)"); + + push_2(s, 4, 1); + super::div(s); + expect_stack_value(s, 4, "divide 4 by 1"); + + push_2(s, u128::MAX, 2); + super::div(s); + expect_stack_value(s, u128::MAX / 2, "divide 2^128 by 2 (uses >1 limb)"); + } + } + use super::*; #[test] fn test_signextend() { From 3c90246935c1917edebcfa7b593f99424650a546 Mon Sep 17 00:00:00 2001 From: sleepy ramen Date: Mon, 28 Nov 2022 17:49:28 +0100 Subject: [PATCH 122/339] fix call_actor precompile (#872) * fix call_actor precompile; modify error message * revert modifying error message * rustfmt --- actors/evm/src/interpreter/precompiles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 8a8ac463b..3b68bfeaa 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -447,7 +447,7 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> let bytes = read_right_pad(start, send_data_size + address_size); let input_data = &bytes[..send_data_size]; - let address = &bytes[send_data_size..address_size]; + let address = &bytes[send_data_size..send_data_size + address_size]; let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; // TODO passing underlying gas into send when implemented in FVM From 5a4b15b9703f401ac80f7b03cc7cd84cf2fc0a25 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 29 Nov 2022 00:15:10 +0200 Subject: [PATCH 123/339] EVM: Refactor Interpreter dispatch (#646) * check stack limits at dispatch, make stack ops unchecked * use a growable vector for the stack * fix growth check * clippy wants default... * optimize ensure somewhat * remove implicit assumption about growth patterns * drop error from Bytecode::new signature, it can't really err. fix rebase artifact * refactor part I: primops * add arity/stack checks for primops * dup and swap * push * immediates * sugar * first stdfun * keccak256 as stdfun and prettier sugar * moar stdfuns * calldatacopy * emacs auto-indent friendly sugar * more functionoids * moar funcionoids * call opcodes also kill leftover callactor * primitive -> primop * codesize and codecopy * create and create2 * control flow * mark stack ops as unsafe only macro mechanically checked invocations for these * fix tests * rustfmt * shut up clippy's frivolous complaints * nit: macro magic Co-authored-by: Steven Allen --- actors/evm/src/interpreter/bytecode.rs | 1 - actors/evm/src/interpreter/execution.rs | 992 +++++------------- .../interpreter/instructions/arithmetic.rs | 176 ++-- .../src/interpreter/instructions/bitwise.rs | 73 +- .../src/interpreter/instructions/boolean.rs | 68 +- .../evm/src/interpreter/instructions/call.rs | 248 ++--- .../src/interpreter/instructions/context.rs | 96 +- .../src/interpreter/instructions/control.rs | 63 +- .../evm/src/interpreter/instructions/ext.rs | 25 +- .../evm/src/interpreter/instructions/hash.rs | 13 +- .../src/interpreter/instructions/lifecycle.rs | 42 +- .../evm/src/interpreter/instructions/log.rs | 12 +- .../src/interpreter/instructions/memory.rs | 43 +- .../evm/src/interpreter/instructions/mod.rs | 334 ++++++ .../evm/src/interpreter/instructions/stack.rs | 9 +- .../evm/src/interpreter/instructions/state.rs | 17 +- .../src/interpreter/instructions/storage.rs | 23 +- actors/evm/src/interpreter/stack.rs | 97 +- 18 files changed, 1123 insertions(+), 1209 deletions(-) diff --git a/actors/evm/src/interpreter/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs index c5f749842..3a3b30f3e 100644 --- a/actors/evm/src/interpreter/bytecode.rs +++ b/actors/evm/src/interpreter/bytecode.rs @@ -12,7 +12,6 @@ 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. - // todo: create the jumpdest table only once during initial contract deployment let mut jumpdest = vec![false; bytecode.len()]; let mut i = 0; while i < bytecode.len() { diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 16078fc1b..671da9da5 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -4,12 +4,9 @@ use fvm_shared::address::Address as FilecoinAddress; use super::address::EthAddress; use { - super::instructions::*, + super::instructions, super::opcode::OpCode, super::StatusCode, - crate::interpreter::instructions::call::CallKind, - crate::interpreter::instructions::log::*, - crate::interpreter::instructions::stack::*, crate::interpreter::memory::Memory, crate::interpreter::stack::Stack, crate::interpreter::{Bytecode, Output, System}, @@ -36,7 +33,7 @@ pub struct ExecutionState { impl ExecutionState { pub fn new(caller: EthAddress, receiver: EthAddress, input_data: Bytes) -> Self { Self { - stack: Stack::default(), + stack: Stack::new(), memory: Memory::default(), input_data, return_data: Default::default(), @@ -50,7 +47,7 @@ impl ExecutionState { pub struct Machine<'r, 'a, RT: Runtime + 'a> { system: &'r mut System<'a, RT>, - runtime: &'r mut ExecutionState, + state: &'r mut ExecutionState, bytecode: &'r Bytecode, pc: usize, reverted: bool, @@ -64,6 +61,20 @@ enum ControlFlow { type Instruction = fn(*mut M) -> Result; +macro_rules! def_opcodes { + ($($op:ident: $body:tt)*) => { + def_ins_raw! { + UNDEFINED(_m) { + Err(StatusCode::UndefinedInstruction) + } + } + $(def_ins! { $op $body })* + def_jmptable! { + $($op)* + } + } +} + macro_rules! def_jmptable { ($($op:ident)*) => { const fn jmptable() -> [Instruction>; 256] { @@ -74,7 +85,33 @@ macro_rules! def_jmptable { } } -macro_rules! def_ins1 { +macro_rules! def_ins { + ($ins:ident {primop}) => { + def_ins_primop! { $ins } + }; + + ($ins:ident {push}) => { + def_ins_push! { $ins } + }; + + ($ins:ident {std}) => { + def_ins_std! { $ins } + }; + + ($ins:ident {code}) => { + def_ins_code! { $ins } + }; + + ($ins:ident {=> $expr:expr}) => { + def_ins_raw! { $ins (_m) { $expr } } + }; + + ($ins:ident {($arg:ident) => $body:block}) => { + def_ins_raw! { $ins ($arg) $body } + }; +} + +macro_rules! def_ins_raw { ($ins:ident ($arg:ident) $body:block) => { #[allow(non_snake_case)] fn $ins(p: *mut Self) -> Result { @@ -85,27 +122,57 @@ macro_rules! def_ins1 { }; } -macro_rules! def_ins { - ($($op:ident ($arg:ident) $body:block)*) => { - def_ins1! { - UNDEFINED(_m) { - Err(StatusCode::UndefinedInstruction) +macro_rules! def_ins_primop { + ($ins:ident) => { + def_ins_raw! { + $ins (m) { + instructions::$ins(&mut m.state.stack)?; + Ok(ControlFlow::Continue) } } - $(def_ins1! { $op ($arg) $body })* - def_jmptable! { - $($op)* + }; +} + +macro_rules! def_ins_push { + ($ins:ident) => { + def_ins_raw! { + $ins (m) { + m.pc += instructions::$ins(&mut m.state.stack, &m.bytecode[m.pc + 1..])?; + Ok(ControlFlow::Continue) + } } - } + }; +} + +macro_rules! def_ins_std { + ($ins:ident) => { + def_ins_raw! { + $ins (m) { + instructions::$ins(m.state, m.system)?; + Ok(ControlFlow::Continue) + } + } + }; +} + +macro_rules! def_ins_code { + ($ins:ident) => { + def_ins_raw! { + $ins (m) { + instructions::$ins(m.state, m.system, m.bytecode.as_ref())?; + Ok(ControlFlow::Continue) + } + } + }; } impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { pub fn new( system: &'r mut System<'a, RT>, - runtime: &'r mut ExecutionState, + state: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { - Machine { system, runtime, bytecode, pc: 0, reverted: false } + Machine { system, state, bytecode, pc: 0, reverted: false } } pub fn execute(&mut self) -> Result<(), StatusCode> { @@ -133,724 +200,203 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { Self::JMPTABLE[op as usize](self) } - def_ins! { - STOP(_m) { - Ok(ControlFlow::Exit) - } - - ADD(m) { - arithmetic::add(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - MUL(m) { - arithmetic::mul(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SUB(m) { - arithmetic::sub(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DIV(m) { - arithmetic::div(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SDIV(m) { - arithmetic::sdiv(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - MOD(m) { - arithmetic::modulo(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SMOD(m) { - arithmetic::smod(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - ADDMOD(m) { - arithmetic::addmod(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - MULMOD(m) { - arithmetic::mulmod(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - EXP(m) { - arithmetic::exp(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SIGNEXTEND(m) { - arithmetic::signextend(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - LT(m) { - boolean::lt(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - GT(m) { - boolean::gt(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SLT(m) { - boolean::slt(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SGT(m) { - boolean::sgt(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - EQ(m) { - boolean::eq(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - ISZERO(m) { - boolean::iszero(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - AND(m) { - boolean::and(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - OR(m) { - boolean::or(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - XOR(m) { - boolean::xor(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - NOT(m) { - boolean::not(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - BYTE(m) { - bitwise::byte(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SHL(m) { - bitwise::shl(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SHR(m) { - bitwise::shr(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SAR(m) { - bitwise::sar(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - KECCAK256(m) { - hash::keccak256(m.system, m.runtime)?; - Ok(ControlFlow::Continue) - } - - ADDRESS(m) { - context::address(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - BALANCE(m) { - state::balance(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - ORIGIN(m) { - context::origin(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - CALLER(m) { - context::caller(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - CALLVALUE(m) { - context::call_value(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - CALLDATALOAD(m) { - call::calldataload(m.runtime); - Ok(ControlFlow::Continue) - } - - CALLDATASIZE(m) { - call::calldatasize(m.runtime); - Ok(ControlFlow::Continue) - } - - CALLDATACOPY(m) { - call::calldatacopy(m.runtime)?; - Ok(ControlFlow::Continue) - } - - CODESIZE(m) { - call::codesize(&mut m.runtime.stack, m.bytecode.as_ref()); - Ok(ControlFlow::Continue) - } - - CODECOPY(m) { - call::codecopy(m.runtime, m.bytecode.as_ref())?; - Ok(ControlFlow::Continue) - } - - GASPRICE(m) { - context::gas_price(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - EXTCODESIZE(m) { - ext::extcodesize(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - EXTCODECOPY(m) { - ext::extcodecopy(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - RETURNDATASIZE(m) { - control::returndatasize(m.runtime); - Ok(ControlFlow::Continue) - } - - RETURNDATACOPY(m) { - control::returndatacopy(m.runtime)?; - Ok(ControlFlow::Continue) - } - - EXTCODEHASH(m) { - ext::extcodehash(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - BLOCKHASH(m) { - context::blockhash(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - COINBASE(m) { - context::coinbase(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - TIMESTAMP(m) { - context::timestamp(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - NUMBER(m) { - context::block_number(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - DIFFICULTY(m) { - context::difficulty(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - GASLIMIT(m) { - context::gas_limit(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - CHAINID(m) { - context::chain_id(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - SELFBALANCE(m) { - state::selfbalance(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - BASEFEE(m) { - context::base_fee(m.runtime, m.system); - Ok(ControlFlow::Continue) - } - - POP(m) { - stack::pop(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - MLOAD(m) { - memory::mload(m.runtime)?; - Ok(ControlFlow::Continue) - } - - MSTORE(m) { - memory::mstore(m.runtime)?; - Ok(ControlFlow::Continue) - } - - MSTORE8(m) { - memory::mstore8(m.runtime)?; - Ok(ControlFlow::Continue) - } - - SLOAD(m) { - storage::sload(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - SSTORE(m) { - storage::sstore(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - JUMP(m) { - m.pc = control::jump(&mut m.runtime.stack, m.bytecode)?; - Ok(ControlFlow::Jump) - } + def_opcodes! { + STOP: {=> Ok(ControlFlow::Exit)} + + // primops + ADD: {primop} + MUL: {primop} + SUB: {primop} + DIV: {primop} + SDIV: {primop} + MOD: {primop} + SMOD: {primop} + ADDMOD: {primop} + MULMOD: {primop} + EXP: {primop} + SIGNEXTEND: {primop} + LT: {primop} + GT: {primop} + SLT: {primop} + SGT: {primop} + EQ: {primop} + ISZERO: {primop} + AND: {primop} + OR: {primop} + XOR: {primop} + NOT: {primop} + BYTE: {primop} + SHL: {primop} + SHR: {primop} + SAR: {primop} + POP: {primop} + + // std call convenction functionoids + KECCAK256: {std} + ADDRESS: {std} + BALANCE: {std} + ORIGIN: {std} + CALLER: {std} + CALLVALUE: {std} + CALLDATALOAD: {std} + CALLDATASIZE: {std} + CALLDATACOPY: {std} + CODESIZE: {code} + CODECOPY: {code} + GASPRICE: {std} + EXTCODESIZE: {std} + EXTCODECOPY: {std} + RETURNDATASIZE: {std} + RETURNDATACOPY: {std} + EXTCODEHASH: {std} + BLOCKHASH: {std} + COINBASE: {std} + TIMESTAMP: {std} + NUMBER: {std} + DIFFICULTY: {std} + GASLIMIT: {std} + CHAINID: {std} + BASEFEE: {std} + SELFBALANCE: {std} + MLOAD: {std} + MSTORE: {std} + MSTORE8: {std} + SLOAD: {std} + SSTORE: {std} + MSIZE: {std} + GAS: {std} + + // push variants + PUSH1: {push} + PUSH2: {push} + PUSH3: {push} + PUSH4: {push} + PUSH5: {push} + PUSH6: {push} + PUSH7: {push} + PUSH8: {push} + PUSH9: {push} + PUSH10: {push} + PUSH11: {push} + PUSH12: {push} + PUSH13: {push} + PUSH14: {push} + PUSH15: {push} + PUSH16: {push} + PUSH17: {push} + PUSH18: {push} + PUSH19: {push} + PUSH20: {push} + PUSH21: {push} + PUSH22: {push} + PUSH23: {push} + PUSH24: {push} + PUSH25: {push} + PUSH26: {push} + PUSH27: {push} + PUSH28: {push} + PUSH29: {push} + PUSH30: {push} + PUSH31: {push} + PUSH32: {push} + + // dup variants + DUP1: {primop} + DUP2: {primop} + DUP3: {primop} + DUP4: {primop} + DUP5: {primop} + DUP6: {primop} + DUP7: {primop} + DUP8: {primop} + DUP9: {primop} + DUP10: {primop} + DUP11: {primop} + DUP12: {primop} + DUP13: {primop} + DUP14: {primop} + DUP15: {primop} + DUP16: {primop} + + // swap variants + SWAP1: {primop} + SWAP2: {primop} + SWAP3: {primop} + SWAP4: {primop} + SWAP5: {primop} + SWAP6: {primop} + SWAP7: {primop} + SWAP8: {primop} + SWAP9: {primop} + SWAP10: {primop} + SWAP11: {primop} + SWAP12: {primop} + SWAP13: {primop} + SWAP14: {primop} + SWAP15: {primop} + SWAP16: {primop} + + // event logs + LOG0: {std} + LOG1: {std} + LOG2: {std} + LOG3: {std} + LOG4: {std} + + // create variants + CREATE: {std} + CREATE2: {std} + + // call variants + CALL: {std} + CALLCODE: {std} + DELEGATECALL: {std} + STATICCALL: {std} + + // control flow magic + JUMPDEST: {=> Ok(ControlFlow::Continue)} // noop marker opcode for valid jumps addresses + + JUMP: {(m) => { + if let Some(dest) = instructions::JUMP(&mut m.state.stack, m.bytecode)? { + m.pc = dest; + Ok(ControlFlow::Jump) + } else { + // cant happen, unless it's a cosmic ray + Err(StatusCode::Failure) + } + }} - JUMPI(m) { - if let Some(dest) = control::jumpi(&mut m.runtime.stack, m.bytecode)? { + JUMPI: {(m) => { + if let Some(dest) = instructions::JUMPI(&mut m.state.stack, m.bytecode)? { m.pc = dest; Ok(ControlFlow::Jump) } else { Ok(ControlFlow::Continue) } - } - - PC(m) { - control::pc(&mut m.runtime.stack, m.pc); - Ok(ControlFlow::Continue) - } - - MSIZE(m) { - memory::msize(m.runtime); - Ok(ControlFlow::Continue) - } + }} - GAS(m) { - context::gas(m.runtime, m.system); + PC: {(m) => { + instructions::PC(&mut m.state.stack, m.pc)?; Ok(ControlFlow::Continue) - } - - JUMPDEST(_m) { - // marker opcode for valid jumps addresses - Ok(ControlFlow::Continue) - } - - PUSH1(m) { - m.pc += push::<1>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH2(m) { - m.pc += push::<2>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH3(m) { - m.pc += push::<3>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH4(m) { - m.pc += push::<4>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH5(m) { - m.pc += push::<5>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH6(m) { - m.pc += push::<6>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH7(m) { - m.pc += push::<7>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH8(m) { - m.pc += push::<8>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH9(m) { - m.pc += push::<9>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH10(m) { - m.pc += push::<10>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH11(m) { - m.pc += push::<11>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH12(m) { - m.pc += push::<12>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH13(m) { - m.pc += push::<13>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH14(m) { - m.pc += push::<14>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH15(m) { - m.pc += push::<15>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH16(m) { - m.pc += push::<16>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH17(m) { - m.pc += push::<17>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH18(m) { - m.pc += push::<18>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH19(m) { - m.pc += push::<19>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH20(m) { - m.pc += push::<20>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH21(m) { - m.pc += push::<21>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH22(m) { - m.pc += push::<22>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH23(m) { - m.pc += push::<23>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH24(m) { - m.pc += push::<24>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH25(m) { - m.pc += push::<25>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH26(m) { - m.pc += push::<26>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH27(m) { - m.pc += push::<27>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH28(m) { - m.pc += push::<28>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH29(m) { - m.pc += push::<29>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH30(m) { - m.pc += push::<30>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH31(m) { - m.pc += push::<31>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - PUSH32(m) { - m.pc += push::<32>(&mut m.runtime.stack, &m.bytecode[m.pc + 1..]); - Ok(ControlFlow::Continue) - } - - DUP1(m) { - dup::<1>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP2(m) { - dup::<2>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP3(m) { - dup::<3>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP4(m) { - dup::<4>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP5(m) { - dup::<5>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP6(m) { - dup::<6>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } + }} - DUP7(m) { - dup::<7>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP8(m) { - dup::<8>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP9(m) { - dup::<9>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP10(m) { - dup::<10>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP11(m) { - dup::<11>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP12(m) { - dup::<12>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP13(m) { - dup::<13>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP14(m) { - dup::<14>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP15(m) { - dup::<15>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - DUP16(m) { - dup::<16>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP1(m) { - swap::<1>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP2(m) { - swap::<2>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP3(m) { - swap::<3>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP4(m) { - swap::<4>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP5(m) { - swap::<5>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP6(m) { - swap::<6>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP7(m) { - swap::<7>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP8(m) { - swap::<8>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP9(m) { - swap::<9>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP10(m) { - swap::<10>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP11(m) { - swap::<11>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP12(m) { - swap::<12>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP13(m) { - swap::<13>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP14(m) { - swap::<14>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP15(m) { - swap::<15>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - SWAP16(m) { - swap::<16>(&mut m.runtime.stack); - Ok(ControlFlow::Continue) - } - - LOG0(m) { - log(m.runtime, m.system, 0)?; - Ok(ControlFlow::Continue) - } - - LOG1(m) { - log(m.runtime, m.system, 1)?; - Ok(ControlFlow::Continue) - } - - LOG2(m) { - log(m.runtime, m.system, 2)?; - Ok(ControlFlow::Continue) - } - - LOG3(m) { - log(m.runtime, m.system, 3)?; - Ok(ControlFlow::Continue) - } - - LOG4(m) { - log(m.runtime, m.system, 4)?; - Ok(ControlFlow::Continue) - } - - CREATE(m) { - lifecycle::create(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - CALL(m) { - call::call(m.runtime, m.system, CallKind::Call)?; - Ok(ControlFlow::Continue) - } - - CALLCODE(m) { - call::call(m.runtime, m.system, CallKind::CallCode)?; - Ok(ControlFlow::Continue) - } - - RETURN(m) { - control::ret(m.runtime)?; + RETURN: {(m) => { + instructions::RETURN(m.state, m.system)?; Ok(ControlFlow::Exit) - } + }} - DELEGATECALL(m) { - call::call(m.runtime, m.system, CallKind::DelegateCall)?; - Ok(ControlFlow::Continue) - } - - CREATE2(m) { - lifecycle::create2(m.runtime, m.system)?; - Ok(ControlFlow::Continue) - } - - STATICCALL(m) { - call::call(m.runtime, m.system, CallKind::StaticCall)?; - Ok(ControlFlow::Continue) - } - - REVERT(m) { - control::ret(m.runtime)?; + REVERT: {(m) => { + instructions::REVERT(m.state, m.system)?; m.reverted = true; Ok(ControlFlow::Exit) - } + }} - INVALID(_m) { - Err(StatusCode::InvalidInstruction) - } - - SELFDESTRUCT(m) { - lifecycle::selfdestruct(m.runtime, m.system)?; + SELFDESTRUCT: {(m) => { + instructions::SELFDESTRUCT(m.state, m.system)?; Ok(ControlFlow::Exit) // selfdestruct halts the current context - } + }} + + INVALID: {=> Err(StatusCode::InvalidInstruction)} } const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); @@ -866,7 +412,7 @@ pub fn execute( Ok(Output { reverted: m.reverted, status_code: StatusCode::Success, - output_data: m.runtime.output_data.clone(), - selfdestroyed: m.runtime.selfdestroyed, + output_data: m.state.output_data.clone(), + selfdestroyed: m.state.selfdestroyed, }) } diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 88191e91c..310b832c2 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -1,116 +1,92 @@ -//! ## Implementer's notes -//! -//! All operations are done with overflowing math -//! TODO (simple?) add, mul, sub div -//! -//! ### Non-critical TODOs -//! Many operations can simply mutate the last value on stack instead of pop/push. - -use {crate::interpreter::stack::Stack, crate::interpreter::uints::*, crate::interpreter::U256}; +use {crate::interpreter::uints::*, crate::interpreter::U256}; #[inline] -pub fn add(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - stack.push(a.overflowing_add(b).0); +pub fn add(a: U256, b: U256) -> U256 { + a.overflowing_add(b).0 } #[inline] -pub fn mul(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - stack.push(a.overflowing_mul(b).0); +pub fn mul(a: U256, b: U256) -> U256 { + a.overflowing_mul(b).0 } #[inline] -pub fn sub(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - stack.push(a.overflowing_sub(b).0); +pub fn sub(a: U256, b: U256) -> U256 { + a.overflowing_sub(b).0 } #[inline] -pub fn div(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); +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() { - *b = a / *b + a / b + } else { + b } } #[inline] -pub fn sdiv(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - let v = i256_div(a, b); - stack.push(v); +pub fn sdiv(a: U256, b: U256) -> U256 { + i256_div(a, b) } #[inline] -pub fn modulo(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); +pub fn modulo(a: U256, b: U256) -> U256 { if !b.is_zero() { - *b = a % *b; + a % b + } else { + b } } #[inline] -pub fn smod(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = i256_mod(a, *b); +pub fn smod(a: U256, b: U256) -> U256 { + i256_mod(a, b) } #[inline] -pub fn addmod(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - let c = stack.get_mut(0); - +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(); + let cl: U512 = c.into(); - *c = (al + bl % cl).low_u256(); + (al + bl % cl).low_u256() + } else { + c } } #[inline] -pub fn mulmod(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.pop(); - let c = stack.get_mut(0); - +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(); - *c = (al * bl % cl).low_u256(); + let cl: U512 = c.into(); + (al * bl % cl).low_u256() + } else { + c } } #[inline] -pub fn signextend(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - +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); - *b = if b.bit(bit_index as usize) { *b | !mask } else { *b & mask } + if b.bit(bit_index as usize) { + b | !mask + } else { + b & mask + } + } else { + b } } #[inline] -pub fn exp(stack: &mut Stack) { - let mut base = stack.pop(); - let power = stack.pop(); - +pub fn exp(mut base: U256, power: U256) -> U256 { let mut v = U256::ONE; // First, compute the number of remaining significant bits. @@ -129,28 +105,36 @@ pub fn exp(stack: &mut Stack) { remaining_bits = remaining_bits.saturating_sub(u64::BITS); } - stack.push(v); + v } #[cfg(test)] mod test { + use super::*; + use crate::interpreter::stack::Stack; mod basic { use crate::interpreter::{stack::Stack, U256}; // all operations go a b fn push_2(s: &mut Stack, a: impl Into, b: impl Into) { - s.push(b.into()); - s.push(a.into()); + unsafe { + s.push(b.into()); + s.push(a.into()); + } } fn expect_stack_value(s: &mut Stack, e: impl Into, comment: impl AsRef) { let mut expected = Stack::new(); - expected.push(e.into()); + unsafe { + expected.push(e.into()); + } // stacks should be _exactly_ the same - assert_eq!(s.0, expected.0, "{}", comment.as_ref()); - s.pop(); + assert_eq!(unsafe { s.get(0) }, unsafe { expected.get(0) }, "{}", comment.as_ref()); + unsafe { + s.pop(); + } } #[test] @@ -159,30 +143,30 @@ mod test { let s = &mut s; push_2(s, 0, 0); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, 0, "add nothing to nothing"); // does "math" on all limbs, so it is different than above push_2(s, U256::max_value(), 0); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, U256::max_value(), "add nothing to max value"); push_2(s, 2, 2); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, 4, "2 plus 2 equals 5 (???)"); push_2(s, u64::MAX, 32); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, u64::MAX as u128 + 32, "add 32 past a single (u64) limb of u256"); // wrap to zero push_2(s, U256::max_value(), 1); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, 0, "overflow by one"); // wrap all limbs push_2(s, U256::max_value(), U256::max_value()); - super::add(s); + crate::interpreter::instructions::ADD(s).unwrap(); expect_stack_value(s, U256::max_value() - 1, "overflow by max, should be 2^256-1"); } @@ -192,15 +176,15 @@ mod test { let s = &mut s; push_2(s, 0, 0); - super::mul(s); + crate::interpreter::instructions::MUL(s).unwrap(); expect_stack_value(s, 0, "multiply nothing by nothing"); push_2(s, 2, 3); - super::mul(s); + crate::interpreter::instructions::MUL(s).unwrap(); expect_stack_value(s, 6, "multiply 2 by 3"); push_2(s, u64::MAX, 2); - super::mul(s); + crate::interpreter::instructions::MUL(s).unwrap(); expect_stack_value(s, (u64::MAX as u128) * 2, "2^64 x 2"); } @@ -210,25 +194,25 @@ mod test { let s = &mut s; push_2(s, 0, 0); - super::sub(s); + crate::interpreter::instructions::SUB(s).unwrap(); expect_stack_value(s, 0, "subtract nothing by nothing"); push_2(s, 2, 1); - super::sub(s); + crate::interpreter::instructions::SUB(s).unwrap(); expect_stack_value(s, 1, "subtract 2 by 1"); push_2(s, (u64::MAX as u128) + 32, 64); - super::sub(s); + crate::interpreter::instructions::SUB(s).unwrap(); expect_stack_value(s, u64::MAX - 32, "subtract 64 from a value 32 over a single limb"); // wrap to max push_2(s, 0, 1); - super::sub(s); + crate::interpreter::instructions::SUB(s).unwrap(); expect_stack_value(s, U256::max_value(), "wrap around to max by one"); // wrap all limbs push_2(s, U256::max_value(), U256::max_value()); - super::sub(s); + crate::interpreter::instructions::SUB(s).unwrap(); expect_stack_value(s, 0, "wrap around zero by 2^256"); } @@ -238,30 +222,31 @@ mod test { let s = &mut s; push_2(s, 0, 0); - super::div(s); + crate::interpreter::instructions::DIV(s).unwrap(); expect_stack_value(s, 0, "divide nothing by nothing (yes)"); push_2(s, 4, 1); - super::div(s); + crate::interpreter::instructions::DIV(s).unwrap(); expect_stack_value(s, 4, "divide 4 by 1"); push_2(s, u128::MAX, 2); - super::div(s); + crate::interpreter::instructions::DIV(s).unwrap(); expect_stack_value(s, u128::MAX / 2, "divide 2^128 by 2 (uses >1 limb)"); } } - use super::*; #[test] fn test_signextend() { macro_rules! assert_exp { ($num:expr, $byte:expr, $result:expr) => { let mut stack = Stack::new(); - stack.push(($num).into()); - stack.push(($byte).into()); - signextend(&mut stack); + unsafe { + stack.push(($num).into()); + stack.push(($byte).into()); + } + crate::interpreter::instructions::SIGNEXTEND(&mut stack).unwrap(); let res: U256 = ($result).into(); - assert_eq!(res, stack.pop()); + assert_eq!(res, unsafe { stack.pop() }); }; } assert_exp!(0xff, 0, U256::MAX); @@ -278,16 +263,19 @@ mod test { // 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 mut stack = Stack::new(); - stack.push(($exp).into()); - stack.push(($base).into()); - exp(&mut stack); + unsafe { + stack.push(($exp).into()); + stack.push(($base).into()); + } + crate::interpreter::instructions::EXP(&mut stack).unwrap(); let res: U256 = ($result).into(); - assert_eq!(res, stack.pop()); + assert_eq!(res, unsafe { stack.pop() }); }; } diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs index 167807c40..22f083ccf 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -1,52 +1,40 @@ -use {crate::interpreter::stack::Stack, crate::interpreter::U256}; +use crate::interpreter::U256; #[inline] -pub fn byte(stack: &mut Stack) { - let i = stack.pop(); - let x = stack.get_mut(0); - +pub fn byte(i: U256, x: U256) -> U256 { if i >= 32 { - *x = U256::ZERO; + U256::ZERO } else { - *x = U256::from_u64(x.byte(31 - i.low_u64() as usize) as u64); + U256::from_u64(x.byte(31 - i.low_u64() as usize) as u64) } } #[inline] -pub fn shl(stack: &mut Stack) { - let shift = stack.pop(); - let value = stack.get_mut(0); - +pub fn shl(shift: U256, value: U256) -> U256 { if value.is_zero() || shift >= 256 { - *value = U256::ZERO; + U256::ZERO } else { - *value <<= shift - }; + value << shift + } } #[inline] -pub fn shr(stack: &mut Stack) { - let shift = stack.pop(); - let value = stack.get_mut(0); - +pub fn shr(shift: U256, value: U256) -> U256 { if value.is_zero() || shift >= 256 { - *value = U256::ZERO; + U256::ZERO } else { - *value >>= shift - }; + value >> shift + } } #[inline] -pub fn sar(stack: &mut Stack) { - let shift = stack.pop(); - let mut value = stack.pop(); - +pub fn sar(shift: U256, mut value: U256) -> U256 { let negative = value.i256_is_negative(); if negative { value = value.i256_neg(); } - stack.push(if value.is_zero() || shift >= 256 { + if value.is_zero() || shift >= 256 { if negative { // value is < 0, pushing U256::MAX (== -1) U256::MAX @@ -64,12 +52,13 @@ pub fn sar(stack: &mut Stack) { } else { value >> shift } - }); + } } #[cfg(test)] mod tests { use super::*; + use crate::interpreter::stack::Stack; #[test] fn test_instruction_byte() { @@ -77,29 +66,35 @@ mod tests { for i in 0u16..32 { let mut stack = Stack::new(); - stack.push(value); - stack.push(U256::from(i)); + unsafe { + stack.push(value); + stack.push(U256::from(i)); + } - byte(&mut stack); - let result = stack.pop(); + crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + let result = unsafe { stack.pop() }; assert_eq!(result, U256::from(5 * (i + 1))); } let mut stack = Stack::new(); - stack.push(value); - stack.push(U256::from(100u128)); + unsafe { + stack.push(value); + stack.push(U256::from(100u128)); + } - byte(&mut stack); - let result = stack.pop(); + crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + let result = unsafe { stack.pop() }; assert_eq!(result, U256::zero()); let mut stack = Stack::new(); - stack.push(value); - stack.push(U256::from_u128_words(1, 0)); + unsafe { + stack.push(value); + stack.push(U256::from_u128_words(1, 0)); + } - byte(&mut stack); - let result = stack.pop(); + crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + let result = unsafe { stack.pop() }; assert_eq!(result, U256::zero()); } } diff --git a/actors/evm/src/interpreter/instructions/boolean.rs b/actors/evm/src/interpreter/instructions/boolean.rs index 50963ced7..c1c6e3186 100644 --- a/actors/evm/src/interpreter/instructions/boolean.rs +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -1,77 +1,51 @@ -use { - crate::interpreter::stack::Stack, crate::interpreter::uints::*, crate::interpreter::U256, - std::cmp::Ordering, -}; +use {crate::interpreter::uints::*, crate::interpreter::U256, std::cmp::Ordering}; #[inline] -pub fn lt(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = U256::from_u64((a < *b).into()); +pub fn lt(a: U256, b: U256) -> U256 { + U256::from_u64((a < b).into()) } #[inline] -pub fn gt(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = U256::from_u64((a > *b).into()); +pub fn gt(a: U256, b: U256) -> U256 { + U256::from_u64((a > b).into()) } #[inline] -pub(crate) fn slt(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = U256::from_u64((i256_cmp(a, *b) == Ordering::Less).into()); +pub(crate) fn slt(a: U256, b: U256) -> U256 { + U256::from_u64((i256_cmp(a, b) == Ordering::Less).into()) } #[inline] -pub(crate) fn sgt(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = U256::from_u64((i256_cmp(a, *b) == Ordering::Greater).into()); +pub(crate) fn sgt(a: U256, b: U256) -> U256 { + U256::from_u64((i256_cmp(a, b) == Ordering::Greater).into()) } #[inline] -pub fn eq(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - - *b = U256::from_u64((a == *b).into()); +pub fn eq(a: U256, b: U256) -> U256 { + U256::from_u64((a == b).into()) } #[inline] -pub fn iszero(stack: &mut Stack) { - let a = stack.get_mut(0); - *a = U256::from_u64(a.is_zero().into()); +pub fn iszero(a: U256) -> U256 { + U256::from_u64(a.is_zero().into()) } #[inline] -pub(crate) fn and(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - *b = a & *b; +pub(crate) fn and(a: U256, b: U256) -> U256 { + a & b } #[inline] -pub(crate) fn or(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - *b = a | *b; +pub(crate) fn or(a: U256, b: U256) -> U256 { + a | b } #[inline] -pub(crate) fn xor(stack: &mut Stack) { - let a = stack.pop(); - let b = stack.get_mut(0); - *b = a ^ *b; +pub(crate) fn xor(a: U256, b: U256) -> U256 { + a ^ b } #[inline] -pub(crate) fn not(stack: &mut Stack) { - let v = stack.get_mut(0); - *v = !*v; +pub(crate) fn not(v: U256) -> U256 { + !v } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 1899a8ded..8549c80f0 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,3 +1,5 @@ +#![allow(clippy::too_many_arguments)] + use fvm_ipld_encoding::{BytesDe, BytesSer}; use fvm_shared::{address::Address, METHOD_SEND}; @@ -9,12 +11,11 @@ use { crate::interpreter::instructions::memory::MemoryRegion, crate::interpreter::output::StatusCode, crate::interpreter::precompiles, - crate::interpreter::stack::Stack, crate::interpreter::ExecutionState, crate::interpreter::System, crate::interpreter::U256, crate::RawBytes, - crate::{DelegateCallParams, Method, EVM_CONTRACT_EXECUTION_ERROR, EVM_CONTRACT_REVERTED}, + crate::{DelegateCallParams, Method, EVM_CONTRACT_EXECUTION_ERROR}, fil_actors_runtime::runtime::builtins::Type, fil_actors_runtime::runtime::Runtime, fil_actors_runtime::ActorError, @@ -30,81 +31,154 @@ pub enum CallKind { CallCode, } -pub fn calldataload(state: &mut ExecutionState) { - let index = state.stack.pop(); +pub fn calldataload( + state: &mut ExecutionState, + _: &System, + index: U256, +) -> Result { let input_len = state.input_data.len(); + Ok(if index > U256::from(input_len) { + U256::zero() + } else { + let index_usize = index.as_usize(); + let end = core::cmp::min(index_usize + 32, input_len); - state.stack.push({ - if index > U256::from(input_len) { - U256::zero() - } else { - let index_usize = index.as_usize(); - let end = core::cmp::min(index_usize + 32, input_len); + let mut data = [0; 32]; + data[..end - index_usize].copy_from_slice(&state.input_data[index_usize..end]); - let mut data = [0; 32]; - data[..end - index_usize].copy_from_slice(&state.input_data[index_usize..end]); + U256::from_big_endian(&data) + }) +} - U256::from_big_endian(&data) - } - }); +#[inline] +pub fn calldatasize( + state: &mut ExecutionState, + _: &System, +) -> Result { + Ok(u128::try_from(state.input_data.len()).unwrap().into()) } #[inline] -pub fn calldatasize(state: &mut ExecutionState) { - state.stack.push(u128::try_from(state.input_data.len()).unwrap().into()); +pub fn calldatacopy( + state: &mut ExecutionState, + _: &System, + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), StatusCode> { + copy_to_memory(&mut state.memory, mem_index, size, input_index, &state.input_data, true) } -pub fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { - let mem_index = state.stack.pop(); - let input_index = state.stack.pop(); - let size = state.stack.pop(); +#[inline] +pub fn codesize( + _state: &mut ExecutionState, + _: &System, + code: &[u8], +) -> Result { + Ok(U256::from(code.len())) +} - copy_to_memory(&mut state.memory, mem_index, size, input_index, &state.input_data, true) +#[inline] +pub fn codecopy( + state: &mut ExecutionState, + _: &System, + code: &[u8], + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), StatusCode> { + copy_to_memory(&mut state.memory, mem_index, size, input_index, code, true) } #[inline] -pub fn codesize(stack: &mut Stack, code: &[u8]) { - stack.push(U256::from(code.len())) +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), + ) } -pub fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> { - let mem_index = state.stack.pop(); - let input_index = state.stack.pop(); - let size = state.stack.pop(); +#[inline] +pub fn call_callcode( + 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::CallCode, + (gas, dst, value, input_offset, input_size, output_offset, output_size), + ) +} - copy_to_memory(&mut state.memory, mem_index, size, input_index, code, true) +#[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), + ) } -pub fn call( +#[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, -) -> Result<(), StatusCode> { - let ExecutionState { stack, memory, .. } = state; + params: (U256, U256, U256, U256, U256, U256, U256), +) -> Result { + let ExecutionState { stack: _, memory, .. } = state; // NOTE gas is currently ignored as FVM's send doesn't allow the caller to specify a gas // limit (external invocation gas limit applies). This may changed in the future. - let (gas, dst, value, input_offset, input_size, output_offset, output_size) = match kind { - CallKind::Call | CallKind::CallCode => ( - stack.pop(), - stack.pop(), - stack.pop(), - stack.pop(), - stack.pop(), - stack.pop(), - stack.pop(), - ), - - CallKind::DelegateCall | CallKind::StaticCall => ( - stack.pop(), - stack.pop(), - U256::from(0), - stack.pop(), - stack.pop(), - stack.pop(), - stack.pop(), - ), - }; + let (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 @@ -233,71 +307,5 @@ pub fn call( // 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)?; - stack.push(U256::from(call_result)); - Ok(()) -} - -pub fn callactor( - state: &mut ExecutionState, - system: &System, -) -> Result<(), StatusCode> { - let ExecutionState { stack, memory, .. } = state; - let rt = &*system.rt; // as immutable reference - - // TODO Until we support readonly (static) calls at the fvm level, we disallow callactor - // when in static mode as it is sticky and there are no guarantee of preserving the - // static invariant - if system.readonly { - return Err(StatusCode::StaticModeViolation); - } - - // stack: GAS DEST VALUE METHODNUM INPUT-OFFSET INPUT-SIZE - // NOTE: we don't need output-offset/output-size (which the CALL instructions have) - // becase these are kinda useless; we can just use RETURNDATA anyway. - // NOTE: gas is currently ignored - let _gas = stack.pop(); - let dst = stack.pop(); - let value = stack.pop(); - let method = stack.pop(); - let input_offset = stack.pop(); - let input_size = stack.pop(); - - let input_region = get_memory_region(memory, input_offset, input_size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; - - let result = { - // TODO: this is wrong https://github.com/filecoin-project/ref-fvm/issues/1018 - let dst_addr: EthAddress = dst.into(); - let dst_addr: Address = dst_addr.try_into()?; - - if method.bits() > 64 { - return Err(StatusCode::ArgumentOutOfRange(format!("bad method number: {}", method))); - } - let methodnum = method.as_u64(); - - let input_data = if let Some(MemoryRegion { offset, size }) = input_region { - &memory[offset..][..size.get()] - } else { - &[] - } - .to_vec(); - rt.send(&dst_addr, methodnum, RawBytes::from(input_data), TokenAmount::from(&value)) - }; - - if let Err(ae) = result { - return if ae.exit_code() == EVM_CONTRACT_REVERTED { - // reverted -- we don't have return data yet - // push failure - stack.push(U256::zero()); - Ok(()) - } else { - Err(StatusCode::from(ae)) - }; - } - - // save return_data - state.return_data = result.unwrap().to_vec().into(); - // push success - stack.push(U256::from(1)); - Ok(()) + Ok(U256::from(call_result)) } diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 8e6542e42..939c1f783 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,14 +1,17 @@ use fvm_shared::clock::ChainEpoch; use { - crate::interpreter::{ExecutionState, System, U256}, + crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, }; #[inline] -pub fn blockhash(state: &mut ExecutionState, system: &System) { - let bn = state.stack.pop(); +pub fn blockhash( + _state: &mut ExecutionState, + system: &System, + bn: U256, +) -> Result { let result = bn .try_into() .ok() @@ -29,76 +32,109 @@ pub fn blockhash(state: &mut ExecutionState, system: &System) { U256::from_big_endian(hash) }) .unwrap_or_default(); - state.stack.push(result); + Ok(result) } #[inline] -pub fn caller(state: &mut ExecutionState, _: &System) { - state.stack.push(state.caller.as_evm_word()) +pub fn caller(state: &mut ExecutionState, _: &System) -> Result { + Ok(state.caller.as_evm_word()) } #[inline] -pub fn address(state: &mut ExecutionState, _system: &System) { - state.stack.push(state.receiver.as_evm_word()) +pub fn address( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(state.receiver.as_evm_word()) } #[inline] -pub fn origin(state: &mut ExecutionState, system: &System) { +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"); - state.stack.push(origin_addr.as_evm_word()) + Ok(origin_addr.as_evm_word()) } #[inline] -pub fn call_value(state: &mut ExecutionState, system: &System) { - state.stack.push(U256::from(&system.rt.message().value_received())); +pub fn call_value( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from(&system.rt.message().value_received())) } #[inline] -pub fn coinbase(state: &mut ExecutionState, _system: &System) { +pub fn coinbase( + _state: &mut ExecutionState, + _system: &System, +) -> Result { // TODO do we want to return the zero ID address, or just a plain 0? - state.stack.push(U256::zero()) + Ok(U256::zero()) } #[inline] -pub fn gas_price(state: &mut ExecutionState, system: &System) { +pub fn gas_price( + _state: &mut ExecutionState, + system: &System, +) -> Result { let effective_price = system.rt.base_fee() + system.rt.message().gas_premium(); - state.stack.push(U256::from(&effective_price)); + Ok(U256::from(&effective_price)) } #[inline] -pub fn gas(state: &mut ExecutionState, system: &System) { - state.stack.push(U256::from(system.rt.gas_available())); +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) { - state.stack.push(U256::from(system.rt.tipset_timestamp())); +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) { - state.stack.push(U256::from(system.rt.curr_epoch())); +pub fn block_number( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from(system.rt.curr_epoch())) } #[inline] -pub fn difficulty(state: &mut ExecutionState, _system: &System) { - state.stack.push(U256::zero()); +pub fn difficulty( + _state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(U256::zero()) } #[inline] -pub fn gas_limit(state: &mut ExecutionState, _system: &System) { +pub fn gas_limit( + _state: &mut ExecutionState, + _system: &System, +) -> Result { const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; - state.stack.push(U256::from(BLOCK_GAS_LIMIT)); + Ok(U256::from(BLOCK_GAS_LIMIT)) } #[inline] -pub fn chain_id(state: &mut ExecutionState, _system: &System) { - state.stack.push(U256::from(chainid::CHAINID)); +pub fn chain_id( + _state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(U256::from(chainid::CHAINID)) } #[inline] -pub fn base_fee(state: &mut ExecutionState, system: &System) { - state.stack.push(U256::from(&system.rt.base_fee())) +pub fn base_fee( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from(&system.rt.base_fee())) } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 14ecc03c4..59f42d52f 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -1,14 +1,18 @@ use { - super::memory::get_memory_region, crate::interpreter::output::StatusCode, - crate::interpreter::stack::Stack, crate::interpreter::Bytecode, - crate::interpreter::ExecutionState, crate::interpreter::U256, + super::memory::get_memory_region, + crate::interpreter::output::StatusCode, + crate::interpreter::Bytecode, + crate::interpreter::{ExecutionState, System, U256}, + fil_actors_runtime::runtime::Runtime, }; #[inline] -pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { - let offset = state.stack.pop(); - let size = state.stack.pop(); - +pub fn output( + state: &mut ExecutionState, + _system: &System, + offset: U256, + size: U256, +) -> Result<(), StatusCode> { if let Some(region) = super::memory::get_memory_region(&mut state.memory, offset, size) .map_err(|_| StatusCode::InvalidMemoryAccess)? { @@ -20,16 +24,21 @@ pub fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> { } #[inline] -pub fn returndatasize(state: &mut ExecutionState) { - state.stack.push(U256::from(state.return_data.len())); +pub fn returndatasize( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(U256::from(state.return_data.len())) } #[inline] -pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { - let mem_index = state.stack.pop(); - let input_index = state.stack.pop(); - let size = state.stack.pop(); - +pub fn returndatacopy( + state: &mut ExecutionState, + _system: &System, + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), StatusCode> { let region = get_memory_region(&mut state.memory, mem_index, size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -51,29 +60,23 @@ pub fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> { } #[inline] -pub fn pc(stack: &mut Stack, pc: usize) { - stack.push(U256::from(pc)) -} - -#[inline] -pub fn jump(stack: &mut Stack, bytecode: &Bytecode) -> Result { - let dst = stack.pop().as_usize(); +pub fn jump(bytecode: &Bytecode, dest: U256) -> Result, StatusCode> { + let dst = dest.as_usize(); if !bytecode.valid_jump_destination(dst) { return Err(StatusCode::BadJumpDestination); } - Ok(dst) + Ok(Some(dst)) } #[inline] -pub fn jumpi(stack: &mut Stack, bytecode: &Bytecode) -> Result, StatusCode> { - if *stack.get(1) != U256::zero() { - let ret = Ok(Some(jump(stack, bytecode)?)); - stack.pop(); - ret +pub fn jumpi(bytecode: &Bytecode, dest: U256, test: U256) -> Result, StatusCode> { + if !test.is_zero() { + let dst = dest.as_usize(); + if !bytecode.valid_jump_destination(dst) { + return Err(StatusCode::BadJumpDestination); + } + Ok(Some(dst)) } else { - stack.pop(); - stack.pop(); - Ok(None) } } diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 77bd6dc6c..e5234ca06 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -14,10 +14,10 @@ use { #[inline] pub fn extcodesize( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &System, -) -> Result<(), StatusCode> { - let addr = state.stack.pop(); + addr: U256, +) -> Result { // TODO 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 @@ -25,30 +25,29 @@ pub fn extcodesize( .and_then(|cid| get_evm_bytecode(system.rt, &cid)) .map(|bytecode| bytecode.len())?; - state.stack.push(len.into()); - Ok(()) + Ok(len.into()) } pub fn extcodehash( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &System, -) -> Result<(), StatusCode> { - let addr = state.stack.pop(); + addr: U256, +) -> Result { let cid = get_evm_bytecode_cid(system.rt, addr)?; let digest = cid.hash().digest(); // Take the first 32 bytes of the Multihash let digest_len = digest.len().min(32); - state.stack.push(digest[..digest_len].into()); - Ok(()) + Ok(digest[..digest_len].into()) } pub fn extcodecopy( state: &mut ExecutionState, system: &System, + addr: U256, + dest_offset: U256, + data_offset: U256, + size: U256, ) -> Result<(), StatusCode> { - let ExecutionState { stack, .. } = state; - let (addr, dest_offset, data_offset, size) = - (stack.pop(), stack.pop(), stack.pop(), stack.pop()); let bytecode = get_evm_bytecode_cid(system.rt, addr).and_then(|cid| get_evm_bytecode(system.rt, &cid))?; diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 0895f1a72..0526d2092 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -6,12 +6,11 @@ use { }; pub fn keccak256( - system: &System, state: &mut ExecutionState, -) -> Result<(), StatusCode> { - let index = state.stack.pop(); - let size = state.stack.pop(); - + system: &System, + index: U256, + size: U256, +) -> Result { let region = get_memory_region(&mut state.memory, index, size) // .map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -24,7 +23,5 @@ pub fn keccak256( }, ); - state.stack.push(U256::from_big_endian(&buf[..size])); - - Ok(()) + Ok(U256::from_big_endian(&buf[..size])) } diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index fe2957156..0fd367210 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -5,7 +5,6 @@ use fvm_shared::MethodNum; use fvm_shared::{address::Address, econ::TokenAmount}; use serde_tuple::{Deserialize_tuple, Serialize_tuple}; -use crate::interpreter::stack::Stack; use crate::interpreter::{address::EthAddress, U256}; use super::memory::{get_memory_region, MemoryRegion}; @@ -42,15 +41,15 @@ pub struct EamReturn { pub fn create( state: &mut ExecutionState, system: &mut System, -) -> Result<(), StatusCode> { + value: U256, + offset: U256, + size: U256, +) -> Result { if system.readonly { return Err(StatusCode::StaticModeViolation); } - let ExecutionState { stack, memory, .. } = state; - - let value = stack.pop(); - let (offset, size) = (stack.pop(), stack.pop()); + let ExecutionState { stack: _, memory, .. } = state; let value = TokenAmount::from(&value); let input_region = @@ -66,25 +65,24 @@ pub fn create( let nonce = system.increment_nonce(); let params = CreateParams { code: input_data.to_vec(), nonce }; - create_init(stack, system, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) + create_init(system, RawBytes::serialize(¶ms)?, CREATE_METHOD_NUM, value) } pub fn create2( state: &mut ExecutionState, system: &mut System, -) -> Result<(), StatusCode> { + endowment: U256, + offset: U256, + size: U256, + salt: U256, +) -> Result { if system.readonly { return Err(StatusCode::StaticModeViolation); } - let ExecutionState { stack, memory, .. } = state; + let ExecutionState { stack: _, memory, .. } = state; // see `create()` overall TODOs - - let endowment = stack.pop(); - let (offset, size) = (stack.pop(), stack.pop()); - let salt = stack.pop(); - let endowment = TokenAmount::from(&endowment); let input_region = get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -102,17 +100,17 @@ pub fn create2( let params = Create2Params { code: input_data.to_vec(), salt }; system.increment_nonce(); - create_init(stack, system, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) + create_init(system, RawBytes::serialize(¶ms)?, CREATE2_METHOD_NUM, endowment) } /// call into Ethereum Address Manager to make the new account +#[inline] fn create_init( - stack: &mut Stack, system: &mut System, params: RawBytes, method: MethodNum, value: TokenAmount, -) -> Result<(), StatusCode> { +) -> Result { // send bytecode & params to EAM to generate the address and contract let ret = system.send(&EAM_ACTOR_ADDR, method, params, value); @@ -135,27 +133,25 @@ fn create_init( // TODO Exit with revert if sys out of gas when subcall gas limits are introduced // https://github.com/filecoin-project/ref-fvm/issues/966 - let word = match ret { + Ok(match ret { Ok(eam_ret) => { let ret: EamReturn = eam_ret.deserialize()?; ret.eth_address.as_evm_word() } Err(_) => U256::zero(), - }; - - stack.push(word); - Ok(()) + }) } #[inline] pub fn selfdestruct( state: &mut ExecutionState, system: &mut System, + beneficiary: U256, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); } - let beneficiary_addr: EthAddress = state.stack.pop().into(); + let beneficiary_addr: EthAddress = beneficiary.into(); // TODO: how do we handle errors here? Just ignore them? state.selfdestroyed = beneficiary_addr.try_into().ok(); Ok(()) diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs index fd72ea910..d6f0d8d02 100644 --- a/actors/evm/src/interpreter/instructions/log.rs +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -2,7 +2,7 @@ use crate::interpreter::instructions::memory::get_memory_region; use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; use fvm_shared::event::{Entry, Flags}; use { - crate::interpreter::{ExecutionState, StatusCode, System}, + crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, }; @@ -17,21 +17,23 @@ pub fn log( state: &mut ExecutionState, system: &System, num_topics: usize, + mem_index: U256, + size: U256, + topics: &[U256], ) -> Result<(), StatusCode> { // 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 mem_index = state.stack.pop(); - let size = state.stack.pop(); let region = get_memory_region(&mut state.memory, mem_index, size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; // 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 key in EVENT_TOPIC_KEYS.iter().take(num_topics) { - let topic = state.stack.pop(); + for i in 0..num_topics { + let key = EVENT_TOPIC_KEYS[i]; + let topic = topics[i]; let entry = Entry { flags: Flags::FLAG_INDEXED_VALUE, key: (*key).to_owned(), diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 695fe055e..82318d766 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -2,7 +2,8 @@ use { crate::interpreter::memory::Memory, - crate::interpreter::{ExecutionState, StatusCode, U256}, + crate::interpreter::{ExecutionState, StatusCode, System, U256}, + fil_actors_runtime::runtime::Runtime, std::num::NonZeroUsize, }; @@ -106,25 +107,27 @@ pub fn copy_to_memory( } #[inline] -pub fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> { - let index = state.stack.pop(); - +pub fn mload( + state: &mut ExecutionState, + _system: &System, + index: U256, +) -> Result { let region = get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); - state.stack.push(value); - - Ok(()) + Ok(value) } #[inline] -pub fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> { - let index = state.stack.pop(); - let value = state.stack.pop(); - +pub fn mstore( + state: &mut ExecutionState, + _system: &System, + index: U256, + value: U256, +) -> Result<(), StatusCode> { let region = get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) .map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -137,23 +140,27 @@ pub fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> { } #[inline] -pub fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> { - let index = state.stack.pop(); - let value = state.stack.pop(); - +pub fn mstore8( + state: &mut ExecutionState, + _system: &System, + index: U256, + value: U256, +) -> Result<(), StatusCode> { let region = get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(1).unwrap()) .map_err(|_| StatusCode::InvalidMemoryAccess)?; let value = (value.low_u32() & 0xff) as u8; - state.memory[region.offset] = value; Ok(()) } #[inline] -pub fn msize(state: &mut ExecutionState) { - state.stack.push(u64::try_from(state.memory.len()).unwrap().into()); +pub fn msize( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(u64::try_from(state.memory.len()).unwrap().into()) } #[cfg(test)] diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index fa818f3f6..fc07dd3ce 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -12,3 +12,337 @@ pub mod memory; pub mod stack; pub mod state; pub mod storage; + +use crate::interpreter::opcode::{OpCode, StackSpec}; +use crate::interpreter::output::StatusCode; +use crate::interpreter::stack::Stack; +use crate::interpreter::{Bytecode, ExecutionState, System, U256}; +use fil_actors_runtime::runtime::Runtime; + +// 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) => { + #[allow(non_snake_case)] + pub fn $op(sk: &mut Stack) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, sk); + $(let $arg = unsafe {sk.pop()};)* + let result = $impl($($arg),*); + unsafe {sk.push(result);} + Ok(()) + } + } +} + +// stackops: operate directly on the stack +macro_rules! def_stackop { + ($op:ident => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(sk: &mut Stack) -> Result<(), StatusCode> { + check_stack!($op, sk); + unsafe { + $impl(sk); + } + Ok(()) + } + }; +} + +// pushops: push stuff on the stack given input bytecode; the kind of thing that makes you want +// to cry because it really is a stack op. +macro_rules! def_push { + ($op:ident => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(sk: &mut Stack, code: &[u8]) -> Result { + check_stack!($op, sk); + let off = unsafe { $impl(sk, code) }; + Ok(off) + } + }; +} + +// 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) => { + #[allow(non_snake_case)] + pub fn $op(state: &mut ExecutionState, system: &mut System) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, state.stack); + $(let $arg = unsafe {state.stack.pop()};)* + let result = $impl(state, system, $($arg),*)?; + unsafe {state.stack.push(result);} + Ok(()) + } + } +} + +// stdproc: like stdfun, but returns no value +macro_rules! def_stdproc { + ($op:ident ($($arg:ident),*) => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(state: &mut ExecutionState, system: &mut System) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, state.stack); + $(let $arg = unsafe {state.stack.pop()};)* + $impl(state, system, $($arg),*)?; + Ok(()) + } + } +} + +// std*_code: code reflective functionoid +macro_rules! def_stdfun_code { + ($op:ident ($($arg:ident),*) => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(state: &mut ExecutionState, system: &mut System, code: &[u8]) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, state.stack); + $(let $arg = unsafe {state.stack.pop()};)* + let result = $impl(state, system, code, $($arg),*)?; + unsafe {state.stack.push(result);} + Ok(()) + } + } +} + +macro_rules! def_stdproc_code { + ($op:ident ($($arg:ident),*) => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(state: &mut ExecutionState, system: &mut System, code: &[u8]) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, state.stack); + $(let $arg = unsafe {state.stack.pop()};)* + $impl(state, system, code, $($arg),*)?; + Ok(()) + } + } +} + +// stdproc: logging functionoid +macro_rules! def_stdlog { + ($op:ident ($ntopics:literal, ($($topic:ident),*))) => { + #[allow(non_snake_case)] + pub fn $op(state: &mut ExecutionState, system: &System) -> Result<(), StatusCode> { + check_stack!($op, state.stack); + let a = unsafe {state.stack.pop()}; + let b = unsafe {state.stack.pop()}; + $(let $topic = unsafe {state.stack.pop()};)* + log::log(state, system, $ntopics, a, b, &[$($topic),*]) + } + } +} + +// jmp: jump variants +macro_rules! def_jmp { + ($op:ident ($($arg:ident),*) => $impl:path) => { + #[allow(non_snake_case)] + pub fn $op(sk: &mut Stack, bytecode: &Bytecode) -> Result, StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, sk); + $(let $arg = unsafe {sk.pop()};)* + $impl(bytecode, $($arg),*) + } + } + +} + +// special: pc and things like that +macro_rules! def_special { + ($op:ident ($($arg:ident: $t:ty),*) => $value:expr) => { + #[allow(non_snake_case)] + pub fn $op(sk: &mut Stack, $($arg:$t),*) -> Result<(), StatusCode> { + check_stack!($op, sk); + let result = $value; + unsafe {sk.push(result)}; + Ok(()) + } + } + +} + +// auxiliary macros +macro_rules! check_stack { + ($op:ident, $sk:expr) => {{ + const SPEC: StackSpec = OpCode::$op.spec(); + if SPEC.required > 0 { + if !$sk.require(SPEC.required as usize) { + return Err(StatusCode::StackUnderflow); + } + } + if SPEC.changed > 0 { + if !$sk.ensure(SPEC.changed as usize) { + return Err(StatusCode::StackOverflow); + } + } + }}; +} + +macro_rules! check_arity { + ($op:ident, ($($arg:ident),*)) => {{ + #[allow(dead_code)] + const fn checkargs() { + const SPEC: StackSpec = OpCode::$op.spec(); + // the error message is super ugly, but this static asserts we got the + // arity of the primop right. + const _: [();(arg_count!($($arg)*)) - SPEC.required as usize] = []; + } + checkargs(); + }} +} + +macro_rules! arg_count { + () => { 0 }; + ($arg:ident $($rest:ident)*) => { 1 + arg_count!($($rest)*) }; +} + +// 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! { 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! { DIFFICULTY() => context::difficulty } +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! { CALLCODE(gas, dst, value, ioff, isz, ooff, osz) => call::call_callcode } +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! { RETURN(a, b) => control::output } +def_stdproc! { REVERT(a, b) => control::output } +def_stdproc! { SELFDESTRUCT(a) => lifecycle::selfdestruct } +def_jmp! { JUMP(a) => control::jump } +def_jmp! { JUMPI(a, b) => control::jumpi } +def_special! { PC(pc: usize) => U256::from(pc) } diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index 231a7c4de..f30837a2f 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -1,7 +1,8 @@ +#![allow(clippy::missing_safety_doc)] use {crate::interpreter::stack::Stack, crate::interpreter::U256}; #[inline] -pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> usize { +pub(crate) unsafe fn push(stack: &mut Stack, code: &[u8]) -> usize { let pushval = &code[..LEN]; stack.push(match pushval.len() { 0 => U256::zero(), @@ -16,16 +17,16 @@ pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> usize { } #[inline] -pub(crate) fn dup(stack: &mut Stack) { +pub(crate) unsafe fn dup(stack: &mut Stack) { stack.push(*stack.get(HEIGHT - 1)); } #[inline] -pub(crate) fn swap(stack: &mut Stack) { +pub(crate) unsafe fn swap(stack: &mut Stack) { stack.swap_top(HEIGHT); } #[inline] -pub(crate) fn pop(stack: &mut Stack) { +pub(crate) unsafe fn pop(stack: &mut Stack) { stack.pop(); } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index fa8d7dc51..6de7d3aa3 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -9,10 +9,11 @@ use { #[inline] pub fn balance( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &System, -) -> Result<(), StatusCode> { - let actor: EthAddress = state.stack.pop().into(); + actor: U256, +) -> Result { + let actor: EthAddress = actor.into(); let balance = actor .try_into() @@ -21,13 +22,15 @@ pub fn balance( .and_then(|id| system.rt.actor_balance(id).as_ref().map(U256::from)) .unwrap_or_default(); - state.stack.push(balance); - Ok(()) + Ok(balance) } #[inline] -pub fn selfbalance(state: &mut ExecutionState, system: &System) { +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). - state.stack.push(U256::from(&system.rt.current_balance())) + Ok(U256::from(&system.rt.current_balance())) } diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 6d2296794..52ded26a6 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -1,33 +1,28 @@ use { - crate::interpreter::{ExecutionState, StatusCode, System}, + crate::interpreter::{ExecutionState, StatusCode, System, U256}, fil_actors_runtime::runtime::Runtime, }; #[inline] pub fn sload( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &mut System, -) -> Result<(), StatusCode> { - // where? - let location = state.stack.pop(); - + location: U256, +) -> Result { // get from storage and place on stack - state.stack.push(system.get_storage(location)?); - Ok(()) + system.get_storage(location) } #[inline] pub fn sstore( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &mut System, + key: U256, + value: U256, ) -> Result<(), StatusCode> { if system.readonly { return Err(StatusCode::StaticModeViolation); } - let key = state.stack.pop(); - let value = state.stack.pop(); - - system.set_storage(key, value)?; - Ok(()) + system.set_storage(key, value) } diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index 0482db106..27e24ca54 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -1,62 +1,93 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::missing_safety_doc)] -use {crate::interpreter::U256, arrayvec::ArrayVec, serde::Serialize}; +use crate::interpreter::U256; /// Ethereum Yellow Paper (9.1) -pub const MAX_STACK_SIZE: usize = 1024; +pub const STACK_SIZE: usize = 1024; + +const INITIAL_STACK_SIZE: usize = 32; /// EVM stack. -#[derive(Clone, Debug, Default, Serialize)] -pub struct Stack(pub ArrayVec); +#[derive(Clone, Debug)] +pub struct Stack { + sk: Vec, + d: usize, +} impl Stack { #[inline] - pub const fn new() -> Self { - Self(ArrayVec::new_const()) + pub fn new() -> Self { + Stack { sk: Vec::from([U256::zero(); INITIAL_STACK_SIZE]), d: 0 } } #[inline] - const fn get_pos(&self, pos: usize) -> usize { - self.len() - 1 - pos + pub fn require(&self, required: usize) -> bool { + required <= self.d } #[inline] - pub fn get(&self, pos: usize) -> &U256 { - let pos = self.get_pos(pos); - self.0.get(pos).unwrap() - } + pub fn ensure(&mut self, space: usize) -> bool { + let required = self.d + space; - #[inline] - pub fn get_mut(&mut self, pos: usize) -> &mut U256 { - let pos = self.get_pos(pos); - self.0.get_mut(pos).unwrap() + if required > self.sk.len() { + if required > STACK_SIZE { + return false; + } + + while required > self.sk.len() { + self.sk.resize(2 * self.sk.len(), U256::zero()); + } + } + true } #[inline(always)] - pub const fn len(&self) -> usize { - self.0.len() + pub fn len(&self) -> usize { + self.d } #[inline(always)] pub fn is_empty(&self) -> bool { - self.len() == 0 + self.d == 0 } #[inline] - pub fn push(&mut self, v: U256) { - self.0.push(v) + pub unsafe fn get(&self, i: usize) -> &U256 { + let pos = self.d - i - 1; + self.sk.get_unchecked(pos) } #[inline] - pub fn pop(&mut self) -> U256 { - self.0.pop().unwrap() + pub unsafe fn get_mut(&mut self, i: usize) -> &mut U256 { + let pos = self.d - i - 1; + self.sk.get_unchecked_mut(pos) } #[inline] - pub fn swap_top(&mut self, pos: usize) { - let top = self.0.len() - 1; - let pos = self.get_pos(pos); - self.0.swap(top, pos); + pub unsafe fn push(&mut self, v: U256) { + *self.sk.get_unchecked_mut(self.d) = v; + self.d += 1; + } + + #[inline] + pub unsafe fn pop(&mut self) -> U256 { + self.d -= 1; + *self.sk.get_unchecked(self.d) + } + + #[inline] + pub unsafe fn swap_top(&mut self, i: usize) { + let top = self.d - 1; + let pos = self.d - i - 1; + let tmp = *self.sk.get_unchecked(top); + *self.sk.get_unchecked_mut(top) = *self.sk.get_unchecked(pos); + *self.sk.get_unchecked_mut(pos) = tmp; + } +} + +impl Default for Stack { + fn default() -> Self { + Self::new() } } @@ -66,17 +97,17 @@ mod tests { #[test] fn stack() { - let mut stack = Stack::default(); + let mut stack = Stack::new(); let items: [u128; 4] = [0xde, 0xad, 0xbe, 0xef]; for (i, item) in items.iter().copied().enumerate() { - stack.push(item.into()); + unsafe { stack.push(item.into()) }; assert_eq!(stack.len(), i + 1); } - assert_eq!(*stack.get(2), U256::from(0xad)); - assert_eq!(stack.pop(), U256::from(0xef)); - assert_eq!(*stack.get(2), U256::from(0xde)); + assert_eq!(unsafe { *stack.get(2) }, U256::from(0xad)); + assert_eq!(unsafe { stack.pop() }, U256::from(0xef)); + assert_eq!(unsafe { *stack.get(2) }, U256::from(0xde)); } } From 8a5a26b9d6355731c3f2c9c013dd58aab38f7b95 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 29 Nov 2022 19:59:40 +0200 Subject: [PATCH 124/339] EVM: Further dispatch refactoring (#876) * defeat instruction zoo call convention complexity it's just one, the intrinsic, which passes a machine. * fix tests * rip ControlFlow enum * more macrology * cosmetics * shut up clippy --- actors/evm/src/interpreter/execution.rs | 450 +++++++++--------- .../interpreter/instructions/arithmetic.rs | 243 ++++++---- .../src/interpreter/instructions/bitwise.rs | 15 +- .../evm/src/interpreter/instructions/mod.rs | 109 +++-- 4 files changed, 439 insertions(+), 378 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 671da9da5..2bdf434d4 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -46,26 +46,23 @@ impl ExecutionState { } pub struct Machine<'r, 'a, RT: Runtime + 'a> { - system: &'r mut System<'a, RT>, - state: &'r mut ExecutionState, - bytecode: &'r Bytecode, - pc: usize, - reverted: bool, -} + pub system: &'r mut System<'a, RT>, + pub state: &'r mut ExecutionState, + pub bytecode: &'r Bytecode, + pub pc: usize, -enum ControlFlow { - Continue, - Jump, - Exit, + // control flow + reverted: bool, + exit: Option, } -type Instruction = fn(*mut M) -> Result; +type Instruction = fn(*mut M); macro_rules! def_opcodes { ($($op:ident: $body:tt)*) => { def_ins_raw! { - UNDEFINED(_m) { - Err(StatusCode::UndefinedInstruction) + UNDEFINED(m) { + m.exit = Some(StatusCode::UndefinedInstruction); } } $(def_ins! { $op $body })* @@ -86,24 +83,12 @@ macro_rules! def_jmptable { } macro_rules! def_ins { - ($ins:ident {primop}) => { - def_ins_primop! { $ins } - }; - - ($ins:ident {push}) => { - def_ins_push! { $ins } - }; - - ($ins:ident {std}) => { - def_ins_std! { $ins } - }; - - ($ins:ident {code}) => { - def_ins_code! { $ins } + ($ins:ident {intrinsic}) => { + def_ins_intrinsic! { $ins } }; ($ins:ident {=> $expr:expr}) => { - def_ins_raw! { $ins (_m) { $expr } } + def_ins_raw! { $ins (m) { m.exit = Some($expr); } } }; ($ins:ident {($arg:ident) => $body:block}) => { @@ -114,7 +99,7 @@ macro_rules! def_ins { macro_rules! def_ins_raw { ($ins:ident ($arg:ident) $body:block) => { #[allow(non_snake_case)] - fn $ins(p: *mut Self) -> Result { + fn $ins(p: *mut Self) { // 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 Self = unsafe { p.as_mut().unwrap() }; $body @@ -122,45 +107,38 @@ macro_rules! def_ins_raw { }; } -macro_rules! def_ins_primop { +macro_rules! def_ins_intrinsic { ($ins:ident) => { def_ins_raw! { $ins (m) { - instructions::$ins(&mut m.state.stack)?; - Ok(ControlFlow::Continue) + match instructions::$ins(m) { + Ok(_) => { + m.pc += 1; + }, + Err(err) => { + m.exit = Some(err); + } + } } } }; } -macro_rules! def_ins_push { - ($ins:ident) => { - def_ins_raw! { - $ins (m) { - m.pc += instructions::$ins(&mut m.state.stack, &m.bytecode[m.pc + 1..])?; - Ok(ControlFlow::Continue) +macro_rules! try_ins { + ($ins:ident($m:ident) => ($res:ident) $body:block) => { + match instructions::$ins($m) { + Ok($res) => $body, + Err(err) => { + $m.exit = Some(err); } } }; -} -macro_rules! def_ins_std { - ($ins:ident) => { - def_ins_raw! { - $ins (m) { - instructions::$ins(m.state, m.system)?; - Ok(ControlFlow::Continue) - } - } - }; -} - -macro_rules! def_ins_code { - ($ins:ident) => { - def_ins_raw! { - $ins (m) { - instructions::$ins(m.state, m.system, m.bytecode.as_ref())?; - Ok(ControlFlow::Continue) + ($ins:ident($m:ident) => $body:block) => { + match instructions::$ins($m) { + Ok(_) => $body, + Err(err) => { + $m.exit = Some(err); } } }; @@ -172,7 +150,7 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { state: &'r mut ExecutionState, bytecode: &'r Bytecode, ) -> Self { - Machine { system, state, bytecode, pc: 0, reverted: false } + Machine { system, state, bytecode, pc: 0, reverted: false, exit: None } } pub fn execute(&mut self) -> Result<(), StatusCode> { @@ -181,222 +159,236 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { break; } - match self.step()? { - ControlFlow::Continue => { - self.pc += 1; - } - ControlFlow::Jump => {} - ControlFlow::Exit => { - break; - } - }; + self.step(); + + if self.exit.is_some() { + break; + } } - Ok(()) + match &self.exit { + None => Ok(()), + Some(StatusCode::Success) => Ok(()), + Some(err) => Err(err.clone()), + } } - fn step(&mut self) -> Result { - let op = OpCode::try_from(self.bytecode[self.pc])?; - Self::JMPTABLE[op as usize](self) + fn step(&mut self) { + let op = OpCode::try_from(self.bytecode[self.pc]); + match op { + Ok(op) => Self::JMPTABLE[op as usize](self), + Err(err) => self.exit = Some(err), + } } def_opcodes! { - STOP: {=> Ok(ControlFlow::Exit)} + STOP: {=> StatusCode::Success} // primops - ADD: {primop} - MUL: {primop} - SUB: {primop} - DIV: {primop} - SDIV: {primop} - MOD: {primop} - SMOD: {primop} - ADDMOD: {primop} - MULMOD: {primop} - EXP: {primop} - SIGNEXTEND: {primop} - LT: {primop} - GT: {primop} - SLT: {primop} - SGT: {primop} - EQ: {primop} - ISZERO: {primop} - AND: {primop} - OR: {primop} - XOR: {primop} - NOT: {primop} - BYTE: {primop} - SHL: {primop} - SHR: {primop} - SAR: {primop} - POP: {primop} + ADD: {intrinsic} + MUL: {intrinsic} + SUB: {intrinsic} + DIV: {intrinsic} + SDIV: {intrinsic} + MOD: {intrinsic} + SMOD: {intrinsic} + ADDMOD: {intrinsic} + MULMOD: {intrinsic} + EXP: {intrinsic} + SIGNEXTEND: {intrinsic} + LT: {intrinsic} + GT: {intrinsic} + SLT: {intrinsic} + SGT: {intrinsic} + EQ: {intrinsic} + ISZERO: {intrinsic} + AND: {intrinsic} + OR: {intrinsic} + XOR: {intrinsic} + NOT: {intrinsic} + BYTE: {intrinsic} + SHL: {intrinsic} + SHR: {intrinsic} + SAR: {intrinsic} // std call convenction functionoids - KECCAK256: {std} - ADDRESS: {std} - BALANCE: {std} - ORIGIN: {std} - CALLER: {std} - CALLVALUE: {std} - CALLDATALOAD: {std} - CALLDATASIZE: {std} - CALLDATACOPY: {std} - CODESIZE: {code} - CODECOPY: {code} - GASPRICE: {std} - EXTCODESIZE: {std} - EXTCODECOPY: {std} - RETURNDATASIZE: {std} - RETURNDATACOPY: {std} - EXTCODEHASH: {std} - BLOCKHASH: {std} - COINBASE: {std} - TIMESTAMP: {std} - NUMBER: {std} - DIFFICULTY: {std} - GASLIMIT: {std} - CHAINID: {std} - BASEFEE: {std} - SELFBALANCE: {std} - MLOAD: {std} - MSTORE: {std} - MSTORE8: {std} - SLOAD: {std} - SSTORE: {std} - MSIZE: {std} - GAS: {std} + KECCAK256: {intrinsic} + ADDRESS: {intrinsic} + BALANCE: {intrinsic} + ORIGIN: {intrinsic} + CALLER: {intrinsic} + CALLVALUE: {intrinsic} + CALLDATALOAD: {intrinsic} + CALLDATASIZE: {intrinsic} + CALLDATACOPY: {intrinsic} + CODESIZE: {intrinsic} + CODECOPY: {intrinsic} + GASPRICE: {intrinsic} + EXTCODESIZE: {intrinsic} + EXTCODECOPY: {intrinsic} + RETURNDATASIZE: {intrinsic} + RETURNDATACOPY: {intrinsic} + EXTCODEHASH: {intrinsic} + BLOCKHASH: {intrinsic} + COINBASE: {intrinsic} + TIMESTAMP: {intrinsic} + NUMBER: {intrinsic} + DIFFICULTY: {intrinsic} + GASLIMIT: {intrinsic} + CHAINID: {intrinsic} + BASEFEE: {intrinsic} + SELFBALANCE: {intrinsic} + MLOAD: {intrinsic} + MSTORE: {intrinsic} + MSTORE8: {intrinsic} + SLOAD: {intrinsic} + SSTORE: {intrinsic} + MSIZE: {intrinsic} + GAS: {intrinsic} + + // stack ops + POP: {intrinsic} // push variants - PUSH1: {push} - PUSH2: {push} - PUSH3: {push} - PUSH4: {push} - PUSH5: {push} - PUSH6: {push} - PUSH7: {push} - PUSH8: {push} - PUSH9: {push} - PUSH10: {push} - PUSH11: {push} - PUSH12: {push} - PUSH13: {push} - PUSH14: {push} - PUSH15: {push} - PUSH16: {push} - PUSH17: {push} - PUSH18: {push} - PUSH19: {push} - PUSH20: {push} - PUSH21: {push} - PUSH22: {push} - PUSH23: {push} - PUSH24: {push} - PUSH25: {push} - PUSH26: {push} - PUSH27: {push} - PUSH28: {push} - PUSH29: {push} - PUSH30: {push} - PUSH31: {push} - PUSH32: {push} + PUSH1: {intrinsic} + PUSH2: {intrinsic} + PUSH3: {intrinsic} + PUSH4: {intrinsic} + PUSH5: {intrinsic} + PUSH6: {intrinsic} + PUSH7: {intrinsic} + PUSH8: {intrinsic} + PUSH9: {intrinsic} + PUSH10: {intrinsic} + PUSH11: {intrinsic} + PUSH12: {intrinsic} + PUSH13: {intrinsic} + PUSH14: {intrinsic} + PUSH15: {intrinsic} + PUSH16: {intrinsic} + PUSH17: {intrinsic} + PUSH18: {intrinsic} + PUSH19: {intrinsic} + PUSH20: {intrinsic} + PUSH21: {intrinsic} + PUSH22: {intrinsic} + PUSH23: {intrinsic} + PUSH24: {intrinsic} + PUSH25: {intrinsic} + PUSH26: {intrinsic} + PUSH27: {intrinsic} + PUSH28: {intrinsic} + PUSH29: {intrinsic} + PUSH30: {intrinsic} + PUSH31: {intrinsic} + PUSH32: {intrinsic} // dup variants - DUP1: {primop} - DUP2: {primop} - DUP3: {primop} - DUP4: {primop} - DUP5: {primop} - DUP6: {primop} - DUP7: {primop} - DUP8: {primop} - DUP9: {primop} - DUP10: {primop} - DUP11: {primop} - DUP12: {primop} - DUP13: {primop} - DUP14: {primop} - DUP15: {primop} - DUP16: {primop} + DUP1: {intrinsic} + DUP2: {intrinsic} + DUP3: {intrinsic} + DUP4: {intrinsic} + DUP5: {intrinsic} + DUP6: {intrinsic} + DUP7: {intrinsic} + DUP8: {intrinsic} + DUP9: {intrinsic} + DUP10: {intrinsic} + DUP11: {intrinsic} + DUP12: {intrinsic} + DUP13: {intrinsic} + DUP14: {intrinsic} + DUP15: {intrinsic} + DUP16: {intrinsic} // swap variants - SWAP1: {primop} - SWAP2: {primop} - SWAP3: {primop} - SWAP4: {primop} - SWAP5: {primop} - SWAP6: {primop} - SWAP7: {primop} - SWAP8: {primop} - SWAP9: {primop} - SWAP10: {primop} - SWAP11: {primop} - SWAP12: {primop} - SWAP13: {primop} - SWAP14: {primop} - SWAP15: {primop} - SWAP16: {primop} + SWAP1: {intrinsic} + SWAP2: {intrinsic} + SWAP3: {intrinsic} + SWAP4: {intrinsic} + SWAP5: {intrinsic} + SWAP6: {intrinsic} + SWAP7: {intrinsic} + SWAP8: {intrinsic} + SWAP9: {intrinsic} + SWAP10: {intrinsic} + SWAP11: {intrinsic} + SWAP12: {intrinsic} + SWAP13: {intrinsic} + SWAP14: {intrinsic} + SWAP15: {intrinsic} + SWAP16: {intrinsic} // event logs - LOG0: {std} - LOG1: {std} - LOG2: {std} - LOG3: {std} - LOG4: {std} + LOG0: {intrinsic} + LOG1: {intrinsic} + LOG2: {intrinsic} + LOG3: {intrinsic} + LOG4: {intrinsic} // create variants - CREATE: {std} - CREATE2: {std} + CREATE: {intrinsic} + CREATE2: {intrinsic} // call variants - CALL: {std} - CALLCODE: {std} - DELEGATECALL: {std} - STATICCALL: {std} + CALL: {intrinsic} + CALLCODE: {intrinsic} + DELEGATECALL: {intrinsic} + STATICCALL: {intrinsic} // control flow magic - JUMPDEST: {=> Ok(ControlFlow::Continue)} // noop marker opcode for valid jumps addresses + // noop marker opcode for valid jumps addresses + JUMPDEST: {(m) => { + m.pc += 1; + }} JUMP: {(m) => { - if let Some(dest) = instructions::JUMP(&mut m.state.stack, m.bytecode)? { - m.pc = dest; - Ok(ControlFlow::Jump) - } else { - // cant happen, unless it's a cosmic ray - Err(StatusCode::Failure) - } + try_ins! { JUMP(m) => (res) { + if let Some(dest) = res { + m.pc = dest; + } else { + // cant happen, unless it's a cosmic ray + m.exit = Some(StatusCode::Failure); + } + }} }} JUMPI: {(m) => { - if let Some(dest) = instructions::JUMPI(&mut m.state.stack, m.bytecode)? { - m.pc = dest; - Ok(ControlFlow::Jump) - } else { - Ok(ControlFlow::Continue) - } + try_ins! { JUMPI(m) => (res) { + if let Some(dest) = res { + m.pc = dest; + } else { + m.pc += 1; + } + }} }} PC: {(m) => { - instructions::PC(&mut m.state.stack, m.pc)?; - Ok(ControlFlow::Continue) + try_ins! { PC(m) => { + m.pc += 1; + }} }} RETURN: {(m) => { - instructions::RETURN(m.state, m.system)?; - Ok(ControlFlow::Exit) + try_ins! { RETURN(m) => { + m.exit = Some(StatusCode::Success); + }} }} REVERT: {(m) => { - instructions::REVERT(m.state, m.system)?; - m.reverted = true; - Ok(ControlFlow::Exit) + try_ins! { REVERT(m) => { + m.reverted = true; + m.exit = Some(StatusCode::Success); + }} }} SELFDESTRUCT: {(m) => { - instructions::SELFDESTRUCT(m.state, m.system)?; - Ok(ControlFlow::Exit) // selfdestruct halts the current context + try_ins! { SELFDESTRUCT(m) => { + m.exit = Some(StatusCode::Success); + }} }} - INVALID: {=> Err(StatusCode::InvalidInstruction)} + INVALID: {=> StatusCode::InvalidInstruction} } const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 310b832c2..139a52adc 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -110,9 +110,6 @@ pub fn exp(mut base: U256, power: U256) -> U256 { #[cfg(test)] mod test { - use super::*; - use crate::interpreter::stack::Stack; - mod basic { use crate::interpreter::{stack::Stack, U256}; @@ -137,36 +134,90 @@ mod test { } } + pub fn ins_add(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::add(a, b); + s.push(c); + } + } + + pub fn ins_mul(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::mul(a, b); + s.push(c); + } + } + + pub fn ins_sub(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::sub(a, b); + s.push(c); + } + } + + pub fn ins_div(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::div(a, b); + s.push(c); + } + } + + pub fn ins_signextend(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::signextend(a, b); + s.push(c); + } + } + + pub fn ins_exp(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::arithmetic::exp(a, b); + s.push(c); + } + } + #[test] fn add() { let mut s = Stack::default(); let s = &mut s; push_2(s, 0, 0); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, 0, "add nothing to nothing"); // does "math" on all limbs, so it is different than above push_2(s, U256::max_value(), 0); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, U256::max_value(), "add nothing to max value"); push_2(s, 2, 2); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, 4, "2 plus 2 equals 5 (???)"); push_2(s, u64::MAX, 32); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, u64::MAX as u128 + 32, "add 32 past a single (u64) limb of u256"); // wrap to zero push_2(s, U256::max_value(), 1); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, 0, "overflow by one"); // wrap all limbs push_2(s, U256::max_value(), U256::max_value()); - crate::interpreter::instructions::ADD(s).unwrap(); + ins_add(s); expect_stack_value(s, U256::max_value() - 1, "overflow by max, should be 2^256-1"); } @@ -176,15 +227,15 @@ mod test { let s = &mut s; push_2(s, 0, 0); - crate::interpreter::instructions::MUL(s).unwrap(); + ins_mul(s); expect_stack_value(s, 0, "multiply nothing by nothing"); push_2(s, 2, 3); - crate::interpreter::instructions::MUL(s).unwrap(); + ins_mul(s); expect_stack_value(s, 6, "multiply 2 by 3"); push_2(s, u64::MAX, 2); - crate::interpreter::instructions::MUL(s).unwrap(); + ins_mul(s); expect_stack_value(s, (u64::MAX as u128) * 2, "2^64 x 2"); } @@ -194,25 +245,25 @@ mod test { let s = &mut s; push_2(s, 0, 0); - crate::interpreter::instructions::SUB(s).unwrap(); + ins_sub(s); expect_stack_value(s, 0, "subtract nothing by nothing"); push_2(s, 2, 1); - crate::interpreter::instructions::SUB(s).unwrap(); + ins_sub(s); expect_stack_value(s, 1, "subtract 2 by 1"); push_2(s, (u64::MAX as u128) + 32, 64); - crate::interpreter::instructions::SUB(s).unwrap(); + ins_sub(s); expect_stack_value(s, u64::MAX - 32, "subtract 64 from a value 32 over a single limb"); // wrap to max push_2(s, 0, 1); - crate::interpreter::instructions::SUB(s).unwrap(); + ins_sub(s); expect_stack_value(s, U256::max_value(), "wrap around to max by one"); // wrap all limbs push_2(s, U256::max_value(), U256::max_value()); - crate::interpreter::instructions::SUB(s).unwrap(); + ins_sub(s); expect_stack_value(s, 0, "wrap around zero by 2^256"); } @@ -222,97 +273,97 @@ mod test { let s = &mut s; push_2(s, 0, 0); - crate::interpreter::instructions::DIV(s).unwrap(); + ins_div(s); expect_stack_value(s, 0, "divide nothing by nothing (yes)"); push_2(s, 4, 1); - crate::interpreter::instructions::DIV(s).unwrap(); + ins_div(s); expect_stack_value(s, 4, "divide 4 by 1"); push_2(s, u128::MAX, 2); - crate::interpreter::instructions::DIV(s).unwrap(); + ins_div(s); expect_stack_value(s, u128::MAX / 2, "divide 2^128 by 2 (uses >1 limb)"); } - } - #[test] - fn test_signextend() { - macro_rules! assert_exp { - ($num:expr, $byte:expr, $result:expr) => { - let mut stack = Stack::new(); - unsafe { - stack.push(($num).into()); - stack.push(($byte).into()); - } - crate::interpreter::instructions::SIGNEXTEND(&mut stack).unwrap(); - let res: U256 = ($result).into(); - assert_eq!(res, unsafe { stack.pop() }); - }; + #[test] + fn test_signextend() { + macro_rules! assert_exp { + ($num:expr, $byte:expr, $result:expr) => { + let mut stack = Stack::new(); + unsafe { + stack.push(($num).into()); + stack.push(($byte).into()); + } + ins_signextend(&mut stack); + let res: U256 = ($result).into(); + assert_eq!(res, unsafe { stack.pop() }); + }; + } + 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)); } - 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 mut stack = Stack::new(); - unsafe { - stack.push(($exp).into()); - stack.push(($base).into()); - } - crate::interpreter::instructions::EXP(&mut stack).unwrap(); - let res: U256 = ($result).into(); - assert_eq!(res, unsafe { stack.pop() }); - }; - } + #[test] + fn test_exp() { + macro_rules! assert_exp { + ($base:expr, $exp:expr, $result:expr) => { + let mut stack = Stack::new(); + unsafe { + stack.push(($exp).into()); + stack.push(($base).into()); + } + ins_exp(&mut stack); + let res: U256 = ($result).into(); + assert_eq!(res, unsafe { stack.pop() }); + }; + } - // 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)); - } + // 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) - ); + // 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 index 22f083ccf..b9800c0f7 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -60,6 +60,15 @@ mod tests { use super::*; use crate::interpreter::stack::Stack; + pub fn ins_byte(s: &mut Stack) { + unsafe { + let a = s.pop(); + let b = s.pop(); + let c = crate::interpreter::instructions::bitwise::byte(a, b); + s.push(c); + } + } + #[test] fn test_instruction_byte() { let value = U256::from_big_endian(&(1u8..=32u8).map(|x| 5 * x).collect::>()); @@ -71,7 +80,7 @@ mod tests { stack.push(U256::from(i)); } - crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + ins_byte(&mut stack); let result = unsafe { stack.pop() }; assert_eq!(result, U256::from(5 * (i + 1))); @@ -83,7 +92,7 @@ mod tests { stack.push(U256::from(100u128)); } - crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + ins_byte(&mut stack); let result = unsafe { stack.pop() }; assert_eq!(result, U256::zero()); @@ -93,7 +102,7 @@ mod tests { stack.push(U256::from_u128_words(1, 0)); } - crate::interpreter::instructions::BYTE(&mut stack).unwrap(); + ins_byte(&mut stack); let result = unsafe { stack.pop() }; assert_eq!(result, U256::zero()); } diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index fc07dd3ce..67da737b9 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::unnecessary_mut_passed)] + pub mod arithmetic; pub mod bitwise; pub mod boolean; @@ -13,10 +15,10 @@ pub mod stack; pub mod state; pub mod storage; +use crate::interpreter::execution::Machine; use crate::interpreter::opcode::{OpCode, StackSpec}; use crate::interpreter::output::StatusCode; -use crate::interpreter::stack::Stack; -use crate::interpreter::{Bytecode, ExecutionState, System, U256}; +use crate::interpreter::U256; use fil_actors_runtime::runtime::Runtime; // macros for the instruction zoo: @@ -24,12 +26,12 @@ use fil_actors_runtime::runtime::Runtime; macro_rules! def_primop { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(sk: &mut Stack) -> Result<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, sk); - $(let $arg = unsafe {sk.pop()};)* + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* let result = $impl($($arg),*); - unsafe {sk.push(result);} + unsafe {m.state.stack.push(result);} Ok(()) } } @@ -39,25 +41,30 @@ macro_rules! def_primop { macro_rules! def_stackop { ($op:ident => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(sk: &mut Stack) -> Result<(), StatusCode> { - check_stack!($op, sk); + pub fn $op<'r, 'a, RT: Runtime + 'a>( + m: &mut Machine<'r, 'a, RT>, + ) -> Result<(), StatusCode> { + check_stack!($op, m.state.stack); unsafe { - $impl(sk); + $impl(&mut m.state.stack); } Ok(()) } }; } -// pushops: push stuff on the stack given input bytecode; the kind of thing that makes you want -// to cry because it really is a stack op. +// pusho 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. macro_rules! def_push { ($op:ident => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(sk: &mut Stack, code: &[u8]) -> Result { - check_stack!($op, sk); - let off = unsafe { $impl(sk, code) }; - Ok(off) + pub fn $op<'r, 'a, RT: Runtime + 'a>( + m: &mut Machine<'r, 'a, RT>, + ) -> Result<(), StatusCode> { + check_stack!($op, m.state.stack); + let code = &m.bytecode[m.pc + 1..]; + m.pc += unsafe { $impl(&mut m.state.stack, code) }; + Ok(()) } }; } @@ -67,12 +74,12 @@ macro_rules! def_push { macro_rules! def_stdfun { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(state: &mut ExecutionState, system: &mut System) -> Result<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, state.stack); - $(let $arg = unsafe {state.stack.pop()};)* - let result = $impl(state, system, $($arg),*)?; - unsafe {state.stack.push(result);} + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + let result = $impl(&mut m.state, &mut m.system, $($arg),*)?; + unsafe {m.state.stack.push(result);} Ok(()) } } @@ -82,11 +89,11 @@ macro_rules! def_stdfun { macro_rules! def_stdproc { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(state: &mut ExecutionState, system: &mut System) -> Result<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, state.stack); - $(let $arg = unsafe {state.stack.pop()};)* - $impl(state, system, $($arg),*)?; + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + $impl(&mut m.state, &mut m.system, $($arg),*)?; Ok(()) } } @@ -96,25 +103,26 @@ macro_rules! def_stdproc { macro_rules! def_stdfun_code { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(state: &mut ExecutionState, system: &mut System, code: &[u8]) -> Result<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, state.stack); - $(let $arg = unsafe {state.stack.pop()};)* - let result = $impl(state, system, code, $($arg),*)?; - unsafe {state.stack.push(result);} + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + let result = $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; + unsafe {m.state.stack.push(result);} Ok(()) } } } +// and the procedural variant macro_rules! def_stdproc_code { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(state: &mut ExecutionState, system: &mut System, code: &[u8]) -> Result<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, state.stack); - $(let $arg = unsafe {state.stack.pop()};)* - $impl(state, system, code, $($arg),*)?; + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; Ok(()) } } @@ -124,12 +132,12 @@ macro_rules! def_stdproc_code { macro_rules! def_stdlog { ($op:ident ($ntopics:literal, ($($topic:ident),*))) => { #[allow(non_snake_case)] - pub fn $op(state: &mut ExecutionState, system: &System) -> Result<(), StatusCode> { - check_stack!($op, state.stack); - let a = unsafe {state.stack.pop()}; - let b = unsafe {state.stack.pop()}; - $(let $topic = unsafe {state.stack.pop()};)* - log::log(state, system, $ntopics, a, b, &[$($topic),*]) + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { + check_stack!($op, m.state.stack); + let a = unsafe {m.state.stack.pop()}; + let b = unsafe {m.state.stack.pop()}; + $(let $topic = unsafe {m.state.stack.pop()};)* + log::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*]) } } } @@ -138,11 +146,11 @@ macro_rules! def_stdlog { macro_rules! def_jmp { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op(sk: &mut Stack, bytecode: &Bytecode) -> Result, StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result, StatusCode> { check_arity!($op, ($($arg),*)); - check_stack!($op, sk); - $(let $arg = unsafe {sk.pop()};)* - $impl(bytecode, $($arg),*) + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + $impl(m.bytecode, $($arg),*) } } @@ -150,16 +158,17 @@ macro_rules! def_jmp { // special: pc and things like that macro_rules! def_special { - ($op:ident ($($arg:ident: $t:ty),*) => $value:expr) => { + ($op:ident ($m:ident) => $value:expr) => { #[allow(non_snake_case)] - pub fn $op(sk: &mut Stack, $($arg:$t),*) -> Result<(), StatusCode> { - check_stack!($op, sk); + pub fn $op<'r, 'a, RT: Runtime + 'a>( + $m: &mut Machine<'r, 'a, RT>, + ) -> Result<(), StatusCode> { + check_stack!($op, $m.state.stack); let result = $value; - unsafe {sk.push(result)}; + unsafe { $m.state.stack.push(result) }; Ok(()) } - } - + }; } // auxiliary macros @@ -345,4 +354,4 @@ def_stdproc! { REVERT(a, b) => control::output } def_stdproc! { SELFDESTRUCT(a) => lifecycle::selfdestruct } def_jmp! { JUMP(a) => control::jump } def_jmp! { JUMPI(a, b) => control::jumpi } -def_special! { PC(pc: usize) => U256::from(pc) } +def_special! { PC(m) => U256::from(m.pc) } From a9b27f4646a264f39a0fc9abfb5c15f634d333e5 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 29 Nov 2022 18:21:22 -0600 Subject: [PATCH 125/339] Update FVM (#877) * Update FVM * feat: correctly handle "read-only" errors. Co-authored-by: Steven Allen --- Cargo.lock | 336 +++++++++++++++++++++++++++---------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- runtime/src/actor_error.rs | 27 ++- runtime/src/runtime/fvm.rs | 6 +- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 21 files changed, 294 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0551e8556..a2b0b5b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,11 +24,22 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[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.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -56,11 +67,11 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ - "concurrent-queue 1.2.4", + "concurrent-queue", "event-listener", "futures-core", ] @@ -73,7 +84,7 @@ checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ "async-lock", "async-task", - "concurrent-queue 2.0.0", + "concurrent-queue", "fastrand", "futures-lite", "slab", @@ -96,13 +107,13 @@ dependencies = [ [[package]] name = "async-io" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" dependencies = [ "async-lock", "autocfg", - "concurrent-queue 1.2.4", + "concurrent-queue", "futures-lite", "libc", "log", @@ -111,7 +122,7 @@ dependencies = [ "slab", "socket2", "waker-fn", - "winapi", + "windows-sys", ] [[package]] @@ -158,9 +169,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" dependencies = [ "proc-macro2", "quote", @@ -325,7 +336,7 @@ checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] @@ -336,20 +347,20 @@ checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", - "constant_time_eq", + "constant_time_eq 0.2.4", ] [[package]] @@ -393,16 +404,61 @@ dependencies = [ [[package]] name = "blocking" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" dependencies = [ "async-channel", + "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", - "once_cell", +] + +[[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]] @@ -429,6 +485,27 @@ 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" @@ -437,19 +514,13 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" dependencies = [ "serde", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "camino" version = "1.1.1" @@ -470,9 +541,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406c859255d568f4f742b3146d51851f3bfd49f734a2c289d9107c4395ee0062" +checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" dependencies = [ "camino", "cargo-platform", @@ -493,9 +564,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.76" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" @@ -632,15 +703,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - [[package]] name = "concurrent-queue" version = "2.0.0" @@ -662,6 +724,12 @@ 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.4.0" @@ -694,9 +762,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -1770,7 +1838,7 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "frc42_hasher", "frc42_macros", @@ -1783,7 +1851,7 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "fvm_sdk", "fvm_shared", @@ -1793,7 +1861,7 @@ dependencies = [ [[package]] name = "frc42_macros" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "blake2b_simd", "frc42_hasher", @@ -1805,7 +1873,7 @@ dependencies = [ [[package]] name = "frc46_token" version = "1.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "anyhow", "cid", @@ -1954,7 +2022,7 @@ dependencies = [ [[package]] name = "fvm_actor_utils" version = "0.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#aa39d65847a57cea2fcf04bab464bac75cf99b97" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "anyhow", "cid", @@ -2060,9 +2128,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2af95e6a8d41b4335686f83897756cb984299c789803325a1a62abcb5df2e0" +checksum = "820720405923ef64f9ec5b1c7bb523629763d888f34904c8ff3bdd69de652195" dependencies = [ "cid", "fvm_ipld_encoding", @@ -2075,9 +2143,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.12" +version = "3.0.0-alpha.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9fcc1caa8878cffebb092aeeab04bf559e03fe4d8e5c3c2d7805b589669da7" +checksum = "1c716adb0c618a2ce9c3e464cdbe1f00c65cc0a18e83d4aae7be38e52895e0a5" dependencies = [ "anyhow", "bitflags", @@ -2145,9 +2213,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b" dependencies = [ "futures-channel", "futures-core", @@ -2185,11 +2253,23 @@ dependencies = [ "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" @@ -2369,12 +2449,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] @@ -2633,7 +2713,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", @@ -2768,9 +2848,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.4.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "parity-scale-codec" @@ -2792,7 +2872,7 @@ version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn", @@ -2886,9 +2966,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a" dependencies = [ "thiserror", "ucd-trie", @@ -2896,9 +2976,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fd9bc6500181952d34bd0b2b0163a54d794227b498be0b7afa7698d0a7b18f" +checksum = "423c2ba011d6e27b02b482a3707c773d19aec65cc024637aec44e19652e66f63" dependencies = [ "pest", "pest_generator", @@ -2906,9 +2986,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2610d5ac5156217b4ff8e46ddcef7cdf44b273da2ac5bca2ecbfa86a330e7c4" +checksum = "3e64e6c2c85031c02fdbd9e5c72845445ca0a724d419aa0bc068ac620c9935c1" dependencies = [ "pest", "pest_meta", @@ -2919,9 +2999,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824749bf7e21dd66b36fbe26b3f45c713879cccd4a009a917ab8e045ca8246fe" +checksum = "57959b91f0a133f89a68be874a5c88ed689c19cd729ecdb5d762ebf16c64d662" dependencies = [ "once_cell", "pest", @@ -2982,16 +3062,16 @@ dependencies = [ [[package]] name = "polling" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" +checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" dependencies = [ "autocfg", "cfg-if", "libc", "log", "wepoll-ffi", - "winapi", + "windows-sys", ] [[package]] @@ -3013,6 +3093,15 @@ dependencies = [ "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" @@ -3056,6 +3145,26 @@ 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 = "quote" version = "1.0.21" @@ -3133,11 +3242,20 @@ 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.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ "base64 0.13.1", "bytes", @@ -3207,6 +3325,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" @@ -3230,13 +3373,20 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.26.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +checksum = "33c321ee4e17d2b7abe12b5d20c1231db708dd36185c8a21e9de5fed6da4dbe9" dependencies = [ "arrayvec", + "borsh", + "bytecheck", + "byteorder", + "bytes", "num-traits", + "rand", + "rkyv", "serde", + "serde_json", ] [[package]] @@ -3334,6 +3484,12 @@ dependencies = [ "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" @@ -3365,9 +3521,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" dependencies = [ "serde_derive", ] @@ -3402,9 +3558,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" dependencies = [ "proc-macro2", "quote", @@ -3425,9 +3581,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", @@ -3663,9 +3819,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" dependencies = [ "proc-macro2", "quote", @@ -3820,9 +3976,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" dependencies = [ "autocfg", "bytes", @@ -3937,9 +4093,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "uint" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", @@ -4292,9 +4448,9 @@ dependencies = [ [[package]] name = "wyz" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 4c0b717ca..ae60a60d8 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index d318dc8bc..8f7067ab3 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index 86483f606..0c44fb720 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index c8a6b225e..a2a5ee19a 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index ae5429573..c4b58eddb 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.14", optional = true } -fvm_shared = { version = "3.0.0-alpha.12", optional = true } +fvm_sdk = { version = "3.0.0-alpha.15", optional = true } +fvm_shared = { version = "3.0.0-alpha.13", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index ec2fe9be2..80290cdf2 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 0d7a3ca71..f833a7d0c 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 5c18f91c7..c00190079 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -23,7 +23,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 446d8112a..c6322569b 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 7f56fe138..2ab3f6b08 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", 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 2fed3d2d2..3f5777b6d 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 2a48747ca..85602cc2c 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 75705688d..4f3a6cbd6 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index c567ec9ff..4042c0462 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 2120bc220..89d124099 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a05f34ace..83ce3832e 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.14", optional = true } +fvm_sdk = { version = "3.0.0-alpha.15", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index d67cd0718..854711e84 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -95,18 +95,21 @@ impl From for ActorError { impl From for ActorError { fn from(e: fvm_sdk::error::ActorDeleteError) -> Self { Self { - exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, + exit_code: match e { + fvm_sdk::error::ActorDeleteError::ReadOnly => ExitCode::USR_READ_ONLY, + _ => ExitCode::USR_ILLEGAL_ARGUMENT, + }, msg: e.to_string(), data: RawBytes::default(), } } } -/// Converts a no-state error into an an actor error with the appropriate exit code (illegal actor). +/// Converts a state read error into an an actor error with the appropriate exit code (illegal actor). /// This facilitates propagation. #[cfg(feature = "fil-actor")] -impl From for ActorError { - fn from(e: fvm_sdk::error::NoStateError) -> Self { +impl From for ActorError { + fn from(e: fvm_sdk::error::StateReadError) -> Self { Self { exit_code: ExitCode::USR_ILLEGAL_STATE, msg: e.to_string(), @@ -115,6 +118,22 @@ impl From for ActorError { } } +/// Converts a state read error into an an actor error with the appropriate exit code (illegal actor). +/// This facilitates propagation. +#[cfg(feature = "fil-actor")] +impl From for ActorError { + fn from(e: fvm_sdk::error::StateUpdateError) -> Self { + Self { + exit_code: match e { + fvm_sdk::error::StateUpdateError::ActorDeleted => ExitCode::USR_ILLEGAL_STATE, + fvm_sdk::error::StateUpdateError::ReadOnly => ExitCode::USR_READ_ONLY, + }, + msg: e.to_string(), + data: RawBytes::default(), + } + } +} + /// Convenience macro for generating Actor Errors #[macro_export] macro_rules! actor_error { diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 57c0618d0..77efd86de 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -310,7 +310,7 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - match fvm::send::send(to, method, params, value) { + match fvm::send::send(to, method, params, value, None) { Ok(ret) => { if ret.exit_code.is_success() { Ok(ret.return_data) @@ -358,6 +358,10 @@ where // TODO: Define a better exit code. actor_error!(assertion_failed; "recursion limit exceeded") } + ErrorNumber::ReadOnly => ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "attempted to mutate state while in readonly mode".into(), + ), err => { // We don't expect any other syscall exit codes. actor_error!(assertion_failed; "unexpected error: {}", err) diff --git a/state/Cargo.toml b/state/Cargo.toml index 253a8ae59..577641d6b 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index e8aa09358..be21623e9 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -37,7 +37,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.12", default-features = false } +fvm_shared = { version = "3.0.0-alpha.13", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From c26adc9f6ac5166247ea70d6defca32438b57342 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 29 Nov 2022 16:35:07 -0800 Subject: [PATCH 126/339] fix: correctly compute addresses in CREATE (#878) * fix: correctly compute addresses in CREATE And add some basic tests. fixes #866 * Apply suggestions from code review Co-authored-by: Melanie Riise Co-authored-by: Melanie Riise --- actors/eam/src/lib.rs | 130 ++++++++++++++++++++++++------------- actors/eam/tests/create.rs | 18 ++--- 2 files changed, 91 insertions(+), 57 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 7b4b34715..45c69c5d0 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -4,7 +4,6 @@ use ext::init::{Exec4Params, Exec4Return}; use fil_actors_runtime::AsActorError; use fvm_ipld_encoding::Cbor; use fvm_shared::error::ExitCode; -use rlp::Encodable; pub mod ext; @@ -37,18 +36,22 @@ pub enum Method { // CreateAccount = 4, } -#[derive(Debug)] -/// Intermediate type between RLP encoding for CREATE -pub struct RlpCreateAddress { - pub address: EthAddress, - pub nonce: u64, +/// Compute the a new actor address using the EVM's CREATE rules. +pub fn compute_address_create(rt: &impl Runtime, from: &EthAddress, nonce: u64) -> EthAddress { + let mut stream = rlp::RlpStream::new(); + stream.begin_list(2).append(&&from.0[..]).append(&nonce); + EthAddress(hash_20(rt, &stream.out())) } -impl rlp::Encodable for RlpCreateAddress { - fn rlp_append(&self, s: &mut rlp::RlpStream) { - s.encoder().encode_value(&self.address.0); - s.append(&self.nonce); - } +/// Compute the a new actor address using the EVM's CREATE2 rules. +pub fn compute_address_create2( + rt: &impl Runtime, + from: &EthAddress, + salt: &[u8; 32], + initcode: &[u8], +) -> EthAddress { + let inithash = rt.hash(SupportedHashes::Keccak256, initcode); + EthAddress(hash_20(rt, &[&[0xff], &from.0[..], salt, &inithash].concat())) } #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, PartialEq, Eq)] @@ -210,8 +213,7 @@ impl EamActor { let caller_addr = resolve_caller(rt)?; // CREATE logic - let rlp = RlpCreateAddress { address: caller_addr, nonce: params.nonce }; - let eth_addr = EthAddress(hash_20(rt, &rlp.rlp_bytes())); + let eth_addr = compute_address_create(rt, &caller_addr, params.nonce); // send to init actor create_actor(rt, caller_addr, eth_addr, params.initcode) @@ -226,16 +228,11 @@ impl EamActor { ) -> Result { rt.validate_immediate_caller_accept_any()?; - // CREATE2 logic - let inithash = rt.hash(SupportedHashes::Keccak256, ¶ms.initcode); - // Try to lookup the caller's EVM address, but otherwise derive one from the ID address. let caller_addr = resolve_caller(rt)?; - let eth_addr = EthAddress(hash_20( - rt, - &[&[0xff], &caller_addr.0[..], ¶ms.salt, &inithash].concat(), - )); + // Compute the CREATE2 address + let eth_addr = compute_address_create2(rt, &caller_addr, ¶ms.salt, ¶ms.initcode); // send to init actor create_actor(rt, caller_addr, eth_addr, params.initcode) @@ -297,30 +294,75 @@ impl ActorCode for EamActor { } } -#[test] -fn test_create_actor_rejects() { +#[cfg(test)] +mod test { use fil_actors_runtime::test_utils::MockRuntime; use fvm_shared::error::ExitCode; - let mut rt = MockRuntime::default(); - let mut creator = EthAddress([0; 20]); - creator.0[0] = 0xff; - creator.0[19] = 0x1; - - // Reject ID. - let mut new_addr = EthAddress([0; 20]); - new_addr.0[0] = 0xff; - new_addr.0[18] = 0x20; - new_addr.0[19] = 0x20; - assert_eq!( - ExitCode::USR_FORBIDDEN, - create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() - ); - - // Reject Precompile. - let mut new_addr = EthAddress([0; 20]); - new_addr.0[19] = 0x20; - assert_eq!( - ExitCode::USR_FORBIDDEN, - create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() - ); + + use crate::compute_address_create2; + + use super::{compute_address_create, create_actor, EthAddress}; + + #[test] + fn test_create_actor_rejects() { + let mut rt = MockRuntime::default(); + let mut creator = EthAddress([0; 20]); + creator.0[0] = 0xff; + creator.0[19] = 0x1; + + // Reject ID. + let mut new_addr = EthAddress([0; 20]); + new_addr.0[0] = 0xff; + new_addr.0[18] = 0x20; + new_addr.0[19] = 0x20; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); + + // Reject Precompile. + let mut new_addr = EthAddress([0; 20]); + new_addr.0[19] = 0x20; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); + } + + #[test] + fn test_create_address() { + let rt = MockRuntime::default(); + // check addresses against externally generated cases + for (from, nonce, expected) in &[ + ([0u8; 20], 0u64, hex_literal::hex!("bd770416a3345f91e4b34576cb804a576fa48eb1")), + ([0; 20], 200, hex_literal::hex!("a6b14387c1356b443061155e9c3e17f72c1777e5")), + ([123; 20], 12345, hex_literal::hex!("809a9ab0471e78ee5100e96ca4d0828d1b97e2ba")), + ] { + let result = compute_address_create(&rt, &EthAddress(*from), *nonce); + assert_eq!(result.0[..], expected[..]); + } + } + + #[test] + fn test_create_address2() { + let rt = MockRuntime::default(); + // check addresses against externally generated cases + for (from, salt, initcode, expected) in &[ + ( + [0u8; 20], + [0u8; 32], + &b""[..], + hex_literal::hex!("e33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0"), + ), + ( + [0x99u8; 20], + [0x42; 32], + &b"foobar"[..], + hex_literal::hex!("64425c93a90901271fa355c2bc462190803b97d4"), + ), + ] { + let result = compute_address_create2(&rt, &EthAddress(*from), salt, initcode); + assert_eq!(result.0[..], expected[..]); + } + } } diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index f8d2c483d..5ba9d7f6a 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -1,6 +1,6 @@ use eam::ext::init::{Exec4Params, Exec4Return, EXEC4_METHOD}; use eam::{ - Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, RlpCreateAddress, + compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, }; use fil_actor_eam as eam; use fil_actors_runtime::runtime::builtins::Type; @@ -13,7 +13,6 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use rlp::Encodable; #[test] fn call_create() { @@ -26,15 +25,11 @@ fn call_create() { let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; - let rlp_params = RlpCreateAddress { address: eth_addr, nonce: 0 }; - let mut subaddress = - rt.hash(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &rlp_params.rlp_bytes()); - subaddress.drain(..12); - + let new_eth_addr = compute_address_create(rt, ð_addr, 0); let params = Exec4Params { code_cid: *EVM_ACTOR_CODE_ID, constructor_params: RawBytes::serialize(evm_params).unwrap(), - subaddress: subaddress.clone().into(), + subaddress: new_eth_addr.0[..].to_owned().into(), }; let send_return = RawBytes::serialize(Exec4Return { @@ -61,11 +56,8 @@ fn call_create() { .deserialize::() .unwrap(); - let expected_return = Return { - actor_id: 111, - robust_address: Address::new_id(0), - eth_address: EthAddress(subaddress.try_into().unwrap()), - }; + let expected_return = + Return { actor_id: 111, robust_address: Address::new_id(0), eth_address: new_eth_addr }; assert_eq!(result, expected_return); rt.verify(); From 57e15f3c9c4b5008e0b4d8050852e94978b62f70 Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Mon, 7 Nov 2022 01:08:56 +1100 Subject: [PATCH 127/339] Proof of concept exported API for Account actor (#797) --- actors/account/src/lib.rs | 7 ++- actors/account/tests/account_actor_test.rs | 73 +++++++++++++--------- runtime/src/builtin/shared.rs | 36 ++++++++++- runtime/src/test_utils.rs | 32 +++++----- test_vm/tests/test_vm_test.rs | 9 ++- 5 files changed, 105 insertions(+), 52 deletions(-) diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index 25a630dcc..2f27a5208 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -12,7 +12,7 @@ use num_traits::FromPrimitive; use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorError}; +use fil_actors_runtime::{actor_error, restrict_internal_api, ActorError}; use fil_actors_runtime::{cbor, ActorDowncast}; use crate::types::AuthenticateMessageParams; @@ -33,6 +33,7 @@ pub enum Method { Constructor = METHOD_CONSTRUCTOR, PubkeyAddress = 2, AuthenticateMessage = 3, + AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -109,6 +110,8 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; + match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -118,7 +121,7 @@ impl ActorCode for Actor { let addr = Self::pubkey_address(rt)?; Ok(RawBytes::serialize(addr)?) } - Some(Method::AuthenticateMessage) => { + Some(Method::AuthenticateMessage) | Some(Method::AuthenticateMessageExported) => { Self::authenticate_message(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index a437ca7c5..4f9bdf3a0 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -16,12 +16,8 @@ use fil_actors_runtime::test_utils::*; #[test] fn construction() { fn construct(addr: Address, exit_code: ExitCode) { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); if exit_code.is_success() { @@ -59,12 +55,8 @@ fn construction() { #[test] fn token_receiver() { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let param = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(); @@ -74,6 +66,7 @@ fn token_receiver() { ) .unwrap(); + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); rt.expect_validate_caller_any(); let ret = rt.call::( Method::UniversalReceiverHook as MethodNum, @@ -83,25 +76,15 @@ fn token_receiver() { assert_eq!(RawBytes::default(), ret.unwrap()); } -fn check_state(rt: &MockRuntime) { - let test_address = Address::new_id(1000); - let (_, acc) = check_state_invariants(&rt.get_state(), &test_address); - acc.assert_empty(); -} - #[test] fn authenticate_message() { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); let addr = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); - - rt.call::(1, &RawBytes::serialize(addr).unwrap()).unwrap(); + rt.call::(Method::Constructor as MethodNum, &RawBytes::serialize(addr).unwrap()) + .unwrap(); let state: State = rt.get_state(); assert_eq!(state.address, addr); @@ -110,6 +93,7 @@ fn authenticate_message() { RawBytes::serialize(AuthenticateMessageParams { signature: vec![], message: vec![] }) .unwrap(); + // Valid signature rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { sig: Signature::new_secp256k1(vec![]), @@ -117,8 +101,13 @@ fn authenticate_message() { plaintext: vec![], result: Ok(()), }); - assert_eq!(RawBytes::default(), rt.call::(3, ¶ms).unwrap()); + assert_eq!( + RawBytes::default(), + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms).unwrap() + ); + rt.verify(); + // Invalid signature rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { sig: Signature::new_secp256k1(vec![]), @@ -126,10 +115,34 @@ fn authenticate_message() { plaintext: vec![], result: Err(anyhow!("bad signature")), }); - assert_eq!( + expect_abort_contains_message( ExitCode::USR_ILLEGAL_ARGUMENT, - rt.call::(3, ¶ms).unwrap_err().exit_code() + "bad signature", + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), ); - rt.verify(); + + // Invalid caller of internal method number + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), + ); + + // Ok to call exported method number + rt.expect_validate_caller_any(); + rt.expect_verify_signature(ExpectedVerifySig { + sig: Signature::new_secp256k1(vec![]), + signer: addr, + plaintext: vec![], + result: Ok(()), + }); + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms).unwrap(); +} + +fn check_state(rt: &MockRuntime) { + let test_address = Address::new_id(1000); + let (_, acc) = check_state_invariants(&rt.get_state(), &test_address); + acc.assert_empty(); } diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 5abc54a2c..aea1180c0 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -3,8 +3,8 @@ use crate::{actor_error, ActorContext, ActorError}; use fvm_shared::address::Address; -use fvm_shared::ActorID; use fvm_shared::METHOD_SEND; +use fvm_shared::{ActorID, MethodNum}; use crate::runtime::builtins::Type; use crate::runtime::Runtime; @@ -38,3 +38,37 @@ pub fn resolve_to_actor_id( Err(actor_error!(illegal_argument, "failed to resolve or initialize address {}", address)) } + +// The lowest FRC-42 method number. +pub const FIRST_EXPORTED_METHOD_NUMBER: MethodNum = 1 << 24; + +// Checks whether the caller is allowed to invoke some method number. +// All method numbers below the FRC-42 range are restricted to built-in actors +// (including the account and multisig actors). +// Methods may subsequently enforce tighter restrictions. +pub fn restrict_internal_api(rt: &mut RT, method: MethodNum) -> Result<(), ActorError> +where + RT: Runtime, +{ + if method >= FIRST_EXPORTED_METHOD_NUMBER { + return Ok(()); + } + let caller = rt.message().caller(); + let code_cid = rt.get_actor_code_cid(&caller.id().unwrap()); + match code_cid { + None => { + return Err( + actor_error!(forbidden; "no code for caller {} of method {}", caller, method), + ) + } + 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), + ); + } + } + } + Ok(()) +} diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 45165df55..b86178719 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -46,21 +46,21 @@ use fvm_shared::event::ActorEvent; use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; lazy_static::lazy_static! { - pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system"); - pub static ref INIT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/init"); - pub static ref CRON_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/cron"); - pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/account"); - pub static ref POWER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagepower"); - pub static ref MINER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storageminer"); - pub static ref MARKET_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagemarket"); - pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/paymentchannel"); - pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/multisig"); - pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward"); - pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry"); - pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/datacap"); - pub static ref EMBRYO_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/embryo"); - pub static ref EVM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/evm"); - pub static ref EAM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/eam"); + pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/system"); + pub static ref INIT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/init"); + pub static ref CRON_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/cron"); + pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/account"); + pub static ref POWER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagepower"); + pub static ref MINER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storageminer"); + pub static ref MARKET_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagemarket"); + pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/paymentchannel"); + pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/multisig"); + pub static ref REWARD_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/reward"); + 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 EMBRYO_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/embryo"); + 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 ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System); @@ -114,7 +114,7 @@ lazy_static::lazy_static! { const IPLD_RAW: u64 = 0x55; /// Returns an identity CID for bz. -pub fn make_builtin(bz: &[u8]) -> Cid { +pub fn make_identity_cid(bz: &[u8]) -> Cid { Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long")) } diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 8311d1faf..b00088cf4 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -1,5 +1,7 @@ use fil_actor_account::State as AccountState; -use fil_actors_runtime::test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID}; +use fil_actors_runtime::test_utils::{ + make_identity_cid, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, +}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -20,7 +22,7 @@ fn state_control() { // set actor let a1 = actor( *ACCOUNT_ACTOR_CODE_ID, - make_builtin(b"a1-head"), + make_identity_cid(b"a1-head"), 42, TokenAmount::from_atto(10u8), None, @@ -32,11 +34,12 @@ fn state_control() { let a2 = actor( *PAYCH_ACTOR_CODE_ID, - make_builtin(b"a2-head"), + make_identity_cid(b"a2-head"), 88, 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 From 2fcd91bbc32a256b2ca29015e1dfc0417df0a26d Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 7 Nov 2022 14:49:19 +0000 Subject: [PATCH 128/339] Export stable methods for public access (#807) * Export Datacap Actor methods * Export Init Actor methods * Export Market Actor methods * Export Miner Actor methods * Export Multisig Actor methods * Export Verifreg Actor methods * Address review --- Cargo.lock | 3 + actors/datacap/src/lib.rs | 48 +++++++---- actors/datacap/tests/datacap_actor_test.rs | 39 ++++++++- actors/datacap/tests/harness/mod.rs | 1 + actors/init/Cargo.toml | 1 + actors/init/src/lib.rs | 8 +- actors/init/tests/init_actor_test.rs | 63 ++++++++++++++- actors/market/Cargo.toml | 1 + actors/market/src/lib.rs | 15 ++-- actors/market/tests/harness.rs | 1 + actors/market/tests/market_actor_test.rs | 49 ++++++++++- actors/miner/Cargo.toml | 1 + actors/miner/src/lib.rs | 41 ++++++---- actors/miner/tests/change_beneficiary_test.rs | 36 ++++++++- actors/miner/tests/declare_recoveries.rs | 2 +- .../tests/miner_actor_test_commitment.rs | 2 +- .../tests/miner_actor_test_construction.rs | 7 ++ actors/miner/tests/report_consensus_fault.rs | 10 +-- actors/multisig/src/lib.rs | 32 +++++--- actors/multisig/tests/multisig_actor_test.rs | 81 ++++++++++++++++--- actors/verifreg/src/lib.rs | 18 +++-- actors/verifreg/tests/harness/mod.rs | 1 + actors/verifreg/tests/verifreg_actor_test.rs | 63 ++++++++++++++- runtime/src/builtin/shared.rs | 2 +- runtime/src/test_utils.rs | 13 +-- 25 files changed, 444 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2b0b5b5d..8af737553 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1536,6 +1536,7 @@ dependencies = [ "anyhow", "cid", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", @@ -1556,6 +1557,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_verifreg", "fil_actors_runtime", + "frc42_dispatch", "frc46_token", "fvm_ipld_amt", "fvm_ipld_bitfield", @@ -1586,6 +1588,7 @@ dependencies = [ "fil_actor_power", "fil_actor_reward", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index 80a1b5cc4..fd9b2df28 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -20,7 +20,8 @@ use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorError, AsActorError, + SYSTEM_ACTOR_ADDR, }; pub use self::state::State; @@ -42,9 +43,10 @@ lazy_static! { * BigInt::from(1_000_000_000_000_000_000_000_i128) ); } -/// Static method numbers for builtin-actor private dispatch. -/// The methods are also expected to be exposed via FRC-XXXX standard calling convention, -/// with numbers determined by name. + +/// Datacap actor methods available +/// Some methods are available under 2 method nums -- a static number for "private" builtin actor usage, +/// and via FRC-XXXX calling convention, with number determined by method name. #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -65,6 +67,19 @@ pub enum Method { Burn = 19, BurnFrom = 20, Allowance = 21, + // Method numbers derived from FRC-XXXX standards + NameExported = frc42_dispatch::method_hash!("Name"), + SymbolExported = frc42_dispatch::method_hash!("Symbol"), + TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), + BalanceOfExported = frc42_dispatch::method_hash!("BalanceOf"), + TransferExported = frc42_dispatch::method_hash!("Transfer"), + TransferFromExported = frc42_dispatch::method_hash!("TransferFrom"), + IncreaseAllowanceExported = frc42_dispatch::method_hash!("IncreaseAllowance"), + DecreaseAllowanceExported = frc42_dispatch::method_hash!("DecreaseAllowance"), + RevokeAllowanceExported = frc42_dispatch::method_hash!("RevokeAllowance"), + BurnExported = frc42_dispatch::method_hash!("Burn"), + BurnFromExported = frc42_dispatch::method_hash!("BurnFrom"), + AllowanceExported = frc42_dispatch::method_hash!("Allowance"), } pub struct Actor; @@ -452,6 +467,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; // I'm trying to find a fixed template for these blocks so we can macro it. // Current blockers: // - the serialize method maps () to CBOR null (we want no bytes instead) @@ -469,51 +485,51 @@ impl ActorCode for Actor { let ret = Self::destroy(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "destroy result") } - Some(Method::Name) => { + Some(Method::Name) | Some(Method::NameExported) => { let ret = Self::name(rt)?; serialize(&ret, "name result") } - Some(Method::Symbol) => { + Some(Method::Symbol) | Some(Method::SymbolExported) => { let ret = Self::symbol(rt)?; serialize(&ret, "symbol result") } - Some(Method::TotalSupply) => { + Some(Method::TotalSupply) | Some(Method::TotalSupplyExported) => { let ret = Self::total_supply(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "total_supply result") } - Some(Method::BalanceOf) => { + Some(Method::BalanceOf) | Some(Method::BalanceOfExported) => { let ret = Self::balance_of(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "balance_of result") } - Some(Method::Transfer) => { + Some(Method::Transfer) | Some(Method::TransferExported) => { let ret = Self::transfer(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer result") } - Some(Method::TransferFrom) => { + Some(Method::TransferFrom) | Some(Method::TransferFromExported) => { let ret = Self::transfer_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer_from result") } - Some(Method::IncreaseAllowance) => { + Some(Method::IncreaseAllowance) | Some(Method::IncreaseAllowanceExported) => { let ret = Self::increase_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "increase_allowance result") } - Some(Method::DecreaseAllowance) => { + Some(Method::DecreaseAllowance) | Some(Method::DecreaseAllowanceExported) => { let ret = Self::decrease_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "decrease_allowance result") } - Some(Method::RevokeAllowance) => { + Some(Method::RevokeAllowance) | Some(Method::RevokeAllowanceExported) => { Self::revoke_allowance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Burn) => { + Some(Method::Burn) | Some(Method::BurnExported) => { let ret = Self::burn(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn result") } - Some(Method::BurnFrom) => { + Some(Method::BurnFrom) | Some(Method::BurnFromExported) => { let ret = Self::burn_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn_from result") } - Some(Method::Allowance) => { + Some(Method::Allowance) | Some(Method::AllowanceExported) => { let ret = Self::allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "allowance result") } diff --git a/actors/datacap/tests/datacap_actor_test.rs b/actors/datacap/tests/datacap_actor_test.rs index 76e485a5c..01ad0b4f7 100644 --- a/actors/datacap/tests/datacap_actor_test.rs +++ b/actors/datacap/tests/datacap_actor_test.rs @@ -34,7 +34,9 @@ mod mint { use fil_actor_datacap::{Actor, Method, MintParams, INFINITE_ALLOWANCE}; use fil_actors_runtime::cbor::serialize; - use fil_actors_runtime::test_utils::{expect_abort_contains_message, MARKET_ACTOR_CODE_ID}; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, MARKET_ACTOR_CODE_ID, + }; use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use std::ops::Sub; @@ -62,6 +64,21 @@ mod mint { h.check_state(&rt); } + #[test] + fn requires_builtin_caller() { + let (mut rt, h) = make_harness(); + let amt = TokenAmount::from_whole(1); + let params = MintParams { to: *ALICE, amount: amt, operators: vec![] }; + + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), + ); + h.check_state(&rt); + } + #[test] fn requires_verifreg_caller() { let (mut rt, h) = make_harness(); @@ -188,8 +205,11 @@ mod transfer { mod destroy { use crate::{make_harness, ALICE, BOB}; use fil_actor_datacap::DestroyParams; - use fil_actors_runtime::test_utils::{expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID}; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + }; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; + use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; @@ -197,6 +217,21 @@ mod destroy { use fil_actors_runtime::cbor::serialize; use fvm_shared::error::ExitCode; + #[test] + fn requires_builtin_caller() { + let (mut rt, h) = make_harness(); + let amt = TokenAmount::from_whole(1); + let params = DestroyParams { owner: *ALICE, amount: amt }; + + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), + ); + h.check_state(&rt); + } + #[test] fn only_governor_allowed() { let (mut rt, h) = make_harness(); diff --git a/actors/datacap/tests/harness/mod.rs b/actors/datacap/tests/harness/mod.rs index 0ca77a71d..ee3e7e506 100644 --- a/actors/datacap/tests/harness/mod.rs +++ b/actors/datacap/tests/harness/mod.rs @@ -41,6 +41,7 @@ pub struct Harness { impl Harness { pub fn construct_and_verify(&self, rt: &mut MockRuntime, registry: &Address) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index f833a7d0c..0d1ad1fd5 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -16,6 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 4b2a4abce..34dd4b302 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -6,7 +6,8 @@ use std::iter; use cid::Cid; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorError, EAM_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorError, EAM_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -33,6 +34,8 @@ pub enum Method { Exec4 = 3, #[cfg(feature = "m2-native")] InstallCode = 4, + // Method numbers derived from FRC-XXXX standards + ExecExported = frc42_dispatch::method_hash!("Exec"), } /// Init actor @@ -196,12 +199,13 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Exec) => { + Some(Method::Exec) | Some(Method::ExecExported) => { let res = Self::exec(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 03e558091..949d772be 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -7,6 +7,7 @@ use fil_actor_init::{ Actor as InitActor, ConstructorParams, Exec4Params, Exec4Return, ExecParams, ExecReturn, Method, State, }; +use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ADDR, EAM_ACTOR_ID}; use fil_actors_runtime::{ @@ -16,7 +17,7 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; +use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; use num_traits::Zero; use serde::Serialize; @@ -283,6 +284,65 @@ fn sending_constructor_failure() { check_state(&rt); } +#[test] +fn exec_restricted_correctly() { + let mut rt = construct_runtime(); + construct_and_verify(&mut rt); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + + // cannot call the unexported method num + let fake_constructor_params = + RawBytes::serialize(ConstructorParams { network_name: String::from("fake_param") }) + .unwrap(); + let exec_params = ExecParams { + code_cid: *MULTISIG_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(fake_constructor_params.clone()).unwrap(), + }; + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::Exec as MethodNum, + &serialize(&exec_params, "params").unwrap(), + ), + ); + + // can call the exported method num + + // Assign addresses + let unique_address = Address::new_actor(b"multisig"); + rt.new_actor_addr = Some(unique_address); + + // Next id + let expected_id = 100; + let expected_id_addr = Address::new_id(expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, None); + + // Expect a send to the multisig actor constructor + rt.expect_send( + expected_id_addr, + METHOD_CONSTRUCTOR, + RawBytes::serialize(&fake_constructor_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + rt.expect_validate_caller_any(); + + let ret = rt + .call::(Method::ExecExported as u64, &RawBytes::serialize(&exec_params).unwrap()) + .unwrap(); + let exec_ret: ExecReturn = RawBytes::deserialize(&ret).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); + rt.verify(); +} + #[test] fn call_exec4() { let mut rt = construct_runtime(); @@ -330,6 +390,7 @@ fn call_exec4() { } fn construct_and_verify(rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let params = ConstructorParams { network_name: "mock".to_string() }; let ret = diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index c00190079..241af3d03 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -18,6 +18,7 @@ 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 = "1.0.0" frc46_token = "1.1.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 376c231fb..14ed2d975 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -30,9 +30,10 @@ use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorDowncast, ActorError, AsActorError, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, - REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, + AsActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, + DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use crate::ext::verifreg::{AllocationID, AllocationRequest}; @@ -71,6 +72,9 @@ pub enum Method { OnMinerSectorsTerminate = 7, ComputeDataCommitment = 8, CronTick = 9, + // Method numbers derived from FRC-XXXX standards + AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), + WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), } /// Market Actor @@ -1389,16 +1393,17 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; Ok(RawBytes::default()) } - Some(Method::AddBalance) => { + Some(Method::AddBalance) | Some(Method::AddBalanceExported) => { Self::add_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::WithdrawBalance) => { + Some(Method::WithdrawBalance) | Some(Method::WithdrawBalanceExported) => { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 2bf48c600..8ab5de6b4 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -114,6 +114,7 @@ pub fn check_state_with_expected(rt: &MockRuntime, expected_patterns: &[Regex]) } pub fn construct_and_verify(rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( RawBytes::default(), diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 6fcad4c43..3a78313f0 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -28,7 +28,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; use fvm_shared::sector::StoragePower; -use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR, METHOD_SEND}; use regex::Regex; use std::ops::Add; @@ -58,6 +58,7 @@ fn simple_construction() { ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( @@ -808,7 +809,17 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t deal.verified_deal = true; // add funds for client using its BLS address -> will be resolved and persisted - add_participant_funds(&mut rt, client_bls, deal.client_balance_requirement()); + let amount = deal.client_balance_requirement(); + + rt.set_value(amount.clone()); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, client_resolved); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + assert!(rt + .call::(Method::AddBalance as u64, &RawBytes::serialize(client_bls).unwrap()) + .is_ok()); + rt.verify(); + rt.add_balance(amount); + assert_eq!( deal.client_balance_requirement(), get_escrow_balance(&rt, &client_resolved).unwrap() @@ -1953,3 +1964,37 @@ fn insufficient_provider_balance_in_a_batch() { check_state(&rt); } + +#[test] +fn add_balance_restricted_correctly() { + let mut rt = setup(); + let amount = TokenAmount::from_atto(1000); + rt.set_value(amount); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::AddBalance as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ), + ); + + // can call the exported method num + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "forbidden, allowed: [Account, Multisig]", + rt.call::( + Method::AddBalanceExported as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ), + ); + + rt.verify(); +} diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index c6322569b..49144e8bf 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -16,6 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +frc42_dispatch = "1.0.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.1" diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index a5dc0870a..9b88ed582 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -40,9 +40,9 @@ use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, - CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, + BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; pub use monies::*; pub use partition_state::*; @@ -123,6 +123,9 @@ pub enum Method { ChangeBeneficiary = 30, GetBeneficiary = 31, ExtendSectorExpiration2 = 32, + // Method numbers derived from FRC-XXXX standards + ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), + GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -3418,10 +3421,12 @@ pub struct ReplicaUpdateInner { } enum ExtensionKind { - ExtendCommittmentLegacy, // handle only legacy sectors - ExtendCommittment, // handle both Simple QAP and legacy sectors - // TODO: when landing https://github.com/filecoin-project/builtin-actors/pull/518 - // ExtendProofValidity + // handle only legacy sectors + ExtendCommittmentLegacy, + // handle both Simple QAP and legacy sectors + // TODO: when landing https://github.com/filecoin-project/builtin-actors/pull/518 + // ExtendProofValidity + ExtendCommittment, } // ExtendSectorExpiration param @@ -3637,18 +3642,19 @@ fn extend_simple_qap_sector( let old_duration = sector.expiration - sector.activation; let deal_space = §or.deal_weight / old_duration; let old_verified_deal_space = §or.verified_deal_weight / old_duration; - let (expected_verified_deal_space, new_verified_deal_space) = - match claim_space_by_sector.get(§or.sector_number) { - None => { - return Err(actor_error!( + let (expected_verified_deal_space, new_verified_deal_space) = match claim_space_by_sector + .get(§or.sector_number) + { + None => { + return Err(actor_error!( illegal_argument, "claim missing from declaration for sector {} with non-zero verified deal weight {}", sector.sector_number, §or.verified_deal_weight - )) - } - Some(space) => space, - }; + )); + } + Some(space) => space, + }; // claims must be completely accounted for if BigInt::from(*expected_verified_deal_space as i64) != old_verified_deal_space { return Err(actor_error!(illegal_argument, "declared verified deal space in claims ({}) does not match verified deal space ({}) for sector {}", expected_verified_deal_space, old_verified_deal_space, sector.sector_number)); @@ -4864,6 +4870,7 @@ impl ActorCode for Actor { RT: Runtime, RT::Blockstore: Clone, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -4981,11 +4988,11 @@ impl ActorCode for Actor { let res = Self::prove_replica_updates2(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::ChangeBeneficiary) => { + Some(Method::ChangeBeneficiary) | Some(Method::ChangeBenificiaryExported) => { Self::change_beneficiary(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::GetBeneficiary) => { + Some(Method::GetBeneficiary) | Some(Method::GetBeneficiaryExported) => { let res = Self::get_beneficiary(rt)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/miner/tests/change_beneficiary_test.rs b/actors/miner/tests/change_beneficiary_test.rs index db93608da..7d1c9fb3b 100644 --- a/actors/miner/tests/change_beneficiary_test.rs +++ b/actors/miner/tests/change_beneficiary_test.rs @@ -1,7 +1,10 @@ -use fil_actor_miner::BeneficiaryTerm; -use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message, MockRuntime}; +use fil_actor_miner::{Actor, BeneficiaryTerm, GetBeneficiaryReturn, Method}; +use fil_actors_runtime::test_utils::{ + expect_abort, expect_abort_contains_message, make_identity_cid, MockRuntime, +}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; -use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; +use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, MethodNum}; use num_traits::Zero; mod util; @@ -441,3 +444,30 @@ fn successfully_get_beneficiary() { assert_eq!(beneficiary_return.active.beneficiary, info.beneficiary); assert_eq!(beneficiary_return.active.term, info.beneficiary_term); } + +#[test] +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)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::GetBeneficiary as MethodNum, &RawBytes::default()), + ); + + // can call the exported method num + rt.expect_validate_caller_any(); + let beneficiary_return: GetBeneficiaryReturn = rt + .call::(Method::GetBeneficiaryExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + assert_eq!(h.owner, beneficiary_return.active.beneficiary); + assert_eq!(BeneficiaryTerm::default(), beneficiary_return.active.term); + + rt.verify(); +} diff --git a/actors/miner/tests/declare_recoveries.rs b/actors/miner/tests/declare_recoveries.rs index 29ae87f3f..7dc8f6f14 100644 --- a/actors/miner/tests/declare_recoveries.rs +++ b/actors/miner/tests/declare_recoveries.rs @@ -127,7 +127,7 @@ fn recovery_fails_during_active_consensus_fault() { h.commit_and_prove_sectors(&mut rt, 1, DEFAULT_SECTOR_EXPIRATION as u64, vec![], true); // consensus fault - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; h.report_consensus_fault( &mut rt, diff --git a/actors/miner/tests/miner_actor_test_commitment.rs b/actors/miner/tests/miner_actor_test_commitment.rs index ee972ee4c..abf30f636 100644 --- a/actors/miner/tests/miner_actor_test_commitment.rs +++ b/actors/miner/tests/miner_actor_test_commitment.rs @@ -443,7 +443,7 @@ mod miner_actor_test_commitment { epoch: rt.epoch - 1, fault_type: ConsensusFaultType::DoubleForkMining, }; - let test_addr = Address::new_actor(b"satoshi"); + let test_addr = Address::new_id(1234); h.report_consensus_fault(&mut rt, test_addr, Some(fault)).unwrap(); let precommit_params = h.make_pre_commit_params(102, challenge_epoch, expiration, vec![]); diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index e34eb3c74..9c9d44a0a 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -68,6 +68,7 @@ fn simple_construction() { let mut env = prepare_env(); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -141,6 +142,7 @@ fn control_addresses_are_resolved_during_construction() { env.rt.id_addresses.insert(control2, control2id); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -175,6 +177,7 @@ fn fails_if_control_address_is_not_an_account_actor() { env.rt.actor_code_cids.insert(control1, *PAYCH_ACTOR_CODE_ID); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -199,6 +202,7 @@ fn test_construct_with_invalid_peer_id() { env.peer_id = vec![0; env.rt.policy.max_peer_id_length + 1]; let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -218,6 +222,7 @@ fn fails_if_control_addresses_exceeds_maximum_length() { } let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -237,6 +242,7 @@ fn test_construct_with_large_multiaddr() { } let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -255,6 +261,7 @@ fn test_construct_with_empty_multiaddr() { env.multiaddrs.push(BytesDe(vec![1])); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env diff --git a/actors/miner/tests/report_consensus_fault.rs b/actors/miner/tests/report_consensus_fault.rs index 23df39e3f..b8e9de426 100644 --- a/actors/miner/tests/report_consensus_fault.rs +++ b/actors/miner/tests/report_consensus_fault.rs @@ -26,7 +26,7 @@ fn invalid_report_rejected() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, h.report_consensus_fault(&mut rt, test_addr, None), @@ -39,7 +39,7 @@ fn mistargeted_report_rejected() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -61,7 +61,7 @@ fn report_consensus_fault_pays_reward_and_charges_fee() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; let receiver = rt.receiver; h.report_consensus_fault( @@ -82,7 +82,7 @@ fn report_consensus_fault_updates_consensus_fault_reported_field() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let receiver = rt.receiver; let start_info = h.get_info(&rt); @@ -114,7 +114,7 @@ fn double_report_of_consensus_fault_fails() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let receiver = rt.receiver; let start_info = h.get_info(&rt); diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 2397e9851..56c413292 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -15,8 +15,8 @@ use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorContext, - ActorError, AsActorError, Map, INIT_ACTOR_ADDR, + actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, + restrict_internal_api, ActorContext, ActorError, AsActorError, Map, INIT_ACTOR_ADDR, }; pub use self::state::*; @@ -42,6 +42,16 @@ pub enum Method { SwapSigner = 7, ChangeNumApprovalsThreshold = 8, LockBalance = 9, + // Method numbers derived from FRC-XXXX standards + ProposeExported = frc42_dispatch::method_hash!("Propose"), + ApproveExported = frc42_dispatch::method_hash!("Approve"), + CancelExported = frc42_dispatch::method_hash!("Cancel"), + AddSignerExported = frc42_dispatch::method_hash!("AddSigner"), + RemoveSignerExported = frc42_dispatch::method_hash!("RemoveSigner"), + SwapSignerExported = frc42_dispatch::method_hash!("SwapSigner"), + ChangeNumApprovalsThresholdExported = + frc42_dispatch::method_hash!("ChangeNumApprovalsThreshold"), + LockBalanceExported = frc42_dispatch::method_hash!("LockBalance"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -556,40 +566,42 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Propose) => { + Some(Method::Propose) | Some(Method::ProposeExported) => { let res = Self::propose(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::Approve) => { + Some(Method::Approve) | Some(Method::ApproveExported) => { let res = Self::approve(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::Cancel) => { + Some(Method::Cancel) | Some(Method::CancelExported) => { Self::cancel(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::AddSigner) => { + Some(Method::AddSigner) | Some(Method::AddSignerExported) => { Self::add_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::RemoveSigner) => { + Some(Method::RemoveSigner) | Some(Method::RemoveSignerExported) => { Self::remove_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::SwapSigner) => { + Some(Method::SwapSigner) | Some(Method::SwapSignerExported) => { Self::swap_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangeNumApprovalsThreshold) => { + Some(Method::ChangeNumApprovalsThreshold) + | Some(Method::ChangeNumApprovalsThresholdExported) => { Self::change_num_approvals_threshold(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::LockBalance) => { + Some(Method::LockBalance) | Some(Method::LockBalanceExported) => { Self::lock_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index b1ab809b1..91957f71b 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -1,7 +1,7 @@ use fil_actor_multisig::testing::check_state_invariants; use fil_actor_multisig::{ - compute_proposal_hash, Actor as MultisigActor, ConstructorParams, Method, ProposeReturn, State, - Transaction, TxnID, TxnIDParams, SIGNERS_MAX, + compute_proposal_hash, Actor as MultisigActor, ConstructorParams, Method, ProposeParams, + ProposeReturn, State, Transaction, TxnID, TxnIDParams, SIGNERS_MAX, }; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; @@ -11,6 +11,7 @@ use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; +use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -120,7 +121,7 @@ mod constructor_tests { RawBytes::default(), rt.call::( Method::Constructor as u64, - &RawBytes::serialize(¶ms).unwrap() + &RawBytes::serialize(¶ms).unwrap(), ) .unwrap() ); @@ -751,6 +752,58 @@ fn test_fail_propose_from_non_signer() { check_state(&rt); } +#[test] +fn test_propose_restricted_correctly() { + let msig = Address::new_id(1000); + let mut rt = construct_runtime(msig); + let h = util::ActorHarness::new(); + + let anne = Address::new_id(101); + let bob = Address::new_id(102); + // We will treat Chuck as having code CID b"103" + let chuck = Address::new_id(103); + let no_unlock_duration = 0; + let start_epoch = 0; + let signers = vec![anne, bob]; + + let send_value = TokenAmount::from_atto(10u8); + h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"103"), Address::new_id(103)); + let propose_params = serialize( + &ProposeParams { + to: chuck, + value: send_value, + method: METHOD_SEND, + params: RawBytes::default(), + }, + "propose params", + ) + .unwrap(); + + // cannot call the unexported method num + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Propose as u64, &propose_params), + ); + + rt.verify(); + + // can call the exported method num + rt.expect_validate_caller_type([Type::Account, Type::Multisig].to_vec()); + // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "forbidden, allowed: [Account, Multisig]", + rt.call::(Method::ProposeExported as u64, &propose_params), + ); + + rt.verify(); +} + // AddSigner #[test] fn test_add_signer() { @@ -764,7 +817,8 @@ fn test_add_signer() { #[allow(dead_code)] desc: &'a str, - id_addr_mapping: Vec<(Address, Address)>, // non-id to id + id_addr_mapping: Vec<(Address, Address)>, + // non-id to id initial_signers: Vec
, initial_approvals: u64, @@ -777,7 +831,7 @@ fn test_add_signer() { } let test_cases = vec![ - TestCase{ + TestCase { desc: "happy path add signer", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob], @@ -788,7 +842,7 @@ fn test_add_signer() { expect_approvals: 2, code: ExitCode::OK, }, - TestCase{ + TestCase { desc: "add signer and increase threshold", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob], @@ -799,7 +853,7 @@ fn test_add_signer() { expect_approvals: 3, code: ExitCode::OK, }, - TestCase{ + TestCase { desc: "fail to add signer that already exists", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob, chuck], @@ -810,28 +864,28 @@ fn test_add_signer() { expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, }, - TestCase{ + TestCase { desc: "fail to add signer with ID address that already exists even thugh we only have non ID address as approver", id_addr_mapping: vec![(chuck_pubkey, chuck)], initial_signers: vec![anne, bob, chuck_pubkey], initial_approvals: 3, add_signer: chuck, - increase:false, + increase: false, expect_signers: vec![anne, bob, chuck], expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, }, - TestCase{ + TestCase { desc: "fail to add signer with ID address that already exists even thugh we only have non ID address as approver", id_addr_mapping: vec![(chuck_pubkey, chuck)], initial_signers: vec![anne, bob, chuck], initial_approvals: 3, add_signer: chuck_pubkey, - increase:false, + increase: false, expect_signers: vec![anne, bob, chuck], expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, - } + }, ]; for tc in test_cases { @@ -1089,7 +1143,7 @@ fn test_signer_swap() { swap_from: anne, expect_signers: vec![], code: ExitCode::USR_ILLEGAL_ARGUMENT, - } + }, ]; for tc in test_cases { @@ -1448,6 +1502,7 @@ mod approval_tests { ); check_state(&rt); } + #[test] fn fail_approval_if_not_enough_unlocked_balance_available() { let msig = Address::new_id(100); diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index 4322e8078..eb6716cc2 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -22,9 +22,9 @@ use fil_actors_runtime::cbor::{deserialize, serialize}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_actor_id, ActorDowncast, - ActorError, BatchReturn, Map, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_actor_id, restrict_internal_api, + ActorDowncast, ActorError, BatchReturn, Map, DATACAP_TOKEN_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fil_actors_runtime::{ActorContext, AsActorError, BatchReturnGen}; @@ -60,6 +60,10 @@ pub enum Method { GetClaims = 10, ExtendClaimTerms = 11, RemoveExpiredClaims = 12, + // Method numbers derived from FRC-XXXX standards + RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), + ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), + RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -1067,6 +1071,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -1089,7 +1094,8 @@ impl ActorCode for Actor { Self::remove_verified_client_data_cap(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::RemoveExpiredAllocations) => { + Some(Method::RemoveExpiredAllocations) + | Some(Method::RemoveExpiredAllocationsExported) => { let res = Self::remove_expired_allocations(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -1097,7 +1103,7 @@ impl ActorCode for Actor { let res = Self::claim_allocations(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::ExtendClaimTerms) => { + Some(Method::ExtendClaimTerms) | Some(Method::ExtendClaimTermsExported) => { let res = Self::extend_claim_terms(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -1105,7 +1111,7 @@ impl ActorCode for Actor { let res = Self::get_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::RemoveExpiredClaims) => { + Some(Method::RemoveExpiredClaims) | Some(Method::RemoveExpiredClaimsExported) => { let res = Self::remove_expired_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 0c98971d2..d608197af 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -62,6 +62,7 @@ pub struct Harness { impl Harness { pub fn construct_and_verify(&self, rt: &mut MockRuntime, root_param: &Address) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index a7aa34cee..fbc3649e3 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -64,6 +64,7 @@ mod construction { let mut rt = new_runtime(); let root_pubkey = Address::new_bls(&[7u8; BLS_PUB_LEN]).unwrap(); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -426,17 +427,22 @@ mod allocs_claims { use fvm_shared::bigint::BigInt; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; - use fvm_shared::ActorID; + use fvm_shared::{ActorID, MethodNum}; use num_traits::Zero; use std::str::FromStr; - use fil_actor_verifreg::Claim; - use fil_actor_verifreg::{AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, State}; + use fil_actor_verifreg::{ + Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, Method, State, + }; + use fil_actor_verifreg::{Claim, ExtendClaimTermsReturn}; + use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::policy_constants::{ MAXIMUM_VERIFIED_ALLOCATION_TERM, MINIMUM_VERIFIED_ALLOCATION_SIZE, MINIMUM_VERIFIED_ALLOCATION_TERM, }; - use fil_actors_runtime::test_utils::ACCOUNT_ACTOR_CODE_ID; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + }; use fil_actors_runtime::FailCode; use harness::*; @@ -888,6 +894,55 @@ mod allocs_claims { assert!(h.load_claim(&mut rt, PROVIDER1, id2).is_none()); // removed h.check_state(&rt); } + + #[test] + fn extend_claims_restricted_correctly() { + let (h, mut rt) = new_harness(); + let size = MINIMUM_VERIFIED_ALLOCATION_SIZE as u64; + let sector = 0; + let start = 0; + let min_term = MINIMUM_VERIFIED_ALLOCATION_TERM; + let max_term = min_term + 1000; + + let claim1 = make_claim("1", CLIENT1, PROVIDER1, size, min_term, max_term, start, sector); + + let id1 = h.create_claim(&mut rt, &claim1).unwrap(); + + // Extend claim terms and verify return value. + let params = ExtendClaimTermsParams { + terms: vec![ClaimTerm { provider: PROVIDER1, claim_id: id1, term_max: max_term + 1 }], + }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(CLIENT1)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + h.extend_claim_terms(&mut rt, ¶ms), + ); + + // can call the exported method num + + rt.expect_validate_caller_any(); + let ret: ExtendClaimTermsReturn = rt + .call::( + Method::ExtendClaimTermsExported as MethodNum, + &serialize(¶ms, "extend claim terms params").unwrap(), + ) + .unwrap() + .deserialize() + .expect("failed to deserialize extend claim terms return"); + + rt.verify(); + + assert_eq!(ret.codes(), vec![ExitCode::OK]); + + // Verify state directly. + assert_claim(&rt, PROVIDER1, id1, &Claim { term_max: max_term + 1, ..claim1 }); + h.check_state(&rt); + } } mod datacap { diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index aea1180c0..2208a45bb 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -11,7 +11,7 @@ use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// Types of built-in actors that can be treated as principles. +/// Types of built-in actors that can be treated as principals. /// This distinction is legacy and should be removed prior to FVM support for /// user-programmable actors. pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index b86178719..087a57454 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -495,6 +495,8 @@ impl MockRuntime { } pub fn set_caller(&mut self, code_id: Cid, address: Address) { + // fail if called with a non-ID address, since the caller() method must always return an ID + address.id().unwrap(); self.caller = address; self.caller_type = code_id; self.actor_code_cids.insert(address, code_id); @@ -951,11 +953,12 @@ impl Runtime for MockRuntime { types, expected_caller_type, ); - let call_type = self.resolve_builtin_actor_type(&self.caller_type).unwrap(); - for expected in &types { - if &call_type == expected { - self.expectations.borrow_mut().expect_validate_caller_type = None; - return Ok(()); + if let Some(call_type) = self.resolve_builtin_actor_type(&self.caller_type) { + for expected in &types { + if &call_type == expected { + self.expectations.borrow_mut().expect_validate_caller_type = None; + return Ok(()); + } } } From 94c17f2d32ccda7e7b0e039a733171552656e39a Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 7 Nov 2022 20:39:31 +0000 Subject: [PATCH 129/339] Restrict internal APIs of all actors (#809) --- actors/cron/src/lib.rs | 3 ++- actors/cron/tests/cron_actor_test.rs | 1 + actors/paych/src/lib.rs | 3 ++- actors/paych/tests/paych_actor_test.rs | 4 ++++ actors/power/src/lib.rs | 5 +++-- actors/power/tests/harness/mod.rs | 1 + actors/reward/src/lib.rs | 5 +++-- actors/reward/tests/reward_actor_test.rs | 1 + actors/system/src/lib.rs | 5 ++++- 9 files changed, 21 insertions(+), 7 deletions(-) diff --git a/actors/cron/src/lib.rs b/actors/cron/src/lib.rs index 173aca0f5..c6d6661f1 100644 --- a/actors/cron/src/lib.rs +++ b/actors/cron/src/lib.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, cbor, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, cbor, restrict_internal_api, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; @@ -83,6 +83,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; diff --git a/actors/cron/tests/cron_actor_test.rs b/actors/cron/tests/cron_actor_test.rs index bc92de96f..18609b0d6 100644 --- a/actors/cron/tests/cron_actor_test.rs +++ b/actors/cron/tests/cron_actor_test.rs @@ -114,6 +114,7 @@ fn epoch_tick_with_entries() { } fn construct_and_verify(rt: &mut MockRuntime, params: &ConstructorParams) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt.call::(1, &RawBytes::serialize(¶ms).unwrap()).unwrap(); assert_eq!(RawBytes::default(), ret); diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 8299b6ea2..75d9a5091 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -4,7 +4,7 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, resolve_to_actor_id, ActorDowncast, ActorError, Array, + actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, Array, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -324,6 +324,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index f250fe277..2250b7114 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -108,6 +108,7 @@ mod paych_constructor { #[test] fn actor_doesnt_exist_test() { let mut rt = construct_runtime(); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), @@ -226,6 +227,7 @@ mod paych_constructor { ExitCode::OK, ); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: non_id_addr, to: to_addr }; expect_abort( @@ -263,6 +265,7 @@ mod paych_constructor { ExitCode::OK, ); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: from_addr, to: non_id_addr }; expect_abort( @@ -1201,6 +1204,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche fn construct_and_verify(rt: &mut MockRuntime, sender: Address, receiver: Address) { let params = ConstructorParams { from: sender, to: receiver }; + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); call(rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap()); rt.verify(); diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 6229f61f0..21dc612b6 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -9,8 +9,8 @@ use ext::init; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_map_with_root_and_bitwidth, ActorDowncast, ActorError, Multimap, - CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + actor_error, cbor, make_map_with_root_and_bitwidth, restrict_internal_api, ActorDowncast, + ActorError, Multimap, CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -625,6 +625,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index c168403bc..37ffb8410 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -101,6 +101,7 @@ pub struct Harness { impl Harness { pub fn construct(&self, rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); rt.call::(Method::Constructor as MethodNum, &RawBytes::default()).unwrap(); rt.verify() diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index e01a414b5..54c4f41d4 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -3,8 +3,8 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorError, BURNT_FUNDS_ACTOR_ADDR, EXPECTED_LEADERS_PER_EPOCH, - STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorError, BURNT_FUNDS_ACTOR_ADDR, + EXPECTED_LEADERS_PER_EPOCH, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; @@ -223,6 +223,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { let param: Option = cbor::deserialize_params(params)?; diff --git a/actors/reward/tests/reward_actor_test.rs b/actors/reward/tests/reward_actor_test.rs index 33dfe720b..98206e69c 100644 --- a/actors/reward/tests/reward_actor_test.rs +++ b/actors/reward/tests/reward_actor_test.rs @@ -340,6 +340,7 @@ fn construct_and_verify(curr_power: &StoragePower) -> MockRuntime { caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/system/src/lib.rs b/actors/system/src/lib.rs index f68d8e2f6..c7abe1af7 100644 --- a/actors/system/src/lib.rs +++ b/actors/system/src/lib.rs @@ -11,7 +11,9 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{ + actor_error, restrict_internal_api, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR, +}; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); @@ -73,6 +75,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; From 305cfa63a1382950436519256758291fe36fdfe7 Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Wed, 9 Nov 2022 08:59:56 +1100 Subject: [PATCH 130/339] Exported API method for market actor escrow/locked balance (#812) --- actors/market/src/lib.rs | 35 ++++++- actors/market/src/types.rs | 55 +++++------ actors/market/tests/cron_tick_deal_expiry.rs | 16 ++-- .../market/tests/cron_tick_timedout_deals.rs | 7 +- actors/market/tests/harness.rs | 72 +++++++-------- actors/market/tests/market_actor_test.rs | 91 ++++++++++--------- 6 files changed, 151 insertions(+), 125 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 14ed2d975..69104c244 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -26,6 +26,7 @@ use log::info; use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; +use crate::balance_table::BalanceTable; use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; @@ -72,9 +73,10 @@ pub enum Method { OnMinerSectorsTerminate = 7, ComputeDataCommitment = 8, CronTick = 9, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), } /// Market Actor @@ -185,6 +187,33 @@ impl Actor { Ok(WithdrawBalanceReturn { amount_withdrawn: amount_extracted }) } + /// Returns the escrow balance and locked amount for an address. + fn get_balance( + rt: &mut impl Runtime, + account: Address, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let nominal = rt.resolve_address(&account).ok_or_else(|| { + actor_error!(illegal_argument, "failed to resolve address {}", account) + })?; + let account = Address::new_id(nominal); + + let store = rt.store(); + let st: State = rt.state()?; + let balances = BalanceTable::from_root(store, &st.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + let locks = BalanceTable::from_root(store, &st.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; + let balance = balances + .get(&account) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance")?; + let locked = locks + .get(&account) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance")?; + + Ok(GetBalanceReturn { balance, locked }) + } + /// Publish a new set of storage deals (not yet included in a sector). fn publish_storage_deals( rt: &mut impl Runtime, @@ -1431,6 +1460,10 @@ impl ActorCode for Actor { Self::cron_tick(rt)?; Ok(RawBytes::default()) } + Some(Method::GetBalanceExported) => { + let res = Self::get_balance(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index 39ee64e13..bac78d0df 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -22,7 +22,7 @@ use super::deal::{ClientDealProposal, DealProposal, DealState}; pub const PROPOSALS_AMT_BITWIDTH: u32 = 5; pub const STATES_AMT_BITWIDTH: u32 = 6; -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct WithdrawBalanceParams { pub provider_or_client: Address, pub amount: TokenAmount, @@ -30,33 +30,32 @@ pub struct WithdrawBalanceParams { impl Cbor for WithdrawBalanceParams {} -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] #[serde(transparent)] pub struct WithdrawBalanceReturn { pub amount_withdrawn: TokenAmount, } -#[derive(Serialize_tuple, Deserialize_tuple)] -pub struct OnMinerSectorsTerminateParams { - pub epoch: ChainEpoch, - pub deal_ids: Vec, +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetBalanceReturn { + pub balance: TokenAmount, + pub locked: TokenAmount, } -#[derive(Serialize_tuple)] - -pub struct OnMinerSectorsTerminateParamsRef<'a> { +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct OnMinerSectorsTerminateParams { pub epoch: ChainEpoch, - pub deal_ids: &'a [DealID], + pub deal_ids: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct PublishStorageDealsParams { pub deals: Vec, } impl Cbor for PublishStorageDealsParams {} -#[derive(Serialize_tuple, Deserialize_tuple, Debug)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] // Add Eq when BitField does pub struct PublishStorageDealsReturn { pub ids: Vec, pub valid_deals: BitField, @@ -65,41 +64,36 @@ pub struct PublishStorageDealsReturn { // Changed since V2: // - Array of Sectors rather than just one // - Removed SectorStart -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifyDealsForActivationParams { pub sectors: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct SectorDeals { pub sector_type: RegisteredSealProof, pub sector_expiry: ChainEpoch, pub deal_ids: Vec, } -#[derive(Serialize_tuple)] -pub struct VerifyDealsForActivationParamsRef<'a> { - pub sectors: &'a [SectorDeals], -} - -#[derive(Serialize_tuple, Deserialize_tuple, Default)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifyDealsForActivationReturn { pub sectors: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple, Default, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq, Default)] pub struct SectorDealData { /// Option::None signifies commitment to empty sector, meaning no deals. pub commd: Option, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ActivateDealsParams { pub deal_ids: Vec, pub sector_expiry: ChainEpoch, } -#[derive(Serialize_tuple, Deserialize_tuple, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifiedDealInfo { pub client: ActorID, pub allocation_id: AllocationID, @@ -107,13 +101,13 @@ pub struct VerifiedDealInfo { pub size: PaddedPieceSize, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ActivateDealsResult { #[serde(with = "bigint_ser")] pub nonverified_deal_space: BigInt, pub verified_infos: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Default)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct DealSpaces { #[serde(with = "bigint_ser")] pub deal_space: BigInt, @@ -121,17 +115,12 @@ pub struct DealSpaces { pub verified_deal_space: BigInt, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ComputeDataCommitmentParams { pub inputs: Vec, } -#[derive(Serialize_tuple)] -pub struct ComputeDataCommitmentParamsRef<'a> { - pub inputs: &'a [SectorDataSpec], -} - -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ComputeDataCommitmentReturn { pub commds: Vec, } @@ -142,7 +131,7 @@ pub type DealArray<'bs, BS> = Array<'bs, DealProposal, BS>; /// A specialization of a array to deals. pub type DealMetaArray<'bs, BS> = Array<'bs, DealState, BS>; -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct SectorDataSpec { pub deal_ids: Vec, pub sector_type: RegisteredSealProof, diff --git a/actors/market/tests/cron_tick_deal_expiry.rs b/actors/market/tests/cron_tick_deal_expiry.rs index 38b7bce7c..6118cd3fe 100644 --- a/actors/market/tests/cron_tick_deal_expiry.rs +++ b/actors/market/tests/cron_tick_deal_expiry.rs @@ -163,8 +163,8 @@ fn expired_deal_should_unlock_the_remaining_client_and_provider_locked_balance_a ); let deal_proposal = get_deal_proposal(&mut rt, deal_id); - let c_escrow = get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(); - let p_escrow = get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(); + let c_escrow = get_balance(&mut rt, &CLIENT_ADDR).balance; + let p_escrow = get_balance(&mut rt, &PROVIDER_ADDR).balance; // move the current epoch so that deal is expired rt.set_epoch(END_EPOCH + 1000); @@ -173,11 +173,13 @@ fn expired_deal_should_unlock_the_remaining_client_and_provider_locked_balance_a // assert balances let payment = deal_proposal.total_storage_fee(); - assert_eq!(c_escrow - &payment, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, CLIENT_ADDR).is_zero()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(c_escrow - &payment, client_acct.balance); + assert!(client_acct.locked.is_zero()); - assert_eq!(p_escrow + &payment, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, PROVIDER_ADDR).is_zero()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(p_escrow + &payment, provider_acct.balance); + assert!(provider_acct.locked.is_zero()); // deal should be deleted assert_deal_deleted(&mut rt, deal_id, deal_proposal); @@ -201,7 +203,7 @@ fn all_payments_are_made_for_a_deal_client_withdraws_collateral_and_client_accou // move the current epoch so that deal is expired rt.set_epoch(END_EPOCH + 100); cron_tick(&mut rt); - assert_eq!(deal_proposal.client_collateral, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(deal_proposal.client_collateral, get_balance(&mut rt, &CLIENT_ADDR).balance); // client withdraws collateral -> account should be removed as it now has zero balance withdraw_client_balance( diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index 55714aab5..dfdff2e45 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -36,7 +36,7 @@ fn timed_out_deal_is_slashed_and_deleted() { ); let deal_proposal = get_deal_proposal(&mut rt, deal_id); - let c_escrow = get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(); + let c_escrow = get_balance(&mut rt, &CLIENT_ADDR).balance; // do a cron tick for it -> should time out and get slashed rt.set_epoch(process_epoch(START_EPOCH, deal_id)); @@ -50,8 +50,9 @@ fn timed_out_deal_is_slashed_and_deleted() { ); cron_tick(&mut rt); - assert_eq!(c_escrow, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, CLIENT_ADDR).is_zero()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(c_escrow, client_acct.balance); + assert!(client_acct.locked.is_zero()); assert_account_zero(&mut rt, PROVIDER_ADDR); assert_deal_deleted(&mut rt, deal_id, deal_proposal); check_state(&rt); diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 8ab5de6b4..7bccd1506 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -10,10 +10,10 @@ use std::{cell::RefCell, collections::HashMap}; use fil_actor_market::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD}; use fil_actor_market::ext::verifreg::{AllocationID, AllocationRequest, AllocationsResponse}; use fil_actor_market::{ - balance_table::BalanceTable, deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, - gen_rand_next_epoch, testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, + deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, gen_rand_next_epoch, + testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, Actor as MarketActor, ClientDealProposal, DealArray, DealMetaArray, DealProposal, DealState, - Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, + GetBalanceReturn, Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, PublishStorageDealsReturn, SectorDeals, State, VerifyDealsForActivationParams, VerifyDealsForActivationReturn, WithdrawBalanceParams, WithdrawBalanceReturn, NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, @@ -123,13 +123,16 @@ pub fn construct_and_verify(rt: &mut MockRuntime) { rt.verify(); } -pub fn get_escrow_balance(rt: &MockRuntime, addr: &Address) -> Result { - let st: State = rt.get_state(); - - let et = BalanceTable::from_root(rt.store(), &st.escrow_table) - .expect("failed to construct balance table from blockstore"); - - Ok(et.get(addr).expect("address does not exist in escrow balance table")) +pub fn get_balance(rt: &mut MockRuntime, addr: &Address) -> GetBalanceReturn { + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.expect_validate_caller_any(); + let ret: GetBalanceReturn = rt + .call::(Method::GetBalanceExported as u64, &RawBytes::serialize(addr).unwrap()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + ret } pub fn expect_get_control_addresses( @@ -316,12 +319,6 @@ pub fn get_pending_deal_allocation(rt: &mut MockRuntime, deal_id: DealID) -> All *pending_allocations.get(&deal_id_key(deal_id)).unwrap().unwrap_or(&NO_ALLOCATION_ID) } -pub fn get_locked_balance(rt: &mut MockRuntime, addr: Address) -> TokenAmount { - let st: State = rt.get_state(); - let lt = BalanceTable::from_root(&rt.store, &st.locked_table).unwrap(); - lt.get(&addr).unwrap() -} - pub fn get_deal_state(rt: &mut MockRuntime, deal_id: DealID) -> DealState { let st: State = rt.get_state(); let states = DealMetaArray::load(&st.states, &rt.store).unwrap(); @@ -359,10 +356,8 @@ pub fn cron_tick_and_assert_balances( deal_id: DealID, ) -> (TokenAmount, TokenAmount) { // fetch current client and provider escrow balances - let c_locked = get_locked_balance(rt, client_addr); - let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); - let p_locked = get_locked_balance(rt, provider_addr); - let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); + let c_acct = get_balance(rt, &client_addr); + let p_acct = get_balance(rt, &provider_addr); let mut amount_slashed = TokenAmount::zero(); let s = get_deal_state(rt, deal_id); @@ -399,10 +394,10 @@ pub fn cron_tick_and_assert_balances( let payment = duration * d.storage_price_per_epoch; // expected updated amounts - let updated_client_escrow = c_escrow - &payment; - let updated_provider_escrow = (p_escrow + &payment) - &amount_slashed; - let mut updated_client_locked = c_locked - &payment; - let mut updated_provider_locked = p_locked; + let updated_client_escrow = c_acct.balance - &payment; + let updated_provider_escrow = (p_acct.balance + &payment) - &amount_slashed; + let mut updated_client_locked = c_acct.locked - &payment; + let mut updated_provider_locked = p_acct.locked; // if the deal has expired or been slashed, locked amount will be zero for provider and client. let is_deal_expired = payment_end == d.end_epoch; if is_deal_expired || s.slash_epoch != EPOCH_UNDEFINED { @@ -412,10 +407,12 @@ pub fn cron_tick_and_assert_balances( cron_tick(rt); - assert_eq!(updated_client_escrow, get_escrow_balance(rt, &client_addr).unwrap()); - assert_eq!(updated_client_locked, get_locked_balance(rt, client_addr)); - assert_eq!(updated_provider_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); - assert_eq!(updated_provider_locked, get_locked_balance(rt, provider_addr)); + let client_acct = get_balance(rt, &client_addr); + let provider_acct = get_balance(rt, &provider_addr); + assert_eq!(updated_client_escrow, client_acct.balance); + assert_eq!(updated_client_locked, client_acct.locked); + assert_eq!(updated_provider_escrow, provider_acct.balance); + assert_eq!(updated_provider_locked, provider_acct.locked); (payment, amount_slashed) } @@ -424,19 +421,17 @@ pub fn cron_tick_no_change(rt: &mut MockRuntime, client_addr: Address, provider_ let epoch_cid = st.deal_ops_by_epoch; // fetch current client and provider escrow balances - let c_locked = get_locked_balance(rt, client_addr); - let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); - let p_locked = get_locked_balance(rt, provider_addr); - let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); + let client_acct = get_balance(rt, &client_addr); + let provider_acct = get_balance(rt, &provider_addr); cron_tick(rt); let st: State = rt.get_state(); + let new_client_acct = get_balance(rt, &client_addr); + let new_provider_acct = get_balance(rt, &provider_addr); assert_eq!(epoch_cid, st.deal_ops_by_epoch); - assert_eq!(c_locked, get_locked_balance(rt, client_addr)); - assert_eq!(c_escrow, get_escrow_balance(rt, &client_addr).unwrap()); - assert_eq!(p_locked, get_locked_balance(rt, provider_addr)); - assert_eq!(p_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); + assert_eq!(client_acct, new_client_acct); + assert_eq!(provider_acct, new_provider_acct); } pub fn publish_deals( @@ -983,8 +978,9 @@ pub fn terminate_deals_raw( } pub fn assert_account_zero(rt: &mut MockRuntime, addr: Address) { - assert!(get_escrow_balance(rt, &addr).unwrap().is_zero()); - assert!(get_locked_balance(rt, addr).is_zero()); + let account = get_balance(rt, &addr); + assert!(account.balance.is_zero()); + assert!(account.locked.is_zero()); } pub fn verify_deals_for_activation( diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 3a78313f0..b4e0c63e0 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -209,10 +209,9 @@ fn adds_to_provider_escrow_funds() { rt.verify(); - assert_eq!( - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), - TokenAmount::from_atto(tc.total) - ); + let acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(acct.balance, TokenAmount::from_atto(tc.total)); + assert_eq!(acct.locked, TokenAmount::zero()); check_state(&rt); } } @@ -224,7 +223,7 @@ fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() add_participant_funds(&mut rt, CLIENT_ADDR, TokenAmount::from_atto(20u8)); - assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_balance(&mut rt, &CLIENT_ADDR).balance); rt.expect_validate_caller_addr(vec![CLIENT_ADDR]); @@ -245,7 +244,7 @@ fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() rt.verify(); // verify there was no withdrawal - assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_balance(&mut rt, &CLIENT_ADDR).balance); check_state(&rt); } @@ -268,8 +267,12 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun end_epoch, ); let deal = get_deal_proposal(&mut rt, deal_id); - assert_eq!(deal.provider_collateral, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); - assert_eq!(deal.client_balance_requirement(), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(deal.provider_collateral, provider_acct.balance); + assert_eq!(deal.provider_collateral, provider_acct.locked); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(deal.client_balance_requirement(), client_acct.balance); + assert_eq!(deal.client_balance_requirement(), client_acct.locked); let withdraw_amount = TokenAmount::from_atto(1u8); let withdrawable_amount = TokenAmount::zero(); @@ -295,6 +298,10 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun let withdrawable_amount = TokenAmount::from_atto(25u8); add_provider_funds(&mut rt, withdrawable_amount.clone(), &MinerAddresses::default()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(&deal.provider_collateral + &withdrawable_amount, provider_acct.balance); + assert_eq!(deal.provider_collateral, provider_acct.locked); + withdraw_provider_balance( &mut rt, withdraw_amount.clone(), @@ -306,6 +313,10 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun // add some more funds to the client & ensure withdrawal is limited by the locked funds add_participant_funds(&mut rt, CLIENT_ADDR, withdrawable_amount.clone()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(deal.client_balance_requirement() + &withdrawable_amount, client_acct.balance); + assert_eq!(deal.client_balance_requirement(), client_acct.locked); + withdraw_client_balance(&mut rt, withdraw_amount, withdrawable_amount, CLIENT_ADDR); check_state(&rt); } @@ -420,10 +431,7 @@ fn adds_to_non_provider_funds() { rt.verify(); - assert_eq!( - get_escrow_balance(&rt, caller_addr).unwrap(), - TokenAmount::from_atto(tc.total) - ); + assert_eq!(get_balance(&mut rt, caller_addr).balance, TokenAmount::from_atto(tc.total)); check_state(&rt); } } @@ -436,7 +444,7 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(amount, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(amount, get_balance(&mut rt, &PROVIDER_ADDR).balance); // worker calls WithdrawBalance, balance is transferred to owner let withdraw_amount = TokenAmount::from_atto(1); @@ -449,7 +457,7 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { WORKER_ADDR, ); - assert_eq!(TokenAmount::from_atto(19), get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(19), get_balance(&mut rt, &PROVIDER_ADDR).balance); check_state(&rt); } @@ -460,12 +468,12 @@ fn withdraws_from_non_provider_escrow_funds() { let amount = TokenAmount::from_atto(20); add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, amount); let withdraw_amount = TokenAmount::from_atto(1); withdraw_client_balance(&mut rt, withdraw_amount.clone(), withdraw_amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::from_atto(19)); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, TokenAmount::from_atto(19)); check_state(&rt); } @@ -480,7 +488,7 @@ fn client_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let withdraw_amount = TokenAmount::from_atto(25); withdraw_client_balance(&mut rt, withdraw_amount, amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::zero()); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, TokenAmount::zero()); check_state(&rt); } @@ -491,7 +499,7 @@ fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); let withdraw_amount = TokenAmount::from_atto(25); withdraw_provider_balance( @@ -503,7 +511,7 @@ fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { WORKER_ADDR, ); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), TokenAmount::zero()); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, TokenAmount::zero()); check_state(&rt); } @@ -554,7 +562,7 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker let amount = TokenAmount::from_atto(20u8); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); // only signing parties can add balance for client AND provider. rt.expect_validate_caller_addr(vec![OWNER_ADDR, WORKER_ADDR]); @@ -577,7 +585,7 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker rt.verify(); // verify there was no withdrawal - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); check_state(&rt); } @@ -820,10 +828,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t rt.verify(); rt.add_balance(amount); - assert_eq!( - deal.client_balance_requirement(), - get_escrow_balance(&rt, &client_resolved).unwrap() - ); + assert_eq!(deal.client_balance_requirement(), get_balance(&mut rt, &client_resolved).balance); // add funds for provider using it's BLS address -> will be resolved and persisted rt.value_received = deal.provider_collateral.clone(); @@ -841,7 +846,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t ); rt.verify(); rt.add_balance(deal.provider_collateral.clone()); - assert_eq!(deal.provider_collateral, get_escrow_balance(&rt, &provider_resolved).unwrap()); + assert_eq!(deal.provider_collateral, get_balance(&mut rt, &provider_resolved).balance); // publish deal using the BLS addresses rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -1056,13 +1061,13 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assert locked balance for all clients and provider let provider_locked_expected = &deal1.provider_collateral + &deal2.provider_collateral + &deal3.provider_collateral; - let client1_locked = get_locked_balance(&mut rt, client1_addr); - let client2_locked = get_locked_balance(&mut rt, client2_addr); - let client3_locked = get_locked_balance(&mut rt, client3_addr); + let client1_locked = get_balance(&mut rt, &client1_addr).locked; + let client2_locked = get_balance(&mut rt, &client2_addr).locked; + let client3_locked = get_balance(&mut rt, &client3_addr).locked; assert_eq!(deal1.client_balance_requirement(), client1_locked); assert_eq!(deal2.client_balance_requirement(), client2_locked); assert_eq!(deal3.client_balance_requirement(), client3_locked); - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); // assert locked funds dealStates let st: State = rt.get_state(); @@ -1095,16 +1100,16 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assert locked balances for clients and provider let provider_locked_expected = &provider_locked_expected + &deal4.provider_collateral + &deal5.provider_collateral; - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); - let client3_locked_updated = get_locked_balance(&mut rt, client3_addr); + let client3_locked_updated = get_balance(&mut rt, &client3_addr).locked; assert_eq!( &client3_locked + &deal4.client_balance_requirement() + &deal5.client_balance_requirement(), client3_locked_updated ); - let client1_locked = get_locked_balance(&mut rt, client1_addr); - let client2_locked = get_locked_balance(&mut rt, client2_addr); + let client1_locked = get_balance(&mut rt, &client1_addr).locked; + let client2_locked = get_balance(&mut rt, &client2_addr).locked; assert_eq!(deal1.client_balance_requirement(), client1_locked); assert_eq!(deal2.client_balance_requirement(), client2_locked); @@ -1138,15 +1143,15 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assertions let st: State = rt.get_state(); let provider2_locked = &deal6.provider_collateral + &deal7.provider_collateral; - assert_eq!(provider2_locked, get_locked_balance(&mut rt, provider2_addr)); - let client1_locked_updated = get_locked_balance(&mut rt, client1_addr); + assert_eq!(provider2_locked, get_balance(&mut rt, &provider2_addr).locked); + let client1_locked_updated = get_balance(&mut rt, &client1_addr).locked; assert_eq!( &deal7.client_balance_requirement() + &client1_locked + &deal6.client_balance_requirement(), client1_locked_updated ); // assert first provider's balance as well - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); let total_client_collateral_locked = &total_client_collateral_locked + &deal6.client_collateral + &deal7.client_collateral; @@ -1676,7 +1681,7 @@ fn market_actor_deals() { // test adding provider funds let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); - assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(funds, get_balance(&mut rt, &PROVIDER_ADDR).balance); add_participant_funds(&mut rt, CLIENT_ADDR, funds); let mut deal_proposal = @@ -1714,7 +1719,7 @@ fn max_deal_label_size() { // Test adding provider funds from both worker and owner address let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); - assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(funds, get_balance(&mut rt, &PROVIDER_ADDR).balance); add_participant_funds(&mut rt, CLIENT_ADDR, funds); let mut deal_proposal = @@ -1779,10 +1784,10 @@ fn insufficient_client_balance_in_a_batch() { rt.verify(); - assert_eq!(deal2.client_balance_requirement(), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(deal2.client_balance_requirement(), get_balance(&mut rt, &CLIENT_ADDR).balance); assert_eq!( deal1.provider_balance_requirement().add(deal2.provider_balance_requirement()), - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap() + get_balance(&mut rt, &PROVIDER_ADDR).balance ); let buf1 = RawBytes::serialize(&deal1).expect("failed to marshal deal proposal"); @@ -1894,11 +1899,11 @@ fn insufficient_provider_balance_in_a_batch() { assert_eq!( deal1.client_balance_requirement().add(deal2.client_balance_requirement()), - get_escrow_balance(&rt, &CLIENT_ADDR).unwrap() + get_balance(&mut rt, &CLIENT_ADDR).balance ); assert_eq!( deal2.provider_balance_requirement().clone(), - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap() + get_balance(&mut rt, &PROVIDER_ADDR).balance ); let buf1 = RawBytes::serialize(&deal1).expect("failed to marshal deal proposal"); From 8c836b3c927ed8a94eeec37715bdf67ca5876ba4 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Nov 2022 09:23:27 -0500 Subject: [PATCH 131/339] Power actor: Add exported getters for raw power (#810) * Power actor: Add exported getters for raw power * FRC-XXXX is FRC-0042 * Power actor: network_raw_power: Return this_epoch_raw_byte_power * Power actor: miner_raw_power: Return whether above consensus min power * Power actor: types: serialize one-element structs transparently * Address review * Miner actor: Add exported getters for info and monies (#811) * Miner actor: Add exported getters for info and monies * Tweak comment * Miner actor: Replace GetWorker and GetControls with IsControllingAddress * Miner actor: Add exported GetAvailableBalance * Miner actor: Add exported GetVestingFunds * Miner actor: Remove exported monies getters * Miner actor: types: serialize one-element structs transparently * Address review * Address review --- Cargo.lock | 1 + actors/datacap/src/lib.rs | 4 +- actors/init/src/lib.rs | 2 +- actors/miner/src/lib.rs | 94 ++++++++++++++- actors/miner/src/state.rs | 2 +- actors/miner/src/types.rs | 49 +++++++- actors/miner/tests/apply_rewards.rs | 15 ++- actors/miner/tests/exported_getters.rs | 150 ++++++++++++++++++++++++ actors/miner/tests/util.rs | 28 +++-- actors/multisig/src/lib.rs | 2 +- actors/power/Cargo.toml | 1 + actors/power/src/lib.rs | 40 +++++++ actors/power/src/state.rs | 39 +++--- actors/power/src/types.rs | 38 +++++- actors/power/tests/harness/mod.rs | 4 +- actors/power/tests/power_actor_tests.rs | 59 +++++++++- actors/verifreg/src/lib.rs | 2 +- 17 files changed, 480 insertions(+), 50 deletions(-) create mode 100644 actors/miner/tests/exported_getters.rs diff --git a/Cargo.lock b/Cargo.lock index 8af737553..bfe392041 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,6 +1651,7 @@ dependencies = [ "cid", "fil_actor_reward", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index fd9b2df28..129934508 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -46,7 +46,7 @@ lazy_static! { /// Datacap actor methods available /// Some methods are available under 2 method nums -- a static number for "private" builtin actor usage, -/// and via FRC-XXXX calling convention, with number determined by method name. +/// and via FRC-0042 calling convention, with number determined by method name. #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -67,7 +67,7 @@ pub enum Method { Burn = 19, BurnFrom = 20, Allowance = 21, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards NameExported = frc42_dispatch::method_hash!("Name"), SymbolExported = frc42_dispatch::method_hash!("Symbol"), TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 34dd4b302..bb2a7ef37 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -34,7 +34,7 @@ pub enum Method { Exec4 = 3, #[cfg(feature = "m2-native")] InstallCode = 4, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ExecExported = frc42_dispatch::method_hash!("Exec"), } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 9b88ed582..7e99005df 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -23,6 +23,7 @@ use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::*; use fvm_shared::smooth::FilterEstimate; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use itertools::Itertools; use log::{error, info, warn}; use multihash::Code::Blake2b256; use num_derive::FromPrimitive; @@ -123,9 +124,14 @@ pub enum Method { ChangeBeneficiary = 30, GetBeneficiary = 31, ExtendSectorExpiration2 = 32, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), + GetOwnerExported = frc42_dispatch::method_hash!("GetOwner"), + IsControllingAddressExported = frc42_dispatch::method_hash!("IsControllingAddress"), + GetSectorSizeExported = frc42_dispatch::method_hash!("GetSectorSize"), + GetAvailableBalanceExported = frc42_dispatch::method_hash!("GetAvailableBalance"), + GetVestingFundsExported = frc42_dispatch::method_hash!("GetVestingFunds"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -205,6 +211,7 @@ impl Actor { Ok(()) } + /// Returns the "controlling" addresses: the owner, the worker, and all control addresses fn control_addresses(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -216,6 +223,71 @@ impl Actor { }) } + /// Returns the owner address + fn get_owner(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let owner = get_miner_info(rt.store(), &state)?.owner; + Ok(GetOwnerReturn { owner }) + } + + /// Returns whether the provided address is "controlling". + /// The "controlling" addresses are the Owner, the Worker, and all Control Addresses. + fn is_controlling_address( + rt: &mut impl Runtime, + params: IsControllingAddressParam, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let input = match rt.resolve_address(¶ms.address) { + Some(a) => Address::new_id(a), + None => return Ok(IsControllingAddressReturn { is_controlling: false }), + }; + let state: State = rt.state()?; + let info = get_miner_info(rt.store(), &state)?; + let is_controlling = info + .control_addresses + .iter() + .chain(&[info.worker, info.owner]) + .into_iter() + .any(|a| *a == input); + + Ok(IsControllingAddressReturn { is_controlling }) + } + + /// Returns the miner's sector size + fn get_sector_size(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let sector_size = get_miner_info(rt.store(), &state)?.sector_size; + Ok(GetSectorSizeReturn { sector_size }) + } + + /// Returns the available balance of this miner. + /// This is calculated as actor balance - (vesting funds + pre-commit deposit + ip requirement + fee debt) + /// Can go negative if the miner is in IP debt. + fn get_available_balance( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let available_balance = + state.get_available_balance(&rt.current_balance()).map_err(|e| { + actor_error!(illegal_state, "failed to calculate available balance: {}", e) + })?; + Ok(GetAvailableBalanceReturn { available_balance }) + } + + /// Returns the funds vesting in this miner as a list of (vesting_epoch, vesting_amount) tuples. + fn get_vesting_funds(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let vesting_funds = state + .load_vesting_funds(rt.store()) + .map_err(|e| actor_error!(illegal_state, "failed to load vesting funds: {}", e))?; + let ret = vesting_funds.funds.into_iter().map(|v| (v.epoch, v.amount)).collect_vec(); + Ok(GetVestingFundsReturn { vesting_funds: ret }) + } + /// Will ALWAYS overwrite the existing control addresses with the control addresses passed in the params. /// If an empty addresses vector is passed, the control addresses will be cleared. /// A worker change will be scheduled if the worker passed in the params is different from the existing worker. @@ -5001,6 +5073,26 @@ impl ActorCode for Actor { Ok(RawBytes::default()) } None => Err(actor_error!(unhandled_message, "Invalid method")), + Some(Method::GetOwnerExported) => { + let res = Self::get_owner(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::IsControllingAddressExported) => { + let res = Self::is_controlling_address(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetSectorSizeExported) => { + let res = Self::get_sector_size(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetAvailableBalanceExported) => { + let res = Self::get_available_balance(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetVestingFundsExported) => { + let res = Self::get_vesting_funds(rt)?; + Ok(RawBytes::serialize(res)?) + } } } } diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index e59aead7f..b44c315ca 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -983,7 +983,7 @@ impl State { &self, actor_balance: &TokenAmount, ) -> anyhow::Result { - // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit + // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit - &self.initial_pledge Ok(self.get_unlocked_balance(actor_balance)? - &self.fee_debt) } diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 0d8dd31f1..49c8290d7 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -13,7 +13,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{ PoStProof, RegisteredPoStProof, RegisteredSealProof, RegisteredUpdateProof, SectorNumber, - StoragePower, + SectorSize, StoragePower, }; use fvm_shared::smooth::FilterEstimate; @@ -482,3 +482,50 @@ pub struct GetBeneficiaryReturn { } impl Cbor for GetBeneficiaryReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetOwnerReturn { + pub owner: Address, +} + +impl Cbor for GetOwnerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct IsControllingAddressParam { + pub address: Address, +} + +impl Cbor for IsControllingAddressParam {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct IsControllingAddressReturn { + pub is_controlling: bool, +} + +impl Cbor for IsControllingAddressReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetSectorSizeReturn { + pub sector_size: SectorSize, +} + +impl Cbor for GetSectorSizeReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetAvailableBalanceReturn { + pub available_balance: TokenAmount, +} + +impl Cbor for GetAvailableBalanceReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetVestingFundsReturn { + pub vesting_funds: Vec<(ChainEpoch, TokenAmount)>, +} + +impl Cbor for GetVestingFundsReturn {} diff --git a/actors/miner/tests/apply_rewards.rs b/actors/miner/tests/apply_rewards.rs index 1bec82b51..44f36c332 100644 --- a/actors/miner/tests/apply_rewards.rs +++ b/actors/miner/tests/apply_rewards.rs @@ -18,6 +18,7 @@ use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; mod util; + use fil_actor_miner::testing::check_state_invariants; use util::*; @@ -165,11 +166,12 @@ fn rewards_pay_back_fee_debt() { assert!(st.locked_funds.is_zero()); let amt = rt.get_balance(); - let available_before = st.get_available_balance(&amt).unwrap(); + let available_before = h.get_available_balance(&mut rt).unwrap(); assert!(available_before.is_positive()); let init_fee_debt: TokenAmount = 2 * &amt; // FeeDebt twice total balance st.fee_debt = init_fee_debt.clone(); - let available_after = st.get_available_balance(&amt).unwrap(); + rt.replace_state(&st); + let available_after = h.get_available_balance(&mut rt).unwrap(); assert!(available_after.is_negative()); rt.replace_state(&st); @@ -178,7 +180,7 @@ fn rewards_pay_back_fee_debt() { let penalty = TokenAmount::zero(); // manually update actor balance to include the added funds from outside let new_balance = &amt + &reward; - rt.set_balance(new_balance.clone()); + rt.set_balance(new_balance); // pledge change is new reward - reward taken for fee debt // 3*LockedRewardFactor*amt - 2*amt = remainingLocked @@ -203,7 +205,7 @@ fn rewards_pay_back_fee_debt() { BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), - expect_burnt.clone(), + expect_burnt, RawBytes::default(), ExitCode::OK, ); @@ -212,13 +214,10 @@ fn rewards_pay_back_fee_debt() { rt.call::(Method::ApplyRewards as u64, &RawBytes::serialize(params).unwrap()).unwrap(); rt.verify(); - // Set balance to deduct fee - let final_balance = &new_balance - &expect_burnt; - let st = h.get_state(&rt); // balance funds used to pay off fee debt // available balance should be 2 - let available_balance = st.get_available_balance(&final_balance).unwrap(); + let available_balance = h.get_available_balance(&mut rt).unwrap(); assert_eq!(available_before + reward - init_fee_debt - &remaining_locked, available_balance); assert!(!st.fee_debt.is_positive()); // remaining funds locked in vesting table diff --git a/actors/miner/tests/exported_getters.rs b/actors/miner/tests/exported_getters.rs new file mode 100644 index 000000000..459ec768d --- /dev/null +++ b/actors/miner/tests/exported_getters.rs @@ -0,0 +1,150 @@ +use fil_actor_miner::{ + Actor, GetAvailableBalanceReturn, GetOwnerReturn, GetSectorSizeReturn, + IsControllingAddressParam, IsControllingAddressReturn, Method, +}; +use fil_actors_runtime::cbor::serialize; +use fil_actors_runtime::test_utils::make_identity_cid; +use fil_actors_runtime::INIT_ACTOR_ADDR; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, sector::MAX_SECTOR_NUMBER}; +use std::ops::Sub; + +mod util; + +use util::*; + +const PERIOD_OFFSET: ChainEpoch = 100; + +// an expiration ~10 days greater than effective min expiration taking into account 30 days max +// between pre and prove commit +const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; + +#[test] +fn info_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.set_balance(BIG_BALANCE.clone()); + h.construct_and_verify(&mut rt); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + // owner is good + rt.expect_validate_caller_any(); + let owner_ret: GetOwnerReturn = rt + .call::(Method::GetOwnerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.owner, owner_ret.owner); + + // check that the controlling addresses all return true + for control in h.control_addrs.iter().chain(&[h.worker, h.owner]) { + rt.expect_validate_caller_any(); + let is_control_ret: IsControllingAddressReturn = rt + .call::( + Method::IsControllingAddressExported as u64, + &serialize(&IsControllingAddressParam { address: *control }, "serializing control") + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + assert!(is_control_ret.is_controlling); + + rt.verify(); + } + + // check that a non-controlling address doesn't return true + + rt.expect_validate_caller_any(); + let is_control_ret: IsControllingAddressReturn = rt + .call::( + Method::IsControllingAddressExported as u64, + &serialize( + &IsControllingAddressParam { address: INIT_ACTOR_ADDR }, + "serializing control", + ) + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + assert!(!is_control_ret.is_controlling); + + rt.verify(); + + // sector size is good + rt.expect_validate_caller_any(); + let sector_size_ret: GetSectorSizeReturn = rt + .call::(Method::GetSectorSizeExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.sector_size, sector_size_ret.sector_size); + + h.check_state(&rt); +} + +#[test] +fn collateral_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.balance.replace(BIG_BALANCE.clone()); + + let precommit_epoch = PERIOD_OFFSET + 1; + rt.set_epoch(precommit_epoch); + + h.construct_and_verify(&mut rt); + let dl_info = h.deadline(&rt); + + // Precommit a sector + // Use the max sector number to make sure everything works. + let sector_no = MAX_SECTOR_NUMBER; + let prove_commit_epoch = precommit_epoch + rt.policy.pre_commit_challenge_delay + 1; + let expiration = + dl_info.period_end() + DEFAULT_SECTOR_EXPIRATION * rt.policy.wpost_proving_period; // something on deadline boundary but > 180 days + + let precommit_params = + h.make_pre_commit_params(sector_no, precommit_epoch - 1, expiration, vec![]); + let precommit = + h.pre_commit_sector_and_get(&mut rt, precommit_params, PreCommitConfig::empty(), true); + + // run prove commit logic + rt.set_epoch(prove_commit_epoch); + let actor_balance = TokenAmount::from_whole(1000); + rt.balance.replace(actor_balance.clone()); + let pcc = ProveCommitConfig::empty(); + + let sector = h + .prove_commit_sector_and_confirm( + &mut rt, + &precommit, + h.make_prove_commit_params(sector_no), + pcc, + ) + .unwrap(); + + // query available balance + + rt.expect_validate_caller_any(); + let available_balance_ret: GetAvailableBalanceReturn = rt + .call::(Method::GetAvailableBalanceExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert_eq!(actor_balance.sub(sector.initial_pledge), available_balance_ret.available_balance); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 48861b90b..12c1a3517 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -22,14 +22,14 @@ use fil_actor_miner::{ CronEventPayload, Deadline, DeadlineInfo, Deadlines, DeclareFaultsParams, DeclareFaultsRecoveredParams, DeferredCronEventParams, DisputeWindowedPoStParams, ExpirationQueue, ExpirationSet, ExtendSectorExpiration2Params, ExtendSectorExpirationParams, - FaultDeclaration, GetBeneficiaryReturn, GetControlAddressesReturn, Method, - MinerConstructorParams as ConstructorParams, MinerInfo, Partition, PendingBeneficiaryChange, - PoStPartition, PowerPair, PreCommitSectorBatchParams, PreCommitSectorBatchParams2, - PreCommitSectorParams, ProveCommitSectorParams, RecoveryDeclaration, - ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, SectorPreCommitOnChainInfo, - Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, TerminationDeclaration, - VestingFunds, WindowedPoSt, WithdrawBalanceParams, WithdrawBalanceReturn, - CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, + FaultDeclaration, GetAvailableBalanceReturn, GetBeneficiaryReturn, GetControlAddressesReturn, + Method, MinerConstructorParams as ConstructorParams, MinerInfo, Partition, + PendingBeneficiaryChange, PoStPartition, PowerPair, PreCommitSectorBatchParams, + PreCommitSectorBatchParams2, PreCommitSectorParams, ProveCommitSectorParams, + RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, + SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, + TerminationDeclaration, VestingFunds, WindowedPoSt, WithdrawBalanceParams, + WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, }; use fil_actor_miner::{Method as MinerMethod, ProveCommitAggregateParams}; use fil_actor_power::{ @@ -861,6 +861,7 @@ impl ActorHarness { pc: &SectorPreCommitOnChainInfo, params: ProveCommitSectorParams, ) -> Result<(), ActorError> { + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); let seal_rand = TEST_RANDOMNESS_ARRAY_FROM_ONE; let seal_int_rand = TEST_RANDOMNESS_ARRAY_FROM_TWO; let interactive_epoch = pc.pre_commit_epoch + rt.policy.pre_commit_challenge_delay; @@ -2532,6 +2533,17 @@ impl ActorHarness { } ret } + + 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.expect_validate_caller_any(); + let available_balance_ret: GetAvailableBalanceReturn = rt + .call::(Method::GetAvailableBalanceExported as u64, &RawBytes::default())? + .deserialize()?; + rt.verify(); + Ok(available_balance_ret.available_balance) + } } #[allow(dead_code)] diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 56c413292..8bd64b74a 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -42,7 +42,7 @@ pub enum Method { SwapSigner = 7, ChangeNumApprovalsThreshold = 8, LockBalance = 9, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ProposeExported = frc42_dispatch::method_hash!("Propose"), ApproveExported = frc42_dispatch::method_hash!("Approve"), CancelExported = frc42_dispatch::method_hash!("Cancel"), diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 85602cc2c..666890dee 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -16,6 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 21dc612b6..43437812c 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -61,12 +61,16 @@ pub enum Method { // OnConsensusFault = 7, SubmitPoRepForBulkVerify = 8, CurrentTotalPower = 9, + // Method numbers derived from FRC-0042 standards + NetworkRawPowerExported = frc42_dispatch::method_hash!("NetworkRawPower"), + MinerRawPowerExported = frc42_dispatch::method_hash!("MinerRawPower"), } pub const ERR_TOO_MANY_PROVE_COMMITS: ExitCode = ExitCode::new(32); /// Storage Power Actor pub struct Actor; + impl Actor { /// Constructor for StoragePower actor fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { @@ -364,6 +368,34 @@ impl Actor { }) } + /// Returns the total raw power of the network. + /// This is defined as the sum of the active (i.e. non-faulty) byte commitments + /// of all miners that have more than the consensus minimum amount of storage active. + /// This value is static over an epoch, and does NOT get updated as messages are executed. + /// It is recalculated after all messages at an epoch have been executed. + fn network_raw_power(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(NetworkRawPowerReturn { raw_byte_power: st.this_epoch_raw_byte_power }) + } + + /// Returns the raw power claimed by the specified miner, + /// and whether the miner has more than the consensus minimum amount of storage active. + /// The raw power is defined as the active (i.e. non-faulty) byte commitments of the miner. + fn miner_raw_power( + rt: &mut impl Runtime, + params: MinerRawPowerParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + let (raw_byte_power, meets_consensus_minimum) = + st.miner_nominal_power_meets_consensus_minimum(rt.policy(), rt.store(), params.miner)?; + + Ok(MinerRawPowerReturn { raw_byte_power, meets_consensus_minimum }) + } + fn process_batch_proof_verifies( rt: &mut impl Runtime, rewret: &ThisEpochRewardReturn, @@ -660,6 +692,14 @@ impl ActorCode for Actor { let res = Self::current_total_power(rt)?; Ok(RawBytes::serialize(res)?) } + Some(Method::NetworkRawPowerExported) => { + let res = Self::network_raw_power(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::MinerRawPowerExported) => { + let res = Self::miner_raw_power(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } diff --git a/actors/power/src/state.rs b/actors/power/src/state.rs index 91739d0eb..fb029d76f 100644 --- a/actors/power/src/state.rs +++ b/actors/power/src/state.rs @@ -3,12 +3,12 @@ use std::ops::Neg; -use anyhow::{anyhow, Context}; +use anyhow::anyhow; use cid::Cid; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::{ actor_error, make_empty_map, make_map_with_root, make_map_with_root_and_bitwidth, - ActorDowncast, ActorError, Map, Multimap, + ActorDowncast, ActorError, AsActorError, Map, Multimap, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -21,7 +21,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; use fvm_shared::smooth::{AlphaBetaFilter, FilterEstimate, DEFAULT_ALPHA, DEFAULT_BETA}; -use fvm_shared::HAMT_BIT_WIDTH; +use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; use integer_encoding::VarInt; use lazy_static::lazy_static; use num_traits::Signed; @@ -103,26 +103,37 @@ impl State { &self, policy: &Policy, s: &BS, - miner: &Address, - ) -> anyhow::Result { - let claims = make_map_with_root_and_bitwidth(&self.claims, s, HAMT_BIT_WIDTH)?; + miner: ActorID, + ) -> Result<(StoragePower, bool), ActorError> { + let claims = make_map_with_root_and_bitwidth(&self.claims, s, HAMT_BIT_WIDTH) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load claims for miner: {}", miner) + })?; - let claim = - get_claim(&claims, miner)?.ok_or_else(|| anyhow!("no claim for actor: {}", miner))?; + let claim = get_claim(&claims, &Address::new_id(miner)) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to get claim for miner: {}", miner) + })? + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("no claim for actor: {}", miner) + })?; - let miner_nominal_power = &claim.raw_byte_power; + let miner_nominal_power = claim.raw_byte_power.clone(); let miner_min_power = consensus_miner_min_power(policy, claim.window_post_proof_type) - .context("could not get miner min power from proof type: {}")?; + .context_code( + ExitCode::USR_ILLEGAL_STATE, + "could not get miner min power from proof type: {}", + )?; - if miner_nominal_power >= &miner_min_power { + if miner_nominal_power >= miner_min_power { // If miner is larger than min power requirement, valid - Ok(true) + Ok((miner_nominal_power, true)) } else if self.miner_above_min_power_count >= CONSENSUS_MINER_MIN_MINERS { // if min consensus miners requirement met, return false - Ok(false) + Ok((miner_nominal_power, false)) } else { // if fewer miners than consensus minimum, return true if non-zero power - Ok(miner_nominal_power.is_positive()) + Ok((miner_nominal_power.clone(), miner_nominal_power.is_positive())) } } diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 74537f7ad..8e26ab4b0 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -9,6 +9,7 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; use fvm_shared::smooth::FilterEstimate; +use fvm_shared::ActorID; pub type SectorTermination = i64; @@ -23,7 +24,7 @@ pub const CRON_QUEUE_HAMT_BITWIDTH: u32 = 6; pub const CRON_QUEUE_AMT_BITWIDTH: u32 = 6; pub const PROOF_VALIDATION_BATCH_AMT_BITWIDTH: u32 = 4; -#[derive(Serialize_tuple, Deserialize_tuple, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] pub struct CreateMinerParams { pub owner: Address, pub worker: Address, @@ -32,9 +33,10 @@ pub struct CreateMinerParams { pub peer: Vec, pub multiaddrs: Vec, } + impl Cbor for CreateMinerParams {} -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct CreateMinerReturn { /// Canonical ID-based address for the actor. pub id_address: Address, @@ -42,7 +44,7 @@ pub struct CreateMinerReturn { pub robust_address: Address, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct UpdateClaimedPowerParams { #[serde(with = "bigint_ser")] pub raw_byte_delta: StoragePower, @@ -50,13 +52,13 @@ pub struct UpdateClaimedPowerParams { pub quality_adjusted_delta: StoragePower, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct EnrollCronEventParams { pub event_epoch: ChainEpoch, pub payload: RawBytes, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] pub struct CurrentTotalPowerReturn { #[serde(with = "bigint_ser")] pub raw_byte_power: StoragePower, @@ -65,3 +67,29 @@ pub struct CurrentTotalPowerReturn { pub pledge_collateral: TokenAmount, pub quality_adj_power_smoothed: FilterEstimate, } + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct NetworkRawPowerReturn { + #[serde(with = "bigint_ser")] + pub raw_byte_power: StoragePower, +} + +impl Cbor for NetworkRawPowerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerRawPowerParams { + pub miner: ActorID, +} + +impl Cbor for MinerRawPowerParams {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct MinerRawPowerReturn { + #[serde(with = "bigint_ser")] + pub raw_byte_power: StoragePower, + pub meets_consensus_minimum: bool, +} + +impl Cbor for MinerRawPowerReturn {} diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 37ffb8410..dd53f55fc 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -222,9 +222,7 @@ impl Harness { pub fn get_claim(&self, rt: &MockRuntime, miner: &Address) -> Option { let st: State = rt.get_state(); - let claims = - make_map_with_root_and_bitwidth(&st.claims, rt.store(), HAMT_BIT_WIDTH).unwrap(); - claims.get(&miner.to_bytes()).unwrap().cloned() + st.get_claim(rt.store(), miner).unwrap() } pub fn delete_claim(&mut self, rt: &mut MockRuntime, miner: &Address) { diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 5be4416bd..5be231f36 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -2,8 +2,8 @@ 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, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, - SYSTEM_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{runtime::Policy, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, RawBytes}; @@ -17,9 +17,11 @@ use num_traits::Zero; use std::ops::Neg; use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, CreateMinerParams, EnrollCronEventParams, - Method, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, + EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, + State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, }; +use fil_actors_runtime::cbor::serialize; use crate::harness::*; @@ -589,6 +591,55 @@ fn claimed_power_is_externally_available() { h.check_state(&rt); } +#[test] +fn get_network_and_miner_power() { + let power_unit = &consensus_miner_min_power( + &Policy::default(), + RegisteredPoStProof::StackedDRGWindow32GiBV1, + ) + .unwrap(); + + let (mut h, mut rt) = setup(); + + h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap(); + h.update_claimed_power(&mut rt, MINER1, power_unit, power_unit); + + // manually update state in lieu of cron running + let mut state: State = rt.get_state(); + state.this_epoch_raw_byte_power = power_unit.clone(); + rt.replace_state(&state); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + rt.expect_validate_caller_any(); + let network_power: NetworkRawPowerReturn = rt + .call::(Method::NetworkRawPowerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(power_unit, &network_power.raw_byte_power); + + rt.expect_validate_caller_any(); + let miner_power: MinerRawPowerReturn = rt + .call::( + Method::MinerRawPowerExported as u64, + &serialize( + &MinerRawPowerParams { miner: MINER1.id().unwrap() }, + "serializing MinerRawPowerParams", + ) + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(power_unit, &miner_power.raw_byte_power); + + h.check_state(&rt); +} + #[test] fn given_no_miner_claim_update_pledge_total_should_abort() { let (mut h, mut rt) = setup(); diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index eb6716cc2..d7962cfd2 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -60,7 +60,7 @@ pub enum Method { GetClaims = 10, ExtendClaimTerms = 11, RemoveExpiredClaims = 12, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), From abbf12c190720c8a3700e725d4f65b375516b37b Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Thu, 10 Nov 2022 05:58:34 +1100 Subject: [PATCH 132/339] Built-in market API for deal proposal metadata (#818) --- actors/market/src/lib.rs | 141 +++++++++++++++++++++++++++ actors/market/src/state.rs | 16 +++ actors/market/src/types.rs | 69 +++++++++++++ actors/market/tests/deal_api_test.rs | 81 +++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 actors/market/tests/deal_api_test.rs diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 69104c244..45dbb7e7d 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -77,6 +77,15 @@ pub enum Method { AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), + GetDealDataCommitmentExported = frc42_dispatch::method_hash!("GetDealDataCommitment"), + GetDealClientExported = frc42_dispatch::method_hash!("GetDealClient"), + GetDealProviderExported = frc42_dispatch::method_hash!("GetDealProvider"), + GetDealLabelExported = frc42_dispatch::method_hash!("GetDealLabel"), + GetDealTermExported = frc42_dispatch::method_hash!("GetDealTerm"), + GetDealEpochPriceExported = frc42_dispatch::method_hash!("GetDealEpochPrice"), + GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), + GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), + GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), } /// Market Actor @@ -1049,6 +1058,101 @@ impl Actor { } Ok(()) } + + /// Returns the data commitment and size of a deal proposal. + /// This will be available after the deal is published (whether or not is is activated) + /// and up until some undefined period after it is terminated. + fn get_deal_data_commitment( + rt: &mut impl Runtime, + params: GetDealDataCommitmentParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealDataCommitmentReturn { data: found.piece_cid, size: found.piece_size }) + } + + /// Returns the client of a deal proposal. + fn get_deal_client( + rt: &mut impl Runtime, + params: GetDealClientParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealClientReturn { client: found.client.id().unwrap() }) + } + + /// Returns the provider of a deal proposal. + fn get_deal_provider( + rt: &mut impl Runtime, + params: GetDealProviderParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealProviderReturn { provider: found.provider.id().unwrap() }) + } + + /// Returns the label of a deal proposal. + fn get_deal_label( + rt: &mut impl Runtime, + params: GetDealLabelParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealLabelReturn { label: found.label }) + } + + /// Returns the start and end epochs of a deal proposal. + /// The deal term is a half-open range, exclusive of the end epoch. + fn get_deal_term( + rt: &mut impl Runtime, + params: GetDealTermParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealTermReturn { start: found.start_epoch, end: found.end_epoch }) + } + + /// Returns the per-epoch price of a deal proposal. + fn get_deal_epoch_price( + rt: &mut impl Runtime, + params: GetDealEpochPriceParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealEpochPriceReturn { price_per_epoch: found.storage_price_per_epoch }) + } + + /// Returns the client collateral requirement for a deal proposal. + fn get_deal_client_collateral( + rt: &mut impl Runtime, + params: GetDealClientCollateralParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealClientCollateralReturn { collateral: found.client_collateral }) + } + + /// Returns the provider collateral requirement for a deal proposal. + fn get_deal_provider_collateral( + rt: &mut impl Runtime, + params: GetDealProviderCollateralParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealProviderCollateralReturn { collateral: found.provider_collateral }) + } + + /// Returns the verified flag for a deal proposal. + /// Note that the source of truth for verified allocations and claims is + /// the verified registry actor. + fn get_deal_verified( + rt: &mut impl Runtime, + params: GetDealVerifiedParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealVerifiedReturn { verified: found.verified_deal }) + } } fn compute_data_commitment( @@ -1464,6 +1568,43 @@ impl ActorCode for Actor { let res = Self::get_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetDealDataCommitmentExported) => { + let res = Self::get_deal_data_commitment(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealClientExported) => { + let res = Self::get_deal_client(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealProviderExported) => { + let res = Self::get_deal_provider(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealLabelExported) => { + let res = Self::get_deal_label(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealTermExported) => { + let res = Self::get_deal_term(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealEpochPriceExported) => { + let res = Self::get_deal_epoch_price(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealClientCollateralExported) => { + let res = Self::get_deal_client_collateral(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealProviderCollateralExported) => { + let res = + Self::get_deal_provider_collateral(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealVerifiedExported) => { + let res = Self::get_deal_verified(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/state.rs b/actors/market/src/state.rs index b07703efa..66c783066 100644 --- a/actors/market/src/state.rs +++ b/actors/market/src/state.rs @@ -115,6 +115,22 @@ impl State { + &self.total_client_storage_fee } + pub fn get_proposal( + &self, + store: &BS, + id: DealID, + ) -> Result { + let proposals = DealArray::load(&self.proposals, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals")?; + let found = proposals + .get(id) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load deal proposal {}", id) + })? + .with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?; + Ok(found.clone()) + } + pub(super) fn mutator<'bs, BS: Blockstore>( &mut self, store: &'bs BS, diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index bac78d0df..afdfa2da4 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -15,6 +15,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::piece::PaddedPieceSize; use fvm_shared::ActorID; +use crate::Label; use fvm_shared::sector::RegisteredSealProof; use super::deal::{ClientDealProposal, DealProposal, DealState}; @@ -136,3 +137,71 @@ pub struct SectorDataSpec { pub deal_ids: Vec, pub sector_type: RegisteredSealProof, } + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct DealQueryParams { + pub id: DealID, +} + +pub type GetDealDataCommitmentParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealDataCommitmentReturn { + pub data: Cid, + pub size: PaddedPieceSize, +} + +pub type GetDealClientParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealClientReturn { + pub client: ActorID, +} + +pub type GetDealProviderParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealProviderReturn { + pub provider: ActorID, +} + +pub type GetDealLabelParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealLabelReturn { + pub label: Label, +} + +pub type GetDealTermParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealTermReturn { + pub start: ChainEpoch, // First epoch for the deal (inclusive) + pub end: ChainEpoch, // Epoch at which the deal expires (i.e. exclusive). +} + +pub type GetDealEpochPriceParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealEpochPriceReturn { + pub price_per_epoch: TokenAmount, +} + +pub type GetDealClientCollateralParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealClientCollateralReturn { + pub collateral: TokenAmount, +} + +pub type GetDealProviderCollateralParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealProviderCollateralReturn { + pub collateral: TokenAmount, +} + +pub type GetDealVerifiedParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealVerifiedReturn { + pub verified: bool, +} diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs new file mode 100644 index 000000000..4471f0f43 --- /dev/null +++ b/actors/market/tests/deal_api_test.rs @@ -0,0 +1,81 @@ +use fvm_ipld_encoding::RawBytes; +use fvm_shared::clock::ChainEpoch; +use serde::de::DeserializeOwned; + +use fil_actor_market::{ + Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, + GetDealDataCommitmentReturn, GetDealEpochPriceReturn, GetDealLabelReturn, + GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, + GetDealVerifiedReturn, Method, +}; +use fil_actors_runtime::network::EPOCHS_IN_DAY; +use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; +use harness::*; + +mod harness; + +#[test] +fn proposal_data() { + let start_epoch = 1000; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + let publish_epoch = ChainEpoch::from(1); + + let mut rt = setup(); + rt.set_epoch(publish_epoch); + let next_allocation_id = 1; + + let proposal = generate_deal_and_add_funds( + &mut rt, + CLIENT_ADDR, + &MinerAddresses::default(), + start_epoch, + end_epoch, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + let id = + publish_deals(&mut rt, &MinerAddresses::default(), &[proposal.clone()], next_allocation_id) + [0]; + + let data: GetDealDataCommitmentReturn = + query_deal(&mut rt, Method::GetDealDataCommitmentExported, id); + assert_eq!(proposal.piece_cid, data.data); + assert_eq!(proposal.piece_size, data.size); + + let client: GetDealClientReturn = query_deal(&mut rt, Method::GetDealClientExported, id); + assert_eq!(proposal.client.id().unwrap(), client.client); + + let provider: GetDealProviderReturn = query_deal(&mut rt, Method::GetDealProviderExported, id); + assert_eq!(proposal.provider.id().unwrap(), provider.provider); + + let label: GetDealLabelReturn = query_deal(&mut rt, Method::GetDealLabelExported, id); + assert_eq!(proposal.label, label.label); + + let term: GetDealTermReturn = query_deal(&mut rt, Method::GetDealTermExported, id); + assert_eq!(proposal.start_epoch, term.start); + assert_eq!(proposal.end_epoch, term.end); + + let price: GetDealEpochPriceReturn = query_deal(&mut rt, Method::GetDealEpochPriceExported, id); + assert_eq!(proposal.storage_price_per_epoch, price.price_per_epoch); + + let client_collateral: GetDealClientCollateralReturn = + query_deal(&mut rt, Method::GetDealClientCollateralExported, id); + assert_eq!(proposal.client_collateral, client_collateral.collateral); + + let provider_collateral: GetDealProviderCollateralReturn = + query_deal(&mut rt, Method::GetDealProviderCollateralExported, id); + assert_eq!(proposal.provider_collateral, provider_collateral.collateral); + + let verified: GetDealVerifiedReturn = query_deal(&mut rt, Method::GetDealVerifiedExported, id); + assert_eq!(proposal.verified_deal, verified.verified); + + check_state(&rt); +} + +fn query_deal(rt: &mut MockRuntime, method: Method, id: u64) -> T { + let params = DealQueryParams { id }; + rt.expect_validate_caller_any(); + rt.call::(method as u64, &RawBytes::serialize(params).unwrap()) + .unwrap() + .deserialize() + .unwrap() +} From 687ae9467c526ceda52590b75b032680cbe1dfe8 Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:27:35 -0500 Subject: [PATCH 133/339] Call exported authenticate method from PSD (#829) Co-authored-by: zenground0 --- actors/account/tests/account_actor_test.rs | 4 ++-- actors/market/src/ext.rs | 3 ++- test_vm/src/util.rs | 2 +- test_vm/tests/account_authenticate_message_test.rs | 6 +++--- test_vm/tests/publish_deals_test.rs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index 4f9bdf3a0..de2792177 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -103,7 +103,7 @@ fn authenticate_message() { }); assert_eq!( RawBytes::default(), - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms).unwrap() + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms).unwrap() ); rt.verify(); @@ -118,7 +118,7 @@ fn authenticate_message() { expect_abort_contains_message( ExitCode::USR_ILLEGAL_ARGUMENT, "bad signature", - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms), ); rt.verify(); diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index ff4082682..63d62b1d2 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -10,7 +10,8 @@ use fvm_shared::smooth::FilterEstimate; pub mod account { use super::*; - pub const AUTHENTICATE_MESSAGE_METHOD: u64 = 3; + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); #[derive(Serialize_tuple, Deserialize_tuple)] pub struct AuthenticateMessageParams { diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index c04139d0a..72ce940dc 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -1130,7 +1130,7 @@ pub fn market_publish_deal( }, ExpectInvocation { to: deal_client, - method: AccountMethod::AuthenticateMessage as u64, + method: AccountMethod::AuthenticateMessageExported as u64, ..Default::default() }, ]; diff --git a/test_vm/tests/account_authenticate_message_test.rs b/test_vm/tests/account_authenticate_message_test.rs index bc06b095a..5506ec617 100644 --- a/test_vm/tests/account_authenticate_message_test.rs +++ b/test_vm/tests/account_authenticate_message_test.rs @@ -1,5 +1,5 @@ use fil_actor_account::types::AuthenticateMessageParams; -use fil_actor_account::Method::AuthenticateMessage; +use fil_actor_account::Method::AuthenticateMessageExported; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::bigint::Zero; @@ -32,7 +32,7 @@ fn account_authenticate_message() { addr, addr, TokenAmount::zero(), - AuthenticateMessage as u64, + AuthenticateMessageExported as u64, authenticate_message_params, ); @@ -44,7 +44,7 @@ fn account_authenticate_message() { addr, addr, TokenAmount::zero(), - AuthenticateMessage as u64, + AuthenticateMessageExported as u64, authenticate_message_params, ExitCode::USR_ILLEGAL_ARGUMENT, ); diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 1e0febffb..242544165 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -553,7 +553,7 @@ fn psd_bad_sig() { }, ExpectInvocation { to: a.client1, - method: AccountMethod::AuthenticateMessage as u64, + method: AccountMethod::AuthenticateMessageExported as u64, params: Some( serialize( &AuthenticateMessageParams { From ac596976c317fed7cabbc4570afa1c36794457a9 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Nov 2022 19:06:55 -0500 Subject: [PATCH 134/339] Drop CALLER_TYPES_SIGNABLE and signable caller validation (#821) --- actors/market/src/lib.rs | 12 ++-- .../market/tests/cron_tick_timedout_deals.rs | 4 +- actors/market/tests/harness.rs | 16 +++-- actors/market/tests/market_actor_test.rs | 62 +++++-------------- .../tests/publish_storage_deals_failures.rs | 37 ++--------- actors/miner/src/lib.rs | 61 +++++++----------- actors/miner/src/state.rs | 18 +++--- .../tests/miner_actor_test_construction.rs | 28 --------- actors/miner/tests/miner_actor_test_wpost.rs | 8 +-- actors/miner/tests/state_harness.rs | 5 +- actors/miner/tests/util.rs | 8 +-- actors/multisig/src/lib.rs | 8 +-- actors/multisig/tests/multisig_actor_test.rs | 20 +++--- actors/multisig/tests/util.rs | 8 +-- actors/power/src/lib.rs | 2 +- actors/power/tests/harness/mod.rs | 3 +- actors/power/tests/power_actor_tests.rs | 32 +--------- runtime/src/builtin/shared.rs | 6 -- 18 files changed, 97 insertions(+), 241 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 45dbb7e7d..287e6c239 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -32,9 +32,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, - AsActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, - DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, - VERIFIED_REGISTRY_ACTOR_ADDR, + AsActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, + REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use crate::ext::verifreg::{AllocationID, AllocationRequest}; @@ -114,8 +113,7 @@ impl Actor { )); } - // only signing parties can add balance for client AND provider. - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let (nominal, _, _) = escrow_address(rt, &provider_or_client)?; @@ -228,9 +226,7 @@ impl Actor { rt: &mut impl Runtime, params: PublishStorageDealsParams, ) -> Result { - // Deal message must have a From field identical to the provider of all the deals. - // This allows us to retain and verify only the client's signature in each deal proposal itself. - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; if params.deals.is_empty() { return Err(actor_error!(illegal_argument, "Empty deals parameter")); } diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index dfdff2e45..9a5a627a1 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -6,7 +6,7 @@ use fil_actor_market::{ }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE}; +use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; @@ -84,7 +84,7 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l let client_deal_proposal = ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig }; let params = PublishStorageDealsParams { deals: vec![client_deal_proposal] }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 7bccd1506..36a071c2d 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -27,8 +27,8 @@ use fil_actors_runtime::{ network::EPOCHS_IN_DAY, runtime::{builtins::Type, Policy, Runtime}, test_utils::*, - ActorError, BatchReturn, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, - CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, + ActorError, BatchReturn, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, + DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_encoding::{to_vec, RawBytes}; @@ -167,7 +167,7 @@ pub fn add_provider_funds(rt: &mut MockRuntime, amount: TokenAmount, addrs: &Min rt.set_value(amount.clone()); rt.set_address_actor_type(addrs.provider, *MINER_ACTOR_CODE_ID); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.owner); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(rt, addrs.provider, addrs.owner, addrs.worker); @@ -188,8 +188,7 @@ pub fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenA rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addr); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(addr).unwrap()) .is_ok()); @@ -440,8 +439,7 @@ pub fn publish_deals( publish_deals: &[DealProposal], next_allocation_id: AllocationID, ) -> Vec { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); let return_value = GetControlAddressesReturnParams { owner: addrs.owner, worker: addrs.worker, @@ -564,7 +562,7 @@ pub fn publish_deals_expect_abort( proposal: DealProposal, expected_exit_code: ExitCode, ) { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address( rt, miner_addresses.provider, @@ -747,7 +745,7 @@ where rt.set_epoch(current_epoch); post_setup(&mut rt, &mut deal_proposal); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index b4e0c63e0..ce6212a49 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -14,7 +14,7 @@ use fil_actors_runtime::runtime::{builtins::Type, Policy, Runtime}; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ make_empty_map, make_map_with_root_and_bitwidth, ActorError, BatchReturn, Map, SetMultimap, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, DATACAP_TOKEN_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use frc46_token::token::types::{TransferFromParams, TransferFromReturn}; @@ -195,7 +195,7 @@ fn adds_to_provider_escrow_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -378,28 +378,6 @@ fn worker_balance_after_withdrawal_must_account_for_slashed_funds() { check_state(&rt); } -#[test] -fn fails_unless_called_by_an_account_actor() { - let mut rt = setup(); - - rt.set_value(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - - rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - assert_eq!( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::AddBalance as u64, - &RawBytes::serialize(PROVIDER_ADDR).unwrap(), - ) - .unwrap_err() - .exit_code() - ); - - rt.verify(); - check_state(&rt); -} - #[test] fn adds_to_non_provider_funds() { struct TestCase { @@ -418,8 +396,7 @@ fn adds_to_non_provider_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); assert_eq!( RawBytes::default(), rt.call::( @@ -564,7 +541,6 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); - // only signing parties can add balance for client AND provider. rt.expect_validate_caller_addr(vec![OWNER_ADDR, WORKER_ADDR]); let params = WithdrawBalanceParams { provider_or_client: PROVIDER_ADDR, @@ -821,7 +797,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t rt.set_value(amount.clone()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, client_resolved); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(client_bls).unwrap()) .is_ok()); @@ -833,7 +809,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // add funds for provider using it's BLS address -> will be resolved and persisted rt.value_received = deal.provider_collateral.clone(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -850,7 +826,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // publish deal using the BLS addresses rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1402,7 +1378,7 @@ fn cannot_publish_the_same_deal_twice_before_a_cron_tick() { let params = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal: d2.clone(), client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -1770,7 +1746,7 @@ fn insufficient_client_balance_in_a_batch() { deal1.provider_balance_requirement().add(deal2.provider_balance_requirement()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(provider_funds); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1802,7 +1778,7 @@ fn insufficient_client_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1883,7 +1859,7 @@ fn insufficient_provider_balance_in_a_batch() { // Provider has enough for only the second deal rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(deal2.provider_balance_requirement().clone()); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1919,7 +1895,7 @@ fn insufficient_provider_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1990,16 +1966,12 @@ fn add_balance_restricted_correctly() { ); // can call the exported method num - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "forbidden, allowed: [Account, Multisig]", - rt.call::( - Method::AddBalanceExported as MethodNum, - &RawBytes::serialize(CLIENT_ADDR).unwrap(), - ), - ); + rt.expect_validate_caller_any(); + rt.call::( + Method::AddBalanceExported as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ) + .unwrap(); rt.verify(); } diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index 38b4315d0..7cdf2492b 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -9,7 +9,6 @@ use fil_actor_market::{ use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; @@ -255,7 +254,7 @@ fn fail_when_provider_has_some_funds_but_not_enough_for_a_deal() { deals: vec![ClientDealProposal { proposal: deal1.clone(), client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -315,7 +314,7 @@ fn fail_when_deals_have_different_providers() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -363,36 +362,12 @@ fn fail_when_deals_have_different_providers() { check_state(&rt); } -#[test] -fn fail_when_caller_is_not_of_signable_type() { - let start_epoch = 10; - let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; - - let mut rt = setup(); - let deal = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); - let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); - let params = PublishStorageDealsParams { - deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], - }; - let w = Address::new_id(1000); - rt.set_caller(*MINER_ACTOR_CODE_ID, w); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - expect_abort( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::PublishStorageDeals as u64, - &RawBytes::serialize(params).unwrap(), - ), - ); - check_state(&rt); -} - #[test] fn fail_when_no_deals_in_params() { let mut rt = setup(); let params = PublishStorageDealsParams { deals: vec![] }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -417,7 +392,7 @@ fn fail_to_resolve_provider_address() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -440,7 +415,7 @@ fn caller_is_not_the_same_as_the_worker_address_for_miner() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(999)); expect_abort( @@ -469,7 +444,7 @@ fn fails_if_provider_is_not_a_storage_miner_actor() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 7e99005df..dd9404e3b 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -22,7 +22,7 @@ use fvm_shared::randomness::*; use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::*; use fvm_shared::smooth::FilterEstimate; -use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use itertools::Itertools; use log::{error, info, warn}; use multihash::Code::Blake2b256; @@ -42,8 +42,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, - STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; pub use monies::*; pub use partition_state::*; @@ -151,12 +151,19 @@ impl Actor { check_peer_info(rt.policy(), ¶ms.peer_id, ¶ms.multi_addresses)?; check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?; - let owner = resolve_control_address(rt, params.owner)?; + let owner = rt.resolve_address(¶ms.owner).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner) + })?; + let worker = resolve_worker_address(rt, params.worker)?; let control_addresses: Vec<_> = params .control_addresses .into_iter() - .map(|address| resolve_control_address(rt, address)) + .map(|address| { + rt.resolve_address(&address).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve control address: {}", address) + }) + }) .collect::>()?; let policy = rt.policy(); @@ -297,11 +304,16 @@ impl Actor { ) -> Result<(), ActorError> { check_control_addresses(rt.policy(), ¶ms.new_control_addresses)?; - let new_worker = resolve_worker_address(rt, params.new_worker)?; + let new_worker = Address::new_id(resolve_worker_address(rt, params.new_worker)?); let control_addresses: Vec
= params .new_control_addresses .into_iter() - .map(|address| resolve_control_address(rt, address)) + .map(|address| { + rt.resolve_address(&address).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve control address: {}", address) + }) + }) + .map(|id_result| id_result.map(Address::new_id)) .collect::>()?; rt.transaction(|state: &mut State, rt| { @@ -1419,7 +1431,7 @@ impl Actor { rt: &mut impl Runtime, params: DisputeWindowedPoStParams, ) -> Result<(), ActorError> { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let reporter = rt.message().caller(); { @@ -3057,7 +3069,7 @@ impl Actor { // Note: only the first report of any fault is processed because it sets the // ConsensusFaultElapsed state variable to an epoch after the fault, and reports prior to // that epoch are no longer valid - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let reporter = rt.message().caller(); let fault = rt @@ -4319,36 +4331,9 @@ fn request_current_total_power( Ok(power) } -/// Resolves an address to an ID address and verifies that it is address of an account or multisig actor. -fn resolve_control_address(rt: &impl Runtime, raw: Address) -> Result { - let resolved = rt - .resolve_address(&raw) - .ok_or_else(|| actor_error!(illegal_argument, "unable to resolve address: {}", raw))?; - - let owner_code = rt - .get_actor_code_cid(&resolved) - .ok_or_else(|| actor_error!(illegal_argument, "no code for address: {}", resolved))?; - - let is_principal = rt - .resolve_builtin_actor_type(&owner_code) - .as_ref() - .map(|t| CALLER_TYPES_SIGNABLE.contains(t)) - .unwrap_or(false); - - if !is_principal { - return Err(actor_error!( - illegal_argument, - "owner actor type must be a principal, was {}", - owner_code - )); - } - - Ok(Address::new_id(resolved)) -} - /// Resolves an address to an ID address and verifies that it is address of an account actor with an associated BLS key. /// The worker must be BLS since the worker key will be used alongside a BLS-VRF. -fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result { +fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result { let resolved = rt .resolve_address(&raw) .ok_or_else(|| actor_error!(illegal_argument, "unable to resolve address: {}", raw))?; @@ -4381,7 +4366,7 @@ fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result
Result<(), ActorError> { diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index b44c315ca..1a3f0499a 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -24,7 +24,8 @@ use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, SectorSize, MAX_SECTOR_NUMBER}; -use fvm_shared::HAMT_BIT_WIDTH; +use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; +use itertools::Itertools; use num_traits::Zero; use super::beneficiary::*; @@ -1266,9 +1267,9 @@ pub struct MinerInfo { impl MinerInfo { pub fn new( - owner: Address, - worker: Address, - control_addresses: Vec
, + owner: ActorID, + worker: ActorID, + control_addresses: Vec, peer_id: Vec, multi_address: Vec, window_post_proof_type: RegisteredPoStProof, @@ -1282,11 +1283,12 @@ impl MinerInfo { .map_err(|e| actor_error!(illegal_argument, "invalid partition sectors: {}", e))?; Ok(Self { - owner, - worker, - control_addresses, + owner: Address::new_id(owner), + worker: Address::new_id(worker), + control_addresses: control_addresses.into_iter().map(Address::new_id).collect_vec(), + pending_worker_key: None, - beneficiary: owner, + beneficiary: Address::new_id(owner), beneficiary_term: BeneficiaryTerm::default(), pending_beneficiary_term: None, peer_id, diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 9c9d44a0a..844b1d59b 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -168,34 +168,6 @@ fn control_addresses_are_resolved_during_construction() { assert_eq!(control2id, info.control_addresses[1]); } -#[test] -fn fails_if_control_address_is_not_an_account_actor() { - let mut env = prepare_env(); - - let control1 = Address::new_id(501); - env.control_addrs = vec![control1]; - env.rt.actor_code_cids.insert(control1, *PAYCH_ACTOR_CODE_ID); - - let params = constructor_params(&env); - env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); - env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); - env.rt.expect_send( - env.worker, - AccountMethod::PubkeyAddress as u64, - RawBytes::default(), - TokenAmount::zero(), - RawBytes::serialize(env.worker_key).unwrap(), - ExitCode::OK, - ); - - let result = env - .rt - .call::(Method::Constructor as u64, &RawBytes::serialize(params).unwrap()) - .unwrap_err(); - assert_eq!(result.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); - env.rt.verify(); -} - #[test] fn test_construct_with_invalid_peer_id() { let mut env = prepare_env(); diff --git a/actors/miner/tests/miner_actor_test_wpost.rs b/actors/miner/tests/miner_actor_test_wpost.rs index 6817181d4..1f9508f54 100644 --- a/actors/miner/tests/miner_actor_test_wpost.rs +++ b/actors/miner/tests/miner_actor_test_wpost.rs @@ -4,7 +4,6 @@ use fil_actor_miner as miner; use fil_actor_miner::PowerPair; use fil_actors_runtime::runtime::DomainSeparationTag; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_bitfield::BitField; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; @@ -1031,7 +1030,7 @@ fn cannot_dispute_posts_when_the_challenge_window_is_open() { let params = miner::DisputeWindowedPoStParams { deadline: dlinfo.index, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); h.expect_query_network_info(&mut rt); let result = rt.call::( @@ -1092,8 +1091,7 @@ fn can_dispute_up_till_window_end_but_not_after() { // Now try to dispute. let params = miner::DisputeWindowedPoStParams { deadline: dlidx, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); h.expect_query_network_info(&mut rt); let result = rt.call::( @@ -1125,7 +1123,7 @@ fn cant_dispute_up_with_an_invalid_deadline() { let params = miner::DisputeWindowedPoStParams { deadline: 50, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let result = rt.call::( miner::Method::DisputeWindowedPoSt as u64, diff --git a/actors/miner/tests/state_harness.rs b/actors/miner/tests/state_harness.rs index 08a653086..7c982ec91 100644 --- a/actors/miner/tests/state_harness.rs +++ b/actors/miner/tests/state_harness.rs @@ -10,7 +10,6 @@ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::BytesDe; use fvm_ipld_encoding::CborStore; use fvm_ipld_hamt::Error as HamtError; -use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{SectorNumber, SectorSize}; use fvm_shared::{clock::ChainEpoch, clock::QuantSpec, sector::RegisteredPoStProof}; @@ -34,8 +33,8 @@ impl StateHarness { // store init let store = MemoryBlockstore::default(); // state field init - let owner = Address::new_id(1); - let worker = Address::new_id(2); + let owner = 1; + let worker = 2; let test_window_post_proof_type = RegisteredPoStProof::StackedDRGWindow2KiBV1; diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 12c1a3517..c0e5bf7d1 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -45,8 +45,8 @@ use fil_actors_runtime::runtime::{DomainSeparationTag, Policy, Runtime, RuntimeP use fil_actors_runtime::{test_utils::*, BatchReturn, BatchReturnGen}; use fil_actors_runtime::{ ActorDowncast, ActorError, Array, DealWeight, MessageAccumulator, BURNT_FUNDS_ACTOR_ADDR, - CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_amt::Amt; use fvm_shared::bigint::Zero; @@ -1436,7 +1436,7 @@ impl ActorHarness { expect_success: Option, ) { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); self.expect_query_network_info(rt); @@ -1876,7 +1876,7 @@ impl ActorHarness { from: Address, fault: Option, ) -> Result<(), ActorError> { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, from); let params = ReportConsensusFaultParams { header1: vec![], header2: vec![], header_extra: vec![] }; diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 8bd64b74a..c31d051b4 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -13,7 +13,7 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize_vec; -use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; +use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, restrict_internal_api, ActorContext, ActorError, AsActorError, Map, INIT_ACTOR_ADDR, @@ -132,7 +132,7 @@ impl Actor { rt: &mut impl Runtime, params: ProposeParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let proposer: Address = rt.message().caller(); if params.value.is_negative() { @@ -185,7 +185,7 @@ impl Actor { rt: &mut impl Runtime, params: TxnIDParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let approver: Address = rt.message().caller(); let id = params.id; @@ -217,7 +217,7 @@ impl Actor { /// Multisig actor cancel function pub fn cancel(rt: &mut impl Runtime, params: TxnIDParams) -> Result<(), ActorError> { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let caller_addr: Address = rt.message().caller(); rt.transaction(|st: &mut State, rt| { diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 91957f71b..d9b2b1084 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -6,12 +6,11 @@ use fil_actor_multisig::{ use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; -use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -759,8 +758,8 @@ fn test_propose_restricted_correctly() { let h = util::ActorHarness::new(); let anne = Address::new_id(101); + // We will treat Bob as having code CID b"102" let bob = Address::new_id(102); - // We will treat Chuck as having code CID b"103" let chuck = Address::new_id(103); let no_unlock_duration = 0; let start_epoch = 0; @@ -770,7 +769,7 @@ fn test_propose_restricted_correctly() { h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"103"), Address::new_id(103)); + rt.set_caller(make_identity_cid(b"102"), Address::new_id(102)); let propose_params = serialize( &ProposeParams { to: chuck, @@ -793,13 +792,8 @@ fn test_propose_restricted_correctly() { rt.verify(); // can call the exported method num - rt.expect_validate_caller_type([Type::Account, Type::Multisig].to_vec()); - // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "forbidden, allowed: [Account, Multisig]", - rt.call::(Method::ProposeExported as u64, &propose_params), - ); + rt.expect_validate_caller_any(); + rt.call::(Method::ProposeExported as u64, &propose_params).unwrap(); rt.verify(); } @@ -1605,7 +1599,7 @@ mod approval_tests { RawBytes::default(), ExitCode::OK, ); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let params = TxnIDParams { id: TxnID(0), proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .unwrap(); @@ -1667,7 +1661,7 @@ mod approval_tests { h.construct_and_verify(&mut rt, 1, 0, 0, signers); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, bob); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let params = TxnIDParams { id: dne_tx_id, proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .expect_err("should fail on approve of non existent tx id"); diff --git a/actors/multisig/tests/util.rs b/actors/multisig/tests/util.rs index fdb96a94f..0dd0f8c4e 100644 --- a/actors/multisig/tests/util.rs +++ b/actors/multisig/tests/util.rs @@ -5,8 +5,8 @@ use fil_actor_multisig::{ }; use fil_actor_multisig::{ChangeNumApprovalsThresholdParams, LockBalanceParams}; use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::INIT_ACTOR_ADDR; use fil_actors_runtime::{make_map_with_root, ActorError}; -use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; @@ -125,7 +125,7 @@ impl ActorHarness { method: MethodNum, params: RawBytes, ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let propose_params = ProposeParams { to, value, method, params }; let ret = rt.call::(Method::Propose as u64, &RawBytes::serialize(propose_params).unwrap()); @@ -139,7 +139,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let approve_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = @@ -154,7 +154,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let cancel_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 43437812c..9d5552094 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -87,7 +87,7 @@ impl Actor { rt: &mut impl Runtime, params: CreateMinerParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let value = rt.message().value_received(); let constructor_params = RawBytes::serialize(ext::miner::MinerConstructorParams { diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index dd53f55fc..b23661098 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -13,7 +13,6 @@ use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fil_actors_runtime::CRON_ACTOR_ADDR; use fil_actors_runtime::REWARD_ACTOR_ADDR; use fvm_ipld_blockstore::Blockstore; @@ -142,7 +141,7 @@ impl Harness { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *owner); rt.set_value(value.clone()); rt.set_balance(value.clone()); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let miner_ctor_params = MinerConstructorParams { owner: *owner, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 5be231f36..fba2a7359 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -5,7 +5,7 @@ use fil_actors_runtime::test_utils::{ expect_abort, expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; -use fil_actors_runtime::{runtime::Policy, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{runtime::Policy, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, RawBytes}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntSer; @@ -77,34 +77,6 @@ fn create_miner() { h.check_state(&rt); } -#[test] -fn create_miner_given_caller_is_not_of_signable_type_should_fail() { - let (h, mut rt) = setup(); - - let peer = "miner".as_bytes().to_vec(); - let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; - - let create_miner_params = CreateMinerParams { - owner: *OWNER, - worker: *OWNER, - window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1, - peer, - multiaddrs, - }; - - rt.set_caller(*MINER_ACTOR_CODE_ID, *OWNER); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - expect_abort( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::CreateMiner as u64, - &RawBytes::serialize(&create_miner_params).unwrap(), - ), - ); - rt.verify(); - h.check_state(&rt); -} - #[test] fn create_miner_given_send_to_init_actor_fails_should_fail() { let (h, mut rt) = setup(); @@ -124,7 +96,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *OWNER); rt.value_received = TokenAmount::from_atto(10); rt.set_balance(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let message_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 2208a45bb..c4a338d59 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -6,16 +6,10 @@ use fvm_shared::address::Address; use fvm_shared::METHOD_SEND; use fvm_shared::{ActorID, MethodNum}; -use crate::runtime::builtins::Type; use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// Types of built-in actors that can be treated as principals. -/// This distinction is legacy and should be removed prior to FVM support for -/// user-programmable actors. -pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; - /// ResolveToActorID resolves the given address to it's actor ID. /// If an actor ID for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. From 30e167a8ca102a4b07bcd8e55eb6f45b37d2ed8b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Nov 2022 19:07:10 -0500 Subject: [PATCH 135/339] Market actor: Minor improvements to two exported getters (#826) * Market actor: GetDealTermExported: Return (start_epoch, duration) * Market actor: Export getter for deal total price --- actors/market/src/lib.rs | 19 +++++++++---------- actors/market/src/types.rs | 11 ++++++----- actors/market/tests/deal_api_test.rs | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 287e6c239..83bab6596 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -81,7 +81,7 @@ pub enum Method { GetDealProviderExported = frc42_dispatch::method_hash!("GetDealProvider"), GetDealLabelExported = frc42_dispatch::method_hash!("GetDealLabel"), GetDealTermExported = frc42_dispatch::method_hash!("GetDealTerm"), - GetDealEpochPriceExported = frc42_dispatch::method_hash!("GetDealEpochPrice"), + GetDealTotalPriceExported = frc42_dispatch::method_hash!("GetDealTotalPrice"), GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), @@ -1097,25 +1097,24 @@ impl Actor { Ok(GetDealLabelReturn { label: found.label }) } - /// Returns the start and end epochs of a deal proposal. - /// The deal term is a half-open range, exclusive of the end epoch. + /// Returns the start epoch and duration (in epochs) of a deal proposal. fn get_deal_term( rt: &mut impl Runtime, params: GetDealTermParams, ) -> Result { rt.validate_immediate_caller_accept_any()?; let found = rt.state::()?.get_proposal(rt.store(), params.id)?; - Ok(GetDealTermReturn { start: found.start_epoch, end: found.end_epoch }) + Ok(GetDealTermReturn { start: found.start_epoch, duration: found.duration() }) } /// Returns the per-epoch price of a deal proposal. - fn get_deal_epoch_price( + fn get_deal_total_price( rt: &mut impl Runtime, - params: GetDealEpochPriceParams, - ) -> Result { + params: GetDealTotalPriceParams, + ) -> Result { rt.validate_immediate_caller_accept_any()?; let found = rt.state::()?.get_proposal(rt.store(), params.id)?; - Ok(GetDealEpochPriceReturn { price_per_epoch: found.storage_price_per_epoch }) + Ok(GetDealTotalPriceReturn { total_price: found.total_storage_fee() }) } /// Returns the client collateral requirement for a deal proposal. @@ -1584,8 +1583,8 @@ impl ActorCode for Actor { let res = Self::get_deal_term(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::GetDealEpochPriceExported) => { - let res = Self::get_deal_epoch_price(rt, cbor::deserialize_params(params)?)?; + Some(Method::GetDealTotalPriceExported) => { + let res = Self::get_deal_total_price(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } Some(Method::GetDealClientCollateralExported) => { diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index afdfa2da4..7c28f164b 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -175,14 +175,15 @@ pub struct GetDealLabelReturn { pub type GetDealTermParams = DealQueryParams; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct GetDealTermReturn { - pub start: ChainEpoch, // First epoch for the deal (inclusive) - pub end: ChainEpoch, // Epoch at which the deal expires (i.e. exclusive). + pub start: ChainEpoch, // First epoch for the deal (inclusive) + pub duration: ChainEpoch, // Duration of the deal. } -pub type GetDealEpochPriceParams = DealQueryParams; +pub type GetDealTotalPriceParams = DealQueryParams; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] -pub struct GetDealEpochPriceReturn { - pub price_per_epoch: TokenAmount, +#[serde(transparent)] +pub struct GetDealTotalPriceReturn { + pub total_price: TokenAmount, } pub type GetDealClientCollateralParams = DealQueryParams; diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs index 4471f0f43..ca18ed999 100644 --- a/actors/market/tests/deal_api_test.rs +++ b/actors/market/tests/deal_api_test.rs @@ -4,9 +4,9 @@ use serde::de::DeserializeOwned; use fil_actor_market::{ Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, - GetDealDataCommitmentReturn, GetDealEpochPriceReturn, GetDealLabelReturn, - GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, - GetDealVerifiedReturn, Method, + GetDealDataCommitmentReturn, GetDealLabelReturn, GetDealProviderCollateralReturn, + GetDealProviderReturn, GetDealTermReturn, GetDealTotalPriceReturn, GetDealVerifiedReturn, + Method, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; @@ -52,10 +52,10 @@ fn proposal_data() { let term: GetDealTermReturn = query_deal(&mut rt, Method::GetDealTermExported, id); assert_eq!(proposal.start_epoch, term.start); - assert_eq!(proposal.end_epoch, term.end); + assert_eq!(proposal.duration(), term.duration); - let price: GetDealEpochPriceReturn = query_deal(&mut rt, Method::GetDealEpochPriceExported, id); - assert_eq!(proposal.storage_price_per_epoch, price.price_per_epoch); + let price: GetDealTotalPriceReturn = query_deal(&mut rt, Method::GetDealTotalPriceExported, id); + assert_eq!(proposal.total_storage_fee(), price.total_price); let client_collateral: GetDealClientCollateralReturn = query_deal(&mut rt, Method::GetDealClientCollateralExported, id); From 4673ef25865f38cdb0d3fc8861039ae35f5a19e7 Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:22:39 +1100 Subject: [PATCH 136/339] Exported API for market deal activation state (#819) --- actors/market/src/lib.rs | 55 ++++++++++++++++ actors/market/src/state.rs | 35 ++++++++-- actors/market/src/types.rs | 10 +++ actors/market/tests/deal_api_test.rs | 95 +++++++++++++++++++++++++--- runtime/src/actor_error.rs | 2 +- 5 files changed, 181 insertions(+), 16 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 83bab6596..9ba13ad19 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -59,6 +59,9 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub const NO_ALLOCATION_ID: u64 = 0; +// An exit code indicating that information about a past deal is no longer available. +pub const EX_DEAL_EXPIRED: ExitCode = ExitCode::new(32); + /// Market actor methods available #[derive(FromPrimitive)] #[repr(u64)] @@ -85,6 +88,7 @@ pub enum Method { GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), + GetDealActivationExported = frc42_dispatch::method_hash!("GetDealActivation"), } /// Market Actor @@ -1148,6 +1152,53 @@ impl Actor { let found = rt.state::()?.get_proposal(rt.store(), params.id)?; Ok(GetDealVerifiedReturn { verified: found.verified_deal }) } + + /// Fetches activation state for a deal. + /// This will be available from when the proposal is published until an undefined period after + /// the deal finishes (either normally or by termination). + /// Returns USR_NOT_FOUND if the deal doesn't exist (yet), or EX_DEAL_EXPIRED if the deal + /// has been removed from state. + fn get_deal_activation( + rt: &mut impl Runtime, + params: GetDealActivationParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st = rt.state::()?; + let found = st.find_deal_state(rt.store(), params.id)?; + match found { + Some(state) => Ok(GetDealActivationReturn { + // If we have state, the deal has been activated. + // It may also have completed normally, or been terminated, + // but not yet been cleaned up. + activated: state.sector_start_epoch, + terminated: state.slash_epoch, + }), + None => { + // State::get_proposal will fail with USR_NOT_FOUND in either case. + let maybe_proposal = st.find_proposal(rt.store(), params.id)?; + match maybe_proposal { + Some(_) => Ok(GetDealActivationReturn { + // The proposal has been published, but not activated. + activated: EPOCH_UNDEFINED, + terminated: EPOCH_UNDEFINED, + }), + None => { + if params.id < st.next_id { + // If the deal ID has been used, it must have been cleaned up. + Err(ActorError::unchecked( + EX_DEAL_EXPIRED, + format!("deal {} expired", params.id), + )) + } else { + // We can't distinguish between failing to activate, or having been + // cleaned up after completion/termination. + Err(ActorError::not_found(format!("no such deal {}", params.id))) + } + } + } + } + } + } } fn compute_data_commitment( @@ -1600,6 +1651,10 @@ impl ActorCode for Actor { let res = Self::get_deal_verified(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetDealActivationExported) => { + let res = Self::get_deal_activation(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/state.rs b/actors/market/src/state.rs index 66c783066..bf3fea656 100644 --- a/actors/market/src/state.rs +++ b/actors/market/src/state.rs @@ -120,15 +120,36 @@ impl State { store: &BS, id: DealID, ) -> Result { + let found = self + .find_proposal(store, id)? + .with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?; + Ok(found) + } + + pub fn find_proposal( + &self, + store: &BS, + id: DealID, + ) -> Result, ActorError> { let proposals = DealArray::load(&self.proposals, store) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals")?; - let found = proposals - .get(id) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to load deal proposal {}", id) - })? - .with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?; - Ok(found.clone()) + let maybe = proposals.get(id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load deal proposal {}", id) + })?; + Ok(maybe.cloned()) + } + + pub fn find_deal_state( + &self, + store: &BS, + id: DealID, + ) -> Result, ActorError> { + let states = DealMetaArray::load(&self.states, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal states")?; + let found = states.get(id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load deal state {}", id) + })?; + Ok(found.cloned()) } pub(super) fn mutator<'bs, BS: Blockstore>( diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index 7c28f164b..eaa98b970 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -206,3 +206,13 @@ pub type GetDealVerifiedParams = DealQueryParams; pub struct GetDealVerifiedReturn { pub verified: bool, } + +pub type GetDealActivationParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealActivationReturn { + /// Epoch at which the deal was activated, or -1. + /// This may be before the proposed start epoch. + pub activated: ChainEpoch, + /// Epoch at which the deal was terminated abnormally, or -1. + pub terminated: ChainEpoch, +} diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs index ca18ed999..91584188b 100644 --- a/actors/market/tests/deal_api_test.rs +++ b/actors/market/tests/deal_api_test.rs @@ -1,15 +1,22 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; +use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; use serde::de::DeserializeOwned; use fil_actor_market::{ - Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, - GetDealDataCommitmentReturn, GetDealLabelReturn, GetDealProviderCollateralReturn, - GetDealProviderReturn, GetDealTermReturn, GetDealTotalPriceReturn, GetDealVerifiedReturn, - Method, + Actor as MarketActor, DealQueryParams, GetDealActivationReturn, GetDealClientCollateralReturn, + GetDealClientReturn, GetDealDataCommitmentReturn, GetDealLabelReturn, + GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, + GetDealTotalPriceReturn, GetDealVerifiedReturn, Method, EX_DEAL_EXPIRED, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; -use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; +use fil_actors_runtime::runtime::policy_constants::DEAL_UPDATES_INTERVAL; +use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, MockRuntime, ACCOUNT_ACTOR_CODE_ID, +}; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use harness::*; mod harness; @@ -71,11 +78,83 @@ fn proposal_data() { check_state(&rt); } +#[test] +fn activation() { + let start_epoch = 10; + let end_epoch = start_epoch + 180 * EPOCHS_IN_DAY; + let publish_epoch = ChainEpoch::from(1); + + let mut rt = setup(); + rt.set_epoch(publish_epoch); + let next_allocation_id = 1; + + let proposal = generate_deal_and_add_funds( + &mut rt, + CLIENT_ADDR, + &MinerAddresses::default(), + start_epoch, + end_epoch, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + let id = + publish_deals(&mut rt, &MinerAddresses::default(), &[proposal.clone()], next_allocation_id) + [0]; + + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(-1, activation.activated); + assert_eq!(-1, activation.terminated); + + // activate the deal + let activate_epoch = start_epoch - 2; + rt.set_epoch(activate_epoch); + activate_deals(&mut rt, end_epoch + 1, PROVIDER_ADDR, activate_epoch, &[id]); + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(activate_epoch, activation.activated); + assert_eq!(-1, activation.terminated); + + // terminate early + let terminate_epoch = activate_epoch + 100; + rt.set_epoch(terminate_epoch); + terminate_deals(&mut rt, PROVIDER_ADDR, &[id]); + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(activate_epoch, activation.activated); + assert_eq!(terminate_epoch, activation.terminated); + + // Clean up state + let clean_epoch = terminate_epoch + DEAL_UPDATES_INTERVAL; + rt.set_epoch(clean_epoch); + rt.expect_send( + BURNT_FUNDS_ACTOR_ADDR, + METHOD_SEND, + RawBytes::default(), + proposal.provider_collateral, + RawBytes::default(), + ExitCode::OK, + ); + cron_tick(&mut rt); + expect_abort_contains_message( + EX_DEAL_EXPIRED, + "expired", + query_deal_raw(&mut rt, Method::GetDealActivationExported, id), + ); + + // Non-existent deal is NOT FOUND + expect_abort_contains_message( + ExitCode::USR_NOT_FOUND, + "no such deal", + query_deal_raw(&mut rt, Method::GetDealActivationExported, id + 1), + ); +} + fn query_deal(rt: &mut MockRuntime, method: Method, id: u64) -> T { + query_deal_raw(rt, method, id).unwrap().deserialize().unwrap() +} + +fn query_deal_raw(rt: &mut MockRuntime, method: Method, id: u64) -> Result { let params = DealQueryParams { id }; rt.expect_validate_caller_any(); rt.call::(method as u64, &RawBytes::serialize(params).unwrap()) - .unwrap() - .deserialize() - .unwrap() } diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 854711e84..7df13c753 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -9,7 +9,7 @@ use thiserror::Error; #[error("ActorError(exit_code: {exit_code:?}, msg: {msg})")] pub struct ActorError { /// The exit code for this invocation. - /// Codes less than `FIRST_ACTOR_EXIT_CODE` are prohibited and will be overwritten by the VM. + /// Codes less than `FIRST_USER_EXIT_CODE` are prohibited and will be overwritten by the VM. exit_code: ExitCode, /// Message for debugging purposes, msg: String, From 382ab4cda9993f84e1591fe2e59abbe65244f734 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 16 Nov 2022 17:21:11 -0500 Subject: [PATCH 137/339] Paych actor: Drop account req, use AuthenticateMessage to verify sigs (#824) * Paych actor: Drop account req, use AuthenticateMessage to verify sigs * Address review * Address review --- Cargo.lock | 1 + actors/paych/Cargo.toml | 1 + actors/paych/src/ext.rs | 17 +++ actors/paych/src/lib.rs | 60 ++++---- actors/paych/src/state.rs | 2 + actors/paych/tests/paych_actor_test.rs | 196 +++++++------------------ runtime/src/builtin/shared.rs | 4 +- 7 files changed, 109 insertions(+), 172 deletions(-) create mode 100644 actors/paych/src/ext.rs diff --git a/Cargo.lock b/Cargo.lock index bfe392041..29bef758e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1634,6 +1634,7 @@ dependencies = [ "cid", "derive_builder", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_blockstore", "fvm_ipld_encoding", diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 3f5777b6d..7f43948be 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -16,6 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +frc42_dispatch = "1.0.0" num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/paych/src/ext.rs b/actors/paych/src/ext.rs new file mode 100644 index 000000000..cea045d3d --- /dev/null +++ b/actors/paych/src/ext.rs @@ -0,0 +1,17 @@ +use fvm_ipld_encoding::serde_bytes; +use fvm_ipld_encoding::tuple::*; + +pub mod account { + use super::*; + + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); + + #[derive(Serialize_tuple, Deserialize_tuple)] + pub struct AuthenticateMessageParams { + #[serde(with = "serde_bytes")] + pub signature: Vec, + #[serde(with = "serde_bytes")] + pub message: Vec, + } +} diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 75d9a5091..2ee125557 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -4,7 +4,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, Array, + actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, + Array, AsActorError, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -22,6 +23,7 @@ pub use self::types::*; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); +pub mod ext; mod state; pub mod testing; mod types; @@ -42,6 +44,7 @@ pub const ERR_CHANNEL_STATE_UPDATE_AFTER_SETTLED: ExitCode = ExitCode::new(32); /// Payment Channel actor pub struct Actor; + impl Actor { /// Constructor for Payment channel actor pub fn constructor(rt: &mut impl Runtime, params: ConstructorParams) -> Result<(), ActorError> { @@ -49,10 +52,16 @@ impl Actor { // behalf of the payer/payee. rt.validate_immediate_caller_type(std::iter::once(&Type::Init))?; - // Check both parties are capable of signing vouchers - let to = Self::resolve_account(rt, ¶ms.to)?; + // Resolve both parties, confirming they exist in the state tree. + let to = Self::resolve_address(rt, ¶ms.to) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("to address not found {}", params.to) + })?; - let from = Self::resolve_account(rt, ¶ms.from)?; + let from = Self::resolve_address(rt, ¶ms.from) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("to address not found {}", params.to) + })?; let empty_arr_cid = Array::<(), _>::new_with_bit_width(rt.store(), LANE_STATES_AMT_BITWIDTH) @@ -65,26 +74,14 @@ impl Actor { Ok(()) } - /// Resolves an address to a canonical ID address and requires it to address an account actor. - fn resolve_account(rt: &mut impl Runtime, raw: &Address) -> Result { + /// Resolves an address to a canonical ID address and confirms it exists in the state tree. + fn resolve_address(rt: &mut impl Runtime, raw: &Address) -> Result { let resolved = resolve_to_actor_id(rt, raw)?; - let code_cid = rt - .get_actor_code_cid(&resolved) - .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", resolved))?; - - let typ = rt.resolve_builtin_actor_type(&code_cid); - if typ != Some(Type::Account) { - Err(actor_error!( - forbidden, - "actor {} must be an account, was {} ({:?})", - raw, - code_cid, - typ - )) - } else { - Ok(Address::new_id(resolved)) - } + // so long as we can find code for this, return `resolved` + rt.get_actor_code_cid(&resolved) + .map(|_| Address::new_id(resolved)) + .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", resolved)) } pub fn update_channel_state( @@ -98,10 +95,11 @@ impl Actor { let sv = params.sv; // Pull signature from signed voucher - let sig = sv + let sig = &sv .signature .as_ref() - .ok_or_else(|| actor_error!(illegal_argument, "voucher has no signature"))?; + .ok_or_else(|| actor_error!(illegal_argument, "voucher has no signature"))? + .bytes; if st.settling_at != 0 && rt.curr_epoch() >= st.settling_at { return Err(ActorError::unchecked( @@ -120,9 +118,17 @@ impl Actor { })?; // Validate signature - rt.verify_signature(sig, &signer, &sv_bz).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "voucher signature invalid") - })?; + + rt.send( + &signer, + ext::account::AUTHENTICATE_MESSAGE_METHOD, + RawBytes::serialize(ext::account::AuthenticateMessageParams { + signature: sig.to_vec(), + message: sv_bz, + })?, + TokenAmount::zero(), + ) + .map_err(|e| e.wrap("voucher sig authentication failed"))?; let pch_addr = rt.message().receiver(); let svpch_id = rt.resolve_address(&sv.channel_addr).ok_or_else(|| { diff --git a/actors/paych/src/state.rs b/actors/paych/src/state.rs index 8692940cd..5c892c977 100644 --- a/actors/paych/src/state.rs +++ b/actors/paych/src/state.rs @@ -57,5 +57,7 @@ pub struct Merge { } impl Cbor for State {} + impl Cbor for LaneState {} + impl Cbor for Merge {} diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index 2250b7114..59cd0cc4f 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -4,14 +4,15 @@ use std::cell::RefCell; use std::collections::HashMap; -use anyhow::anyhow; use cid::Cid; use derive_builder::Builder; +use fil_actor_paych::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD}; use fil_actor_paych::testing::check_state_invariants; use fil_actor_paych::{ Actor as PaychActor, ConstructorParams, LaneState, Merge, Method, ModVerifyParams, SignedVoucher, State as PState, UpdateChannelStateParams, MAX_LANE, SETTLE_DELAY, }; + use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; @@ -69,15 +70,12 @@ fn check_state(rt: &MockRuntime) { mod paych_constructor { use fil_actors_runtime::runtime::builtins::Type; - use fvm_shared::METHOD_CONSTRUCTOR; - use fvm_shared::METHOD_SEND; - use num_traits::Zero; + use fvm_shared::{METHOD_CONSTRUCTOR, METHOD_SEND}; use super::*; const TEST_PAYCH_ADDR: u64 = 100; const TEST_PAYER_ADDR: u64 = 101; - const TEST_PAYEE_ADDR: u64 = 102; const TEST_CALLER_ADDR: u64 = 102; fn construct_runtime() -> MockRuntime { @@ -112,7 +110,7 @@ mod paych_constructor { rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), - from: Address::new_id(TEST_PAYER_ADDR), + from: Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(), }; expect_abort( &mut rt, @@ -140,65 +138,6 @@ mod paych_constructor { check_state(&rt); } - #[test] - fn actor_constructor_fails() { - let paych_addr = Address::new_id(TEST_PAYCH_ADDR); - let payer_addr = Address::new_id(TEST_PAYER_ADDR); - let payee_addr = Address::new_id(TEST_PAYEE_ADDR); - let caller_addr = Address::new_id(TEST_CALLER_ADDR); - - struct TestCase { - from_code: Cid, - from_addr: Address, - to_code: Cid, - to_addr: Address, - expected_exit_code: ExitCode, - } - - let test_cases: Vec = vec![ - // fails if target (to) is not account actor - TestCase { - from_code: *ACCOUNT_ACTOR_CODE_ID, - from_addr: payer_addr, - to_code: *MULTISIG_ACTOR_CODE_ID, - to_addr: payee_addr, - expected_exit_code: ExitCode::USR_FORBIDDEN, - }, - // fails if sender (from) is not account actor - TestCase { - from_code: *MULTISIG_ACTOR_CODE_ID, - from_addr: payer_addr, - to_code: *ACCOUNT_ACTOR_CODE_ID, - to_addr: payee_addr, - expected_exit_code: ExitCode::USR_FORBIDDEN, - }, - ]; - - for test_case in test_cases { - let mut actor_code_cids = HashMap::default(); - actor_code_cids.insert(paych_addr, *PAYCH_ACTOR_CODE_ID); - actor_code_cids.insert(test_case.to_addr, test_case.to_code); - actor_code_cids.insert(test_case.from_addr, test_case.from_code); - - let mut rt = MockRuntime { - receiver: paych_addr, - caller: caller_addr, - caller_type: *INIT_ACTOR_CODE_ID, - actor_code_cids, - ..Default::default() - }; - - rt.expect_validate_caller_type(vec![Type::Init]); - let params = ConstructorParams { to: test_case.to_addr, from: test_case.from_addr }; - expect_abort( - &mut rt, - METHOD_CONSTRUCTOR, - &RawBytes::serialize(params).unwrap(), - test_case.expected_exit_code, - ); - } - } - #[test] fn sendr_addr_not_resolvable_to_id_addr() { const TO_ADDR: u64 = 101; @@ -447,17 +386,15 @@ mod create_lane_tests { rt.expect_validate_caller_addr(vec![payer_addr, payee_addr]); if test_case.sig.is_some() && test_case.secret_preimage.is_empty() { - let exp_exit_code = - if !test_case.verify_sig { Err(anyhow!("bad signature")) } else { Ok(()) }; - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: exp_exit_code, - }); + let exp_exit_code = if !test_case.verify_sig { + ExitCode::USR_ILLEGAL_ARGUMENT + } else { + ExitCode::OK + }; + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), exp_exit_code); } - if test_case.exp_exit_code == ExitCode::OK { + if test_case.exp_exit_code.is_success() { call( &mut rt, Method::UpdateChannelState as u64, @@ -502,12 +439,7 @@ mod update_channel_state_redeem { let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -545,12 +477,7 @@ mod update_channel_state_redeem { sv.nonce = ls_to_update.nonce + 1; let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -585,12 +512,7 @@ mod update_channel_state_redeem { let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); expect_abort( &mut rt, @@ -617,12 +539,8 @@ mod merge_tests { fn failure_end(rt: &mut MockRuntime, sv: SignedVoucher, exp_exit_code: ExitCode) { let payee_addr = Address::new_id(PAYEE_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(rt, payee_addr, sv.clone(), ExitCode::OK); + expect_abort( rt, Method::UpdateChannelState as u64, @@ -645,12 +563,7 @@ mod merge_tests { sv.merges = vec![Merge { lane: 1, nonce: merge_nonce }]; let payee_addr = Address::new_id(PAYEE_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payee_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -770,12 +683,7 @@ mod update_channel_state_extra { method: Method::UpdateChannelState as u64, data: fake_params.clone(), }); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv.clone(), ExitCode::OK); rt.expect_send( other_addr, @@ -848,16 +756,13 @@ fn update_channel_settling() { }, ]; - let mut ucp = UpdateChannelStateParams::from(sv.clone()); + let mut ucp = UpdateChannelStateParams::from(sv); for tc in test_cases { ucp.sv.min_settle_height = tc.min_settle; rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: ucp.sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(&mut rt, state.to, ucp.sv.clone(), ExitCode::OK); + call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(&ucp).unwrap()); let new_state: PState = rt.get_state(); assert_eq!(tc.exp_settling_at, new_state.settling_at); @@ -878,12 +783,7 @@ mod secret_preimage { let ucp = UpdateChannelStateParams::from(sv.clone()); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv, ExitCode::OK); call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(ucp).unwrap()); @@ -897,18 +797,15 @@ mod secret_preimage { let state: PState = rt.get_state(); - let mut ucp = UpdateChannelStateParams { secret: b"Profesr".to_vec(), sv: sv.clone() }; + let mut ucp = UpdateChannelStateParams { secret: b"Profesr".to_vec(), sv }; let mut mag = b"Magneto".to_vec(); mag.append(&mut vec![0; 25]); ucp.sv.secret_pre_image = mag; rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.signature.unwrap(), - signer: state.to, - plaintext: ucp.sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(&mut rt, state.to, ucp.sv.clone(), ExitCode::OK); + expect_abort( &mut rt, Method::UpdateChannelState as u64, @@ -971,12 +868,8 @@ mod actor_settle { let ucp = UpdateChannelStateParams::from(sv.clone()); rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: ucp.sv.signature.clone().unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv, ExitCode::OK); + call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(&ucp).unwrap()); state = rt.get_state(); @@ -1177,7 +1070,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche lane: param.lane, nonce: param.nonce, amount: param.amt.clone(), - signature: Some(sig.clone()), + signature: Some(sig), secret_pre_image: Default::default(), channel_addr: Address::new_id(PAYCH_ID), extra: Default::default(), @@ -1186,12 +1079,9 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, param.from); rt.expect_validate_caller_addr(vec![param.from, param.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig, - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(rt, payee_addr, sv.clone(), ExitCode::OK); + call( rt, Method::UpdateChannelState as u64, @@ -1241,3 +1131,23 @@ fn assert_lane_states_length(rt: &MockRuntime, cid: &Cid, l: u64) { let arr = Amt::::load(cid, &rt.store).unwrap(); assert_eq!(arr.count(), l); } + +fn expect_authenticate_message( + rt: &mut MockRuntime, + payer_addr: Address, + sv: SignedVoucher, + exp_exit_code: ExitCode, +) { + rt.expect_send( + payer_addr, + AUTHENTICATE_MESSAGE_METHOD, + RawBytes::serialize(AuthenticateMessageParams { + signature: sv.clone().signature.unwrap().bytes, + message: sv.signing_bytes().unwrap(), + }) + .unwrap(), + TokenAmount::zero(), + RawBytes::default(), + exp_exit_code, + ) +} diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index c4a338d59..4293c75a9 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -10,8 +10,8 @@ use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// ResolveToActorID resolves the given address to it's actor ID. -/// If an actor ID for the given address dosen't exist yet, it tries to create one by sending +/// ResolveToActorID resolves the given address to its actor ID. +/// If an actor ID for the given address doesn't exist yet, it tries to create one by sending /// a zero balance to the given address. pub fn resolve_to_actor_id( rt: &mut impl Runtime, From 3e02530b5b522a86610040bc3e4be368bfe41587 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 17 Nov 2022 13:41:00 -0500 Subject: [PATCH 138/339] Account actor: Deprecate AuthenticateMessage (#856) --- actors/account/src/lib.rs | 5 +++-- actors/account/tests/account_actor_test.rs | 8 -------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index 2f27a5208..0f23d9944 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -32,7 +32,8 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, PubkeyAddress = 2, - AuthenticateMessage = 3, + // Deprecated in v10 + // AuthenticateMessage = 3, AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -121,7 +122,7 @@ impl ActorCode for Actor { let addr = Self::pubkey_address(rt)?; Ok(RawBytes::serialize(addr)?) } - Some(Method::AuthenticateMessage) | Some(Method::AuthenticateMessageExported) => { + Some(Method::AuthenticateMessageExported) => { Self::authenticate_message(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index de2792177..c1a631314 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -122,14 +122,6 @@ fn authenticate_message() { ); rt.verify(); - // Invalid caller of internal method number - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), - ); - // Ok to call exported method number rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { From 7035d111edef65b77442e42b33bdf69b2b038974 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 19 Nov 2022 15:26:00 -0500 Subject: [PATCH 139/339] Market actor: Export PublishStorageDeals (#857) --- actors/market/src/lib.rs | 3 +- actors/market/tests/market_actor_test.rs | 92 +++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 9ba13ad19..a050d3439 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -78,6 +78,7 @@ pub enum Method { // Method numbers derived from FRC-0042 standards AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + PublishStorageDealsExported = frc42_dispatch::method_hash!("PublishStorageDeals"), GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), GetDealDataCommitmentExported = frc42_dispatch::method_hash!("GetDealDataCommitment"), GetDealClientExported = frc42_dispatch::method_hash!("GetDealClient"), @@ -1586,7 +1587,7 @@ impl ActorCode for Actor { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::PublishStorageDeals) => { + Some(Method::PublishStorageDeals) | Some(Method::PublishStorageDealsExported) => { let res = Self::publish_storage_deals(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index ce6212a49..be3ebd637 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -1953,7 +1953,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(1000)); + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); // cannot call the unexported method num expect_abort_contains_message( @@ -1975,3 +1975,93 @@ fn add_balance_restricted_correctly() { rt.verify(); } + +#[test] +fn psd_restricted_correctly() { + let mut rt = setup(); + + let deal = generate_deal_proposal( + CLIENT_ADDR, + PROVIDER_ADDR, + ChainEpoch::from(1), + 200 * EPOCHS_IN_DAY, + ); + + // Client gets enough funds + add_participant_funds(&mut rt, CLIENT_ADDR, deal.client_balance_requirement()); + + // Provider has enough funds + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); + rt.set_value(deal.provider_balance_requirement().clone()); + rt.expect_validate_caller_any(); + expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); + + assert_eq!( + RawBytes::default(), + rt.call::( + Method::AddBalance as u64, + &RawBytes::serialize(PROVIDER_ADDR).unwrap(), + ) + .unwrap() + ); + + rt.verify(); + + // Prep the message + + let buf = RawBytes::serialize(&deal).expect("failed to marshal deal proposal"); + + let sig = Signature::new_bls(buf.to_vec()); + + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal.clone(), client_signature: sig }], + }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), WORKER_ADDR); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::PublishStorageDeals as MethodNum, + &RawBytes::serialize(params.clone()).unwrap(), + ), + ); + + // can call the exported method num + + let authenticate_param1 = RawBytes::serialize(AuthenticateMessageParams { + signature: buf.to_vec(), + message: buf.to_vec(), + }) + .unwrap(); + + rt.expect_validate_caller_any(); + expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); + expect_query_network_info(&mut rt); + + rt.expect_send( + deal.client, + AUTHENTICATE_MESSAGE_METHOD as u64, + authenticate_param1, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + let ret: PublishStorageDealsReturn = rt + .call::( + Method::PublishStorageDealsExported as MethodNum, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert!(ret.valid_deals.get(0)); + + rt.verify(); + check_state(&rt); +} From b790646cfb0f2f3df2247408be7276ef55319d16 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Nov 2022 19:09:41 -0500 Subject: [PATCH 140/339] Power: Derive Eq for power types --- actors/power/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 8e26ab4b0..3df018c18 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -24,7 +24,7 @@ pub const CRON_QUEUE_HAMT_BITWIDTH: u32 = 6; pub const CRON_QUEUE_AMT_BITWIDTH: u32 = 6; pub const PROOF_VALIDATION_BATCH_AMT_BITWIDTH: u32 = 4; -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct CreateMinerParams { pub owner: Address, pub worker: Address, @@ -58,7 +58,7 @@ pub struct EnrollCronEventParams { pub payload: RawBytes, } -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct CurrentTotalPowerReturn { #[serde(with = "bigint_ser")] pub raw_byte_power: StoragePower, From f49676026d55c786d4501e872fa844d4f79fc06f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 22 Nov 2022 13:00:28 -0500 Subject: [PATCH 141/339] Miner: Export several more methods (#863) * Miner: Export ChangeWorkerAddress * Miner: Export ChangePeerID * Miner: Export WithdrawBalance * Miner: Export ChangeMultiaddrs * Miner: Export ConfirmUpdateWorkerKey * Miner: Export RepayDebt * Miner: Export ChangeOwnerAddress * Miner: Add exported getters for PeerID & multiaddrs * Miner: Refactor: Rename ConfirmUpdateWorkerKey to ConfirmChangeWorkerAddress --- actors/miner/src/lib.rs | 52 +++++++++--- actors/miner/src/types.rs | 11 +++ .../miner/tests/change_owner_address_test.rs | 46 ++++++++++- actors/miner/tests/change_peer_id_test.rs | 46 ++++++++++- .../miner/tests/change_worker_address_test.rs | 80 ++++++++++++++++++- .../tests/confirm_update_worker_key_test.rs | 6 +- .../miner/tests/miner_actor_test_peer_info.rs | 49 +++++++++++- actors/miner/tests/repay_debts.rs | 53 +++++++++++- actors/miner/tests/util.rs | 52 +++++++----- actors/miner/tests/withdraw_balance.rs | 55 ++++++++++++- 10 files changed, 409 insertions(+), 41 deletions(-) diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index dd9404e3b..a5b6bd550 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -112,7 +112,7 @@ pub enum Method { ChangeMultiaddrs = 18, CompactPartitions = 19, CompactSectorNumbers = 20, - ConfirmUpdateWorkerKey = 21, + ConfirmChangeWorkerAddress = 21, RepayDebt = 22, ChangeOwnerAddress = 23, DisputeWindowedPoSt = 24, @@ -125,6 +125,13 @@ pub enum Method { GetBeneficiary = 31, ExtendSectorExpiration2 = 32, // Method numbers derived from FRC-0042 standards + ChangeWorkerAddressExported = frc42_dispatch::method_hash!("ChangeWorkerAddress"), + ChangePeerIDExported = frc42_dispatch::method_hash!("ChangePeerID"), + WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + ChangeMultiaddrsExported = frc42_dispatch::method_hash!("ChangeMultiaddrs"), + ConfirmChangeWorkerAddressExported = frc42_dispatch::method_hash!("ConfirmChangeWorkerAddress"), + RepayDebtExported = frc42_dispatch::method_hash!("RepayDebt"), + ChangeOwnerAddressExported = frc42_dispatch::method_hash!("ChangeOwnerAddress"), ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), GetOwnerExported = frc42_dispatch::method_hash!("GetOwner"), @@ -132,6 +139,8 @@ pub enum Method { GetSectorSizeExported = frc42_dispatch::method_hash!("GetSectorSize"), GetAvailableBalanceExported = frc42_dispatch::method_hash!("GetAvailableBalance"), GetVestingFundsExported = frc42_dispatch::method_hash!("GetVestingFunds"), + GetPeerIDExported = frc42_dispatch::method_hash!("GetPeerID"), + GetMultiaddrsExported = frc42_dispatch::method_hash!("GetMultiaddrs"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -344,7 +353,7 @@ impl Actor { } /// Triggers a worker address change if a change has been requested and its effective epoch has arrived. - fn confirm_update_worker_key(rt: &mut impl Runtime) -> Result<(), ActorError> { + fn confirm_change_worker_address(rt: &mut impl Runtime) -> Result<(), ActorError> { rt.transaction(|state: &mut State, rt| { let mut info = get_miner_info(rt.store(), state)?; @@ -413,6 +422,13 @@ impl Actor { }) } + fn get_peer_id(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let peer_id = get_miner_info(rt.store(), &state)?.peer_id; + Ok(GetPeerIDReturn { peer_id }) + } + fn change_peer_id(rt: &mut impl Runtime, params: ChangePeerIDParams) -> Result<(), ActorError> { let policy = rt.policy(); check_peer_info(policy, ¶ms.new_id, &[])?; @@ -434,6 +450,13 @@ impl Actor { Ok(()) } + fn get_multiaddresses(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let multi_addrs = get_miner_info(rt.store(), &state)?.multi_address; + Ok(GetMultiaddrsReturn { multi_addrs }) + } + fn change_multiaddresses( rt: &mut impl Runtime, params: ChangeMultiaddrsParams, @@ -4937,11 +4960,11 @@ impl ActorCode for Actor { let res = Self::control_addresses(rt)?; Ok(RawBytes::serialize(&res)?) } - Some(Method::ChangeWorkerAddress) => { + Some(Method::ChangeWorkerAddress) | Some(Method::ChangeWorkerAddressExported) => { Self::change_worker_address(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangePeerID) => { + Some(Method::ChangePeerID) | Some(Method::ChangePeerIDExported) => { Self::change_peer_id(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -4989,7 +5012,7 @@ impl ActorCode for Actor { Self::report_consensus_fault(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::WithdrawBalance) => { + Some(Method::WithdrawBalance) | Some(Method::WithdrawBalanceExported) => { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(&res)?) } @@ -4997,7 +5020,7 @@ impl ActorCode for Actor { Self::confirm_sector_proofs_valid(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangeMultiaddrs) => { + Some(Method::ChangeMultiaddrs) | Some(Method::ChangeMultiaddrsExported) => { Self::change_multiaddresses(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -5009,15 +5032,16 @@ impl ActorCode for Actor { Self::compact_sector_numbers(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ConfirmUpdateWorkerKey) => { - Self::confirm_update_worker_key(rt)?; + Some(Method::ConfirmChangeWorkerAddress) + | Some(Method::ConfirmChangeWorkerAddressExported) => { + Self::confirm_change_worker_address(rt)?; Ok(RawBytes::default()) } - Some(Method::RepayDebt) => { + Some(Method::RepayDebt) | Some(Method::RepayDebtExported) => { Self::repay_debt(rt)?; Ok(RawBytes::default()) } - Some(Method::ChangeOwnerAddress) => { + Some(Method::ChangeOwnerAddress) | Some(Method::ChangeOwnerAddressExported) => { Self::change_owner_address(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -5078,6 +5102,14 @@ impl ActorCode for Actor { let res = Self::get_vesting_funds(rt)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetPeerIDExported) => { + let res = Self::get_peer_id(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetMultiaddrsExported) => { + let res = Self::get_multiaddresses(rt)?; + Ok(RawBytes::serialize(res)?) + } } } } diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 49c8290d7..1f92e6eb6 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -529,3 +529,14 @@ pub struct GetVestingFundsReturn { } impl Cbor for GetVestingFundsReturn {} + +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] +pub struct GetPeerIDReturn { + #[serde(with = "serde_bytes")] + pub peer_id: Vec, +} + +#[derive(Debug, Default, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +pub struct GetMultiaddrsReturn { + pub multi_addrs: Vec, +} diff --git a/actors/miner/tests/change_owner_address_test.rs b/actors/miner/tests/change_owner_address_test.rs index f540b71c5..88fb32654 100644 --- a/actors/miner/tests/change_owner_address_test.rs +++ b/actors/miner/tests/change_owner_address_test.rs @@ -1,6 +1,9 @@ +use fil_actor_miner::{Actor, Method}; use fil_actors_runtime::test_utils::{ - expect_abort, new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, make_identity_cid, new_bls_addr, MockRuntime, + ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; +use fvm_ipld_encoding::RawBytes; use fvm_shared::econ::TokenAmount; use fvm_shared::{address::Address, error::ExitCode}; @@ -52,6 +55,47 @@ fn successful_change() { h.check_state(&rt); } +#[test] +fn change_owner_address_restricted_correctly() { + let (h, mut rt) = setup(); + + let params = &RawBytes::serialize(NEW_ADDRESS).unwrap(); + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeOwnerAddress as u64, params), + ); + + // can call the exported method + + rt.expect_validate_caller_addr(vec![h.owner]); + rt.call::(Method::ChangeOwnerAddressExported as u64, params).unwrap(); + + rt.verify(); + + let info = h.get_info(&rt); + assert_eq!(h.owner, info.owner); + assert_eq!(NEW_ADDRESS, info.pending_owner_address.unwrap()); + + // 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.call::(Method::ChangeOwnerAddressExported as u64, params).unwrap(); + + rt.verify(); + let info = h.get_info(&rt); + assert_eq!(NEW_ADDRESS, info.owner); + assert_eq!(NEW_ADDRESS, info.beneficiary); + assert!(info.pending_owner_address.is_none()); + + h.check_state(&rt); +} + #[test] fn successful_keep_beneficiary_when_change_owner() { let (mut h, mut rt) = setup(); diff --git a/actors/miner/tests/change_peer_id_test.rs b/actors/miner/tests/change_peer_id_test.rs index 1fd3cbeb2..1b704b277 100644 --- a/actors/miner/tests/change_peer_id_test.rs +++ b/actors/miner/tests/change_peer_id_test.rs @@ -1,4 +1,9 @@ -use fil_actors_runtime::test_utils::MockRuntime; +use fil_actor_miner::{Actor, ChangePeerIDParams, GetPeerIDReturn, Method}; +use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, MockRuntime, +}; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::error::ExitCode; mod util; use util::*; @@ -24,3 +29,42 @@ fn successfully_change_peer_id() { h.check_state(&rt); } + +#[test] +fn change_peer_id_restricted_correctly() { + let (h, mut rt) = setup(); + + let new_id = b"cthulhu".to_vec(); + + let params = RawBytes::serialize(ChangePeerIDParams { new_id: new_id.clone() }).unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.worker); + + // fail to call the unexported setter + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangePeerID as u64, ¶ms), + ); + + // call the exported setter + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.call::(Method::ChangePeerIDExported as u64, ¶ms).unwrap(); + + // call the exported getter + + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(new_id, ret.peer_id); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/change_worker_address_test.rs b/actors/miner/tests/change_worker_address_test.rs index 58cd4d9b1..f66a05049 100644 --- a/actors/miner/tests/change_worker_address_test.rs +++ b/actors/miner/tests/change_worker_address_test.rs @@ -11,6 +11,7 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; mod util; +use fil_actors_runtime::test_utils::make_identity_cid; use itertools::Itertools; use num_traits::Zero; use util::*; @@ -50,7 +51,7 @@ fn successfully_change_only_the_worker_address() { rt.set_epoch(deadline.period_end()); assert!(deadline.period_end() < effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let info = h.get_info(&rt); assert_eq!(info.pending_worker_key.unwrap().new_worker, new_worker); @@ -60,7 +61,7 @@ fn successfully_change_only_the_worker_address() { rt.set_epoch(effective_epoch); // enact worker change - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert address has changed let info = h.get_info(&rt); @@ -73,6 +74,77 @@ fn successfully_change_only_the_worker_address() { h.check_state(&rt); } +#[test] +fn change_and_confirm_worker_address_restricted_correctly() { + let (h, mut rt) = setup(); + + let original_control_addresses = h.control_addrs.clone(); + let new_worker = Address::new_id(999); + + rt.set_address_actor_type(new_worker, *ACCOUNT_ACTOR_CODE_ID); + + let params = RawBytes::serialize(ChangeWorkerAddressParams { + new_worker, + new_control_addresses: original_control_addresses, + }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeWorkerAddress as u64, ¶ms), + ); + + // call the exported method + rt.expect_send( + new_worker, + AccountMethod::PubkeyAddress as u64, + RawBytes::default(), + TokenAmount::zero(), + RawBytes::serialize(h.worker_key).unwrap(), + ExitCode::OK, + ); + + rt.expect_validate_caller_addr(vec![h.owner]); + + rt.call::(Method::ChangeWorkerAddressExported as u64, ¶ms).unwrap(); + + rt.verify(); + + // assert change has been made in state + let pending_worker_key = h.get_info(&rt).pending_worker_key.unwrap(); + assert_eq!(pending_worker_key.new_worker, new_worker); + + // confirmation time + + // move to deadline containing effective epoch + rt.set_epoch(rt.epoch + rt.policy.worker_key_change_delay); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ConfirmChangeWorkerAddress as u64, &RawBytes::default()), + ); + + // call the exported method + rt.expect_validate_caller_addr(vec![h.owner]); + rt.call::(Method::ConfirmChangeWorkerAddressExported as u64, &RawBytes::default()) + .unwrap(); + rt.verify(); + + // assert address has changed + let info = h.get_info(&rt); + assert_eq!(new_worker, info.worker); + + h.check_state(&rt); +} + #[test] fn change_cannot_be_overridden() { let (h, mut rt) = setup(); @@ -100,7 +172,7 @@ fn change_cannot_be_overridden() { assert_eq!(pending_worker_key.effective_at, effective_epoch); rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert original change is effected assert_eq!(new_worker_1, h.get_info(&rt).worker); @@ -146,7 +218,7 @@ fn successfully_change_both_worker_and_control_addresses() { // set current epoch and update worker key rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert both worker and control addresses have changed let info = h.get_info(&rt); diff --git a/actors/miner/tests/confirm_update_worker_key_test.rs b/actors/miner/tests/confirm_update_worker_key_test.rs index d19ff4600..97f720ecd 100644 --- a/actors/miner/tests/confirm_update_worker_key_test.rs +++ b/actors/miner/tests/confirm_update_worker_key_test.rs @@ -32,7 +32,7 @@ fn successfully_changes_the_worker_address() { // confirm at effective epoch rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); @@ -52,7 +52,7 @@ fn does_nothing_before_the_effective_date() { // confirm right before the effective epoch rt.set_epoch(effective_epoch - 1); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); @@ -70,7 +70,7 @@ fn does_nothing_before_the_effective_date() { fn does_nothing_when_no_update_is_set() { let (h, mut rt) = setup(); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); diff --git a/actors/miner/tests/miner_actor_test_peer_info.rs b/actors/miner/tests/miner_actor_test_peer_info.rs index 55c4a5250..478a9859a 100644 --- a/actors/miner/tests/miner_actor_test_peer_info.rs +++ b/actors/miner/tests/miner_actor_test_peer_info.rs @@ -1,6 +1,8 @@ use fil_actors_runtime::test_utils::*; -use fvm_ipld_encoding::BytesDe; +use fil_actor_miner::{Actor, ChangeMultiaddrsParams, GetMultiaddrsReturn, Method}; +use fvm_ipld_encoding::{BytesDe, RawBytes}; +use fvm_shared::error::ExitCode; mod util; @@ -109,3 +111,48 @@ fn cant_set_large_multiaddrs() { h.check_state(&rt); } + +#[test] +fn get_and_change_multiaddrs_restricted_correctly() { + let mut rt = MockRuntime::default(); + let h = util::ActorHarness::new(0); + + h.construct_and_verify(&mut rt); + + let new_multiaddrs = vec![BytesDe(vec![1, 3, 3, 7])]; + + let params = + &RawBytes::serialize(ChangeMultiaddrsParams { new_multi_addrs: new_multiaddrs.clone() }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.worker); + + // fail to call the unexported setter + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeMultiaddrs as u64, params), + ); + + // call the exported setter + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.call::(Method::ChangeMultiaddrsExported as u64, params).unwrap(); + rt.verify(); + + // call the exported getter + + rt.expect_validate_caller_any(); + let ret: GetMultiaddrsReturn = rt + .call::(Method::GetMultiaddrsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(new_multiaddrs, ret.multi_addrs); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/repay_debts.rs b/actors/miner/tests/repay_debts.rs index d4dba48cd..740daabe5 100644 --- a/actors/miner/tests/repay_debts.rs +++ b/actors/miner/tests/repay_debts.rs @@ -1,7 +1,12 @@ -use fil_actor_miner::locked_reward_from_reward; +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::BURNT_FUNDS_ACTOR_ADDR; +use fvm_ipld_encoding::RawBytes; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; mod util; use util::*; @@ -48,6 +53,52 @@ fn pay_debt_entirely_from_balance() { h.check_state(&rt); } +#[test] +fn repay_debt_restricted_correctly() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + h.construct_and_verify(&mut rt); + + // introduce fee debt + let mut st = h.get_state(&rt); + let fee_debt: TokenAmount = 4 * &*BIG_BALANCE; + st.fee_debt = fee_debt.clone(); + rt.replace_state(&st); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::RepayDebt as u64, &RawBytes::default()), + ); + + // can call the exported method + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.add_balance(fee_debt.clone()); + rt.set_received(fee_debt.clone()); + + rt.expect_send( + BURNT_FUNDS_ACTOR_ADDR, + METHOD_SEND, + RawBytes::default(), + fee_debt, + RawBytes::default(), + ExitCode::OK, + ); + + rt.call::(Method::RepayDebtExported as u64, &RawBytes::default()).unwrap(); + + rt.verify(); + + let st = h.get_state(&rt); + assert!(st.fee_debt.is_zero()); + h.check_state(&rt); +} + #[test] fn partially_repay_debt() { let h = ActorHarness::new(PERIOD_OFFSET); diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index c0e5bf7d1..fe8b8e1d3 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -23,13 +23,14 @@ use fil_actor_miner::{ DeclareFaultsRecoveredParams, DeferredCronEventParams, DisputeWindowedPoStParams, ExpirationQueue, ExpirationSet, ExtendSectorExpiration2Params, ExtendSectorExpirationParams, FaultDeclaration, GetAvailableBalanceReturn, GetBeneficiaryReturn, GetControlAddressesReturn, - Method, MinerConstructorParams as ConstructorParams, MinerInfo, Partition, - PendingBeneficiaryChange, PoStPartition, PowerPair, PreCommitSectorBatchParams, - PreCommitSectorBatchParams2, PreCommitSectorParams, ProveCommitSectorParams, - RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, - SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, - TerminationDeclaration, VestingFunds, WindowedPoSt, WithdrawBalanceParams, - WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, + GetMultiaddrsReturn, GetPeerIDReturn, Method, MinerConstructorParams as ConstructorParams, + MinerInfo, Partition, PendingBeneficiaryChange, PoStPartition, PowerPair, + PreCommitSectorBatchParams, PreCommitSectorBatchParams2, PreCommitSectorParams, + ProveCommitSectorParams, RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, + SectorPreCommitInfo, SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, + TerminateSectorsParams, TerminationDeclaration, VestingFunds, WindowedPoSt, + WithdrawBalanceParams, WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, + SECTORS_AMT_BITWIDTH, }; use fil_actor_miner::{Method as MinerMethod, ProveCommitAggregateParams}; use fil_actor_power::{ @@ -289,10 +290,15 @@ impl ActorHarness { expect_empty(result); rt.verify(); - let state = self.get_state(rt); - let info = state.get_info(&rt.store).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_id, info.peer_id); + assert_eq!(new_id, ret.peer_id); } pub fn set_peer_id_fail(&self, rt: &mut MockRuntime, new_id: Vec) { @@ -319,10 +325,15 @@ impl ActorHarness { expect_empty(result); rt.verify(); - let state = self.get_state(rt); - let info = state.get_info(&rt.store).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetMultiaddrsReturn = rt + .call::(Method::GetMultiaddrsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_multiaddrs, info.multi_address); + assert_eq!(new_multiaddrs, ret.multi_addrs); } pub fn set_multiaddr_fail(&self, rt: &mut MockRuntime, new_multiaddrs: Vec) { @@ -2085,10 +2096,15 @@ impl ActorHarness { .unwrap(); rt.verify(); - let state: State = rt.get_state(); - let info = state.get_info(rt.store()).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_id, info.peer_id); + assert_eq!(new_id, ret.peer_id); } pub fn repay_debts( @@ -2245,10 +2261,10 @@ impl ActorHarness { ret } - pub fn confirm_update_worker_key(&self, rt: &mut MockRuntime) -> Result<(), ActorError> { + pub fn confirm_change_worker_address(&self, rt: &mut MockRuntime) -> Result<(), ActorError> { rt.expect_validate_caller_addr(vec![self.owner]); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.owner); - rt.call::(Method::ConfirmUpdateWorkerKey as u64, &RawBytes::default())?; + rt.call::(Method::ConfirmChangeWorkerAddress as u64, &RawBytes::default())?; rt.verify(); Ok(()) diff --git a/actors/miner/tests/withdraw_balance.rs b/actors/miner/tests/withdraw_balance.rs index 7d1a71b3c..bd65bff7b 100644 --- a/actors/miner/tests/withdraw_balance.rs +++ b/actors/miner/tests/withdraw_balance.rs @@ -1,10 +1,16 @@ -use fil_actor_miner::BeneficiaryTerm; -use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message}; +use fil_actor_miner::{ + Actor, BeneficiaryTerm, Method, WithdrawBalanceParams, WithdrawBalanceReturn, +}; +use fil_actors_runtime::test_utils::{ + expect_abort, expect_abort_contains_message, make_identity_cid, +}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; mod util; use util::*; @@ -29,6 +35,51 @@ fn happy_path_withdraws_funds() { h.check_state(&rt); } +#[test] +fn withdraw_funds_restricted_correctly() { + let (h, mut rt) = setup(); + rt.set_balance(BIG_BALANCE.clone()); + let amount_requested = ONE_PERCENT_BALANCE.clone(); + + let params = + &RawBytes::serialize(WithdrawBalanceParams { amount_requested: amount_requested.clone() }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::WithdrawBalance as u64, params), + ); + + // call the exported method + + rt.expect_validate_caller_addr(vec![h.owner, h.beneficiary]); + rt.expect_send( + h.beneficiary, + METHOD_SEND, + RawBytes::default(), + amount_requested.clone(), + RawBytes::default(), + ExitCode::OK, + ); + + let ret = rt + .call::(Method::WithdrawBalanceExported as u64, params) + .unwrap() + .deserialize::() + .unwrap(); + + assert_eq!(amount_requested, ret.amount_withdrawn,); + + rt.verify(); + + h.check_state(&rt); +} + #[test] fn fails_if_miner_cant_repay_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); From fc811bd1e74180717f9500330543db9d2dfdae38 Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 22 Nov 2022 14:15:41 -0500 Subject: [PATCH 142/339] Miner: Derive Eq for more types --- actors/miner/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 1f92e6eb6..db2882c4e 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -536,7 +536,7 @@ pub struct GetPeerIDReturn { pub peer_id: Vec, } -#[derive(Debug, Default, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct GetMultiaddrsReturn { pub multi_addrs: Vec, } From 59f0c4a64a10db0e9759c40a73cc37a80a5e0d8b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 23 Nov 2022 17:56:44 -0500 Subject: [PATCH 143/339] Power actor: Export methods to CreateMiner and get miner counts (#868) * Power: Export CreateMiner * Power Actor: Export MinerCount and MinerConsensusCount * Update actors/power/src/lib.rs Co-authored-by: Alex <445306+anorth@users.noreply.github.com> Co-authored-by: Alex <445306+anorth@users.noreply.github.com> --- actors/power/src/lib.rs | 33 +++++++++- actors/power/src/types.rs | 12 ++++ actors/power/tests/harness/mod.rs | 29 ++++++--- actors/power/tests/power_actor_tests.rs | 80 +++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 9d5552094..5eeac63a0 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -62,8 +62,11 @@ pub enum Method { SubmitPoRepForBulkVerify = 8, CurrentTotalPower = 9, // Method numbers derived from FRC-0042 standards + CreateMinerExported = frc42_dispatch::method_hash!("CreateMiner"), NetworkRawPowerExported = frc42_dispatch::method_hash!("NetworkRawPower"), MinerRawPowerExported = frc42_dispatch::method_hash!("MinerRawPower"), + MinerCountExported = frc42_dispatch::method_hash!("MinerCount"), + MinerConsensusCountExported = frc42_dispatch::method_hash!("MinerConsensusCount"), } pub const ERR_TOO_MANY_PROVE_COMMITS: ExitCode = ExitCode::new(32); @@ -396,6 +399,26 @@ impl Actor { Ok(MinerRawPowerReturn { raw_byte_power, meets_consensus_minimum }) } + /// Returns the total number of miners created, regardless of whether or not + /// they have any pledged storage. + fn miner_count(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerCountReturn { miner_count: st.miner_count }) + } + + /// Returns the total number of miners that have more than the consensus minimum amount of storage active. + /// Active means that the storage must not be faulty. + fn miner_consensus_count( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerConsensusCountReturn { miner_consensus_count: st.miner_above_min_power_count }) + } + fn process_batch_proof_verifies( rt: &mut impl Runtime, rewret: &ThisEpochRewardReturn, @@ -663,7 +686,7 @@ impl ActorCode for Actor { Self::constructor(rt)?; Ok(RawBytes::default()) } - Some(Method::CreateMiner) => { + Some(Method::CreateMiner) | Some(Method::CreateMinerExported) => { let res = Self::create_miner(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -700,6 +723,14 @@ impl ActorCode for Actor { let res = Self::miner_raw_power(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::MinerCountExported) => { + let res = Self::miner_count(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::MinerConsensusCountExported) => { + let res = Self::miner_consensus_count(rt)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 3df018c18..7afe6f1d3 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -93,3 +93,15 @@ pub struct MinerRawPowerReturn { } impl Cbor for MinerRawPowerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerCountReturn { + pub miner_count: i64, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerConsensusCountReturn { + pub miner_consensus_count: i64, +} diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index b23661098..cad99e160 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -1,15 +1,15 @@ use cid::Cid; use fil_actor_power::detail::GAS_ON_SUBMIT_VERIFY_SEAL; -use fil_actor_power::epoch_key; use fil_actor_power::ext::miner::ConfirmSectorProofsParams; use fil_actor_power::ext::miner::CONFIRM_SECTOR_PROOFS_VALID_METHOD; use fil_actor_power::ext::reward::Method::ThisEpochReward; use fil_actor_power::ext::reward::UPDATE_NETWORK_KPI; use fil_actor_power::testing::check_state_invariants; -use fil_actor_power::CronEvent; use fil_actor_power::EnrollCronEventParams; use fil_actor_power::CRON_QUEUE_AMT_BITWIDTH; use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; +use fil_actor_power::{epoch_key, MinerCountReturn}; +use fil_actor_power::{CronEvent, MinerConsensusCountReturn}; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; @@ -210,9 +210,15 @@ impl Harness { keys.iter().map(|k| Address::from_bytes(k).unwrap()).collect::>() } - pub fn miner_count(&self, rt: &MockRuntime) -> i64 { - let st: State = rt.get_state(); - st.miner_count + pub fn miner_count(&self, rt: &mut MockRuntime) -> i64 { + rt.expect_validate_caller_any(); + let ret: MinerCountReturn = rt + .call::(Method::MinerCountExported as MethodNum, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + ret.miner_count } pub fn this_epoch_baseline_power(&self) -> &StoragePower { @@ -366,8 +372,17 @@ impl Harness { } pub fn expect_miners_above_min_power(&self, rt: &mut MockRuntime, count: i64) { - let st: State = rt.get_state(); - assert_eq!(count, st.miner_above_min_power_count); + rt.expect_validate_caller_any(); + let ret: MinerConsensusCountReturn = rt + .call::( + Method::MinerConsensusCountExported as MethodNum, + &RawBytes::default(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(count, ret.miner_consensus_count); } pub fn expect_query_network_info(&self, rt: &mut MockRuntime) { diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index fba2a7359..addff1624 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -13,11 +13,12 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; +use fvm_shared::MethodNum; use num_traits::Zero; use std::ops::Neg; use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, CreateMinerReturn, EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, }; @@ -326,8 +327,7 @@ fn new_miner_updates_miner_above_min_power_count() { h.window_post_proof = test.proof; h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap(); - let st: State = rt.get_state(); - assert_eq!(test.expected_miners, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, test.expected_miners); h.check_state(&rt); } } @@ -372,8 +372,7 @@ fn power_accounting_crossing_threshold() { let expected_total_above = &(power_unit * 4); h.expect_total_power_eager(&mut rt, expected_total_above, &(expected_total_above * 10)); - let st: State = rt.get_state(); - assert_eq!(4, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, 4); // Less than 4 miners above threshold again small miner power is counted again h.update_claimed_power(&mut rt, MINER4, &delta.neg(), &(delta.neg() * 10)); @@ -1011,7 +1010,7 @@ mod cron_tests { assert!(h.get_claim(&rt, &miner1).is_none()); // miner count has been reduced to 1 - assert_eq!(h.miner_count(&rt), 1); + assert_eq!(h.miner_count(&mut rt), 1); // next epoch, only the reward actor is invoked rt.set_epoch(3); @@ -1440,3 +1439,72 @@ mod submit_porep_for_bulk_verify_tests { h.check_state(&rt); } } + +#[test] +fn create_miner_restricted_correctly() { + let (h, mut rt) = setup(); + + let peer = "miner".as_bytes().to_vec(); + let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; + + let params = serialize( + &CreateMinerParams { + owner: *OWNER, + worker: *OWNER, + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer: peer.clone(), + multiaddrs: multiaddrs.clone(), + }, + "create miner params", + ) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), *OWNER); + + // cannot call the unexported method + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::CreateMiner as MethodNum, ¶ms), + ); + + // can call the exported method + + rt.expect_validate_caller_any(); + let expected_init_params = ExecParams { + code_cid: *MINER_ACTOR_CODE_ID, + constructor_params: serialize( + &MinerConstructorParams { + owner: *OWNER, + worker: *OWNER, + control_addresses: vec![], + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer_id: peer, + multi_addresses: multiaddrs, + }, + "minerctor params", + ) + .unwrap(), + }; + let create_miner_ret = CreateMinerReturn { id_address: *MINER, robust_address: *ACTOR }; + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC_METHOD, + RawBytes::serialize(expected_init_params).unwrap(), + TokenAmount::zero(), + RawBytes::serialize(create_miner_ret).unwrap(), + ExitCode::OK, + ); + + let ret: CreateMinerReturn = rt + .call::(Method::CreateMinerExported as MethodNum, ¶ms) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(ret.id_address, *MINER); + assert_eq!(ret.robust_address, *ACTOR); + + h.check_state(&rt); +} From fc7477c4906f9927c4c63a6c4ceac168c9620472 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 28 Nov 2022 17:31:35 -0600 Subject: [PATCH 144/339] Verifreg: Export AddVerifiedClient and GetClaims (#873) * Verifreg: Rename AddVerifierClientParams to AddVerifiedClientParams * Verifreg: Export AddVerifiedClient and GetClaims --- actors/verifreg/src/lib.rs | 8 +- actors/verifreg/src/types.rs | 2 +- actors/verifreg/tests/harness/mod.rs | 4 +- actors/verifreg/tests/verifreg_actor_test.rs | 106 ++++++++++++++++-- test_vm/src/util.rs | 4 +- test_vm/tests/publish_deals_test.rs | 4 +- test_vm/tests/verifreg_remove_datacap_test.rs | 4 +- 7 files changed, 112 insertions(+), 20 deletions(-) diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index d7962cfd2..ded09f416 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -61,7 +61,9 @@ pub enum Method { ExtendClaimTerms = 11, RemoveExpiredClaims = 12, // Method numbers derived from FRC-0042 standards + AddVerifiedClientExported = frc42_dispatch::method_hash!("AddVerifiedClient"), RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), + GetClaimsExported = frc42_dispatch::method_hash!("GetClaims"), ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), @@ -145,7 +147,7 @@ impl Actor { pub fn add_verified_client( rt: &mut impl Runtime, - params: AddVerifierClientParams, + params: AddVerifiedClientParams, ) -> Result<(), ActorError> { // The caller will be verified by checking table below rt.validate_immediate_caller_accept_any()?; @@ -1085,7 +1087,7 @@ impl ActorCode for Actor { Self::remove_verifier(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::AddVerifiedClient) => { + Some(Method::AddVerifiedClient) | Some(Method::AddVerifiedClientExported) => { Self::add_verified_client(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -1107,7 +1109,7 @@ impl ActorCode for Actor { let res = Self::extend_claim_terms(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::GetClaims) => { + Some(Method::GetClaims) | Some(Method::GetClaimsExported) => { let res = Self::get_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/verifreg/src/types.rs b/actors/verifreg/src/types.rs index a888fc029..bc4a595e5 100644 --- a/actors/verifreg/src/types.rs +++ b/actors/verifreg/src/types.rs @@ -30,7 +30,7 @@ impl Cbor for VerifierParams {} pub type AddVerifierParams = VerifierParams; -pub type AddVerifierClientParams = VerifierParams; +pub type AddVerifiedClientParams = VerifierParams; /// DataCap is an integer number of bytes. /// We can introduce policy changes and replace this in the future. diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index d608197af..2fe45ff82 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -14,7 +14,7 @@ use num_traits::{ToPrimitive, Zero}; use fil_actor_verifreg::testing::check_state_invariants; use fil_actor_verifreg::{ - ext, Actor as VerifregActor, AddVerifierClientParams, AddVerifierParams, Allocation, + ext, Actor as VerifregActor, AddVerifiedClientParams, AddVerifierParams, Allocation, AllocationID, AllocationRequest, AllocationRequests, AllocationsResponse, Claim, ClaimAllocationsParams, ClaimAllocationsReturn, ClaimExtensionRequest, ClaimID, DataCap, ExtendClaimTermsParams, ExtendClaimTermsReturn, GetClaimsParams, GetClaimsReturn, Method, @@ -187,7 +187,7 @@ impl Harness { ExitCode::OK, ); - let params = AddVerifierClientParams { address: *client, allowance: allowance.clone() }; + let params = AddVerifiedClientParams { address: *client, allowance: allowance.clone() }; let ret = rt.call::( Method::AddVerifiedClient as MethodNum, &RawBytes::serialize(params).unwrap(), diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index fbc3649e3..d7bdec2c8 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -238,9 +238,14 @@ mod clients { use fvm_shared::{MethodNum, METHOD_SEND}; use num_traits::Zero; - use fil_actor_verifreg::{Actor as VerifregActor, AddVerifierClientParams, DataCap, Method}; + use fil_actor_verifreg::{ + ext, Actor as VerifregActor, AddVerifiedClientParams, DataCap, Method, + }; use fil_actors_runtime::test_utils::*; + use fil_actors_runtime::{DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR}; + use harness::*; + use num_traits::ToPrimitive; use util::*; use crate::*; @@ -363,7 +368,7 @@ mod clients { let caller = Address::new_id(209); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, caller); rt.expect_validate_caller_any(); - let params = AddVerifierClientParams { address: *CLIENT, allowance: allowance_client }; + let params = AddVerifiedClientParams { address: *CLIENT, allowance: allowance_client }; expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -374,6 +379,59 @@ mod clients { h.check_state(&rt); } + #[test] + fn add_verified_client_restricted_correctly() { + let (h, mut rt) = new_harness(); + let allowance_verifier = verifier_allowance(&rt); + let allowance_client = client_allowance(&rt); + h.add_verifier(&mut rt, &VERIFIER, &allowance_verifier).unwrap(); + + let params = + AddVerifiedClientParams { address: *CLIENT, allowance: allowance_client.clone() }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), *VERIFIER); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::AddVerifiedClient as MethodNum, + &RawBytes::serialize(params.clone()).unwrap(), + ), + ); + + rt.verify(); + + // can call the exported method num + + let mint_params = ext::datacap::MintParams { + to: *CLIENT, + amount: TokenAmount::from_whole(allowance_client.to_i64().unwrap()), + operators: vec![STORAGE_MARKET_ACTOR_ADDR], + }; + rt.expect_send( + DATACAP_TOKEN_ACTOR_ADDR, + ext::datacap::Method::Mint as MethodNum, + RawBytes::serialize(&mint_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + rt.expect_validate_caller_any(); + rt.call::( + Method::AddVerifiedClientExported as MethodNum, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + rt.verify(); + + h.check_state(&rt); + } + #[test] fn rejects_allowance_greater_than_verifier_cap() { let (h, mut rt) = new_harness(); @@ -432,7 +490,8 @@ mod allocs_claims { use std::str::FromStr; use fil_actor_verifreg::{ - Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, Method, State, + Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, GetClaimsParams, + GetClaimsReturn, Method, State, }; use fil_actor_verifreg::{Claim, ExtendClaimTermsReturn}; use fil_actors_runtime::cbor::serialize; @@ -896,7 +955,7 @@ mod allocs_claims { } #[test] - fn extend_claims_restricted_correctly() { + fn claims_restricted_correctly() { let (h, mut rt) = new_harness(); let size = MINIMUM_VERIFIED_ALLOCATION_SIZE as u64; let sector = 0; @@ -908,7 +967,8 @@ mod allocs_claims { let id1 = h.create_claim(&mut rt, &claim1).unwrap(); - // Extend claim terms and verify return value. + // First, let's extend some claims + let params = ExtendClaimTermsParams { terms: vec![ClaimTerm { provider: PROVIDER1, claim_id: id1, term_max: max_term + 1 }], }; @@ -916,7 +976,7 @@ mod allocs_claims { // set caller to not-builtin rt.set_caller(make_identity_cid(b"1234"), Address::new_id(CLIENT1)); - // cannot call the unexported method num + // cannot call the unexported extend method num expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "must be built-in", @@ -939,8 +999,38 @@ mod allocs_claims { assert_eq!(ret.codes(), vec![ExitCode::OK]); - // Verify state directly. - assert_claim(&rt, PROVIDER1, id1, &Claim { term_max: max_term + 1, ..claim1 }); + // Now let's Get those Claims, and check them + + let params = GetClaimsParams { claim_ids: vec![id1], provider: PROVIDER1 }; + + // cannot call the unexported extend method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::GetClaims as MethodNum, + &serialize(¶ms, "get claims params").unwrap(), + ), + ); + + rt.verify(); + + // can call the exported method num + rt.expect_validate_caller_any(); + let ret: GetClaimsReturn = rt + .call::( + Method::GetClaimsExported as MethodNum, + &serialize(¶ms, "get claims params").unwrap(), + ) + .unwrap() + .deserialize() + .expect("failed to deserialize get claims return"); + + rt.verify(); + + assert_eq!(ret.batch_info.codes(), vec![ExitCode::OK]); + assert_eq!(ret.claims, vec![Claim { term_max: max_term + 1, ..claim1 }]); + h.check_state(&rt); } } diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 72ce940dc..7ba03842d 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -42,7 +42,7 @@ use fil_actor_power::{ }; use fil_actor_reward::Method as RewardMethod; use fil_actor_verifreg::{ - AddVerifierClientParams, AllocationID, ClaimID, ClaimTerm, ExtendClaimTermsParams, + AddVerifiedClientParams, AllocationID, ClaimID, ClaimTerm, ExtendClaimTermsParams, GetClaimsParams, Method as VerifregMethod, RemoveExpiredAllocationsParams, VerifierParams, }; use fil_actors_runtime::cbor::deserialize; @@ -868,7 +868,7 @@ pub fn verifreg_add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) pub fn verifreg_add_client(v: &VM, verifier: Address, client: Address, allowance: StoragePower) { let add_client_params = - AddVerifierClientParams { address: client, allowance: allowance.clone() }; + AddVerifiedClientParams { address: client, allowance: allowance.clone() }; apply_ok( v, verifier, diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 242544165..39238d1a0 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -11,7 +11,7 @@ use fil_actor_miner::Method as MinerMethod; use fil_actor_power::Method as PowerMethod; use fil_actor_reward::Method as RewardMethod; -use fil_actor_verifreg::{AddVerifierClientParams, Method as VerifregMethod}; +use fil_actor_verifreg::{AddVerifiedClientParams, Method as VerifregMethod}; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; @@ -74,7 +74,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { // setup verified client verifreg_add_verifier(&v, verifier, StoragePower::from((32_u64 << 40) as u128)); - let add_client_params = AddVerifierClientParams { + let add_client_params = AddVerifiedClientParams { address: verified_client, allowance: StoragePower::from((1_u64 << 32) as u64), }; diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 8f098b298..24194ed43 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -15,7 +15,7 @@ use fil_actor_datacap::{ DestroyParams, Method as DataCapMethod, MintParams, State as DataCapState, }; use fil_actor_verifreg::{ - AddVerifierClientParams, DataCap, RemoveDataCapParams, RemoveDataCapRequest, + AddVerifiedClientParams, DataCap, RemoveDataCapParams, RemoveDataCapRequest, RemoveDataCapReturn, SIGNATURE_DOMAIN_SEPARATION_REMOVE_DATA_CAP, }; use fil_actor_verifreg::{AddrPairKey, Method as VerifregMethod}; @@ -47,7 +47,7 @@ fn remove_datacap_simple_successful_path() { // register the verified client let add_verified_client_params = - AddVerifierClientParams { address: verified_client, allowance: verifier_allowance.clone() }; + AddVerifiedClientParams { address: verified_client, allowance: verifier_allowance.clone() }; let mint_params = MintParams { to: verified_client, amount: TokenAmount::from_whole(verifier_allowance.to_i64().unwrap()), From ec7d3f2f65941737c90f7d2e41047c6aead1d771 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 30 Nov 2022 13:39:02 -0800 Subject: [PATCH 145/339] feat: avoid exposing the gas limit in the runtime (#880) We exposed this for the GASLIMIT opcode, but didn't actually need it. I'm removing it here to reduce the diff and API surface area. See https://github.com/filecoin-project/ref-fvm/issues/1168 for context. --- runtime/src/runtime/fvm.rs | 4 ---- runtime/src/runtime/mod.rs | 3 --- runtime/src/test_utils.rs | 3 --- test_vm/src/lib.rs | 3 --- 4 files changed, 13 deletions(-) diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 77efd86de..72dcb6eb9 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -94,10 +94,6 @@ impl MessageInfo for FvmMessage { fvm::message::value_received() } - fn gas_limit(&self) -> u64 { - fvm::message::gas_limit() - } - fn gas_premium(&self) -> TokenAmount { fvm::message::gas_premium() } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 2a03aea50..605da403c 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -265,9 +265,6 @@ pub trait MessageInfo { /// added to current_balance() before method invocation. fn value_received(&self) -> TokenAmount; - /// The message gas limit - fn gas_limit(&self) -> u64; - /// The message gas premium fn gas_premium(&self) -> TokenAmount; } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 087a57454..c74733271 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -821,9 +821,6 @@ impl MessageInfo for MockRuntime { fn value_received(&self) -> TokenAmount { self.value_received.clone() } - fn gas_limit(&self) -> u64 { - self.gas_limit - } fn gas_premium(&self) -> TokenAmount { self.gas_premium.clone() } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 410901a64..c054015d1 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -573,9 +573,6 @@ impl MessageInfo for InvocationCtx<'_, '_> { fn value_received(&self) -> TokenAmount { self.msg.value.clone() } - fn gas_limit(&self) -> u64 { - u32::MAX.into() - } fn gas_premium(&self) -> TokenAmount { TokenAmount::zero() } From a30ee4e034988b1f8f894b5c24e684457f31c8b8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 30 Nov 2022 22:57:25 -0800 Subject: [PATCH 146/339] chore: cleanup instruction tests (#884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (@vyzo 👀) --- .../interpreter/instructions/arithmetic.rs | 223 ++++-------------- .../src/interpreter/instructions/bitwise.rs | 37 +-- 2 files changed, 54 insertions(+), 206 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 139a52adc..64ff0ec9b 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -111,192 +111,77 @@ pub fn exp(mut base: U256, power: U256) -> U256 { #[cfg(test)] mod test { mod basic { - use crate::interpreter::{stack::Stack, U256}; - - // all operations go a b - fn push_2(s: &mut Stack, a: impl Into, b: impl Into) { - unsafe { - s.push(b.into()); - s.push(a.into()); - } - } - - fn expect_stack_value(s: &mut Stack, e: impl Into, comment: impl AsRef) { - let mut expected = Stack::new(); - unsafe { - expected.push(e.into()); - } - - // stacks should be _exactly_ the same - assert_eq!(unsafe { s.get(0) }, unsafe { expected.get(0) }, "{}", comment.as_ref()); - unsafe { - s.pop(); - } - } - - pub fn ins_add(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::add(a, b); - s.push(c); - } - } - - pub fn ins_mul(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::mul(a, b); - s.push(c); - } - } - - pub fn ins_sub(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::sub(a, b); - s.push(c); - } - } - - pub fn ins_div(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::div(a, b); - s.push(c); - } - } - - pub fn ins_signextend(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::signextend(a, b); - s.push(c); - } - } - - pub fn ins_exp(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::arithmetic::exp(a, b); - s.push(c); - } - } + use crate::interpreter::instructions::arithmetic::*; + use crate::interpreter::U256; #[test] - fn add() { - let mut s = Stack::default(); - let s = &mut s; - - push_2(s, 0, 0); - ins_add(s); - expect_stack_value(s, 0, "add nothing to nothing"); - + 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 - push_2(s, U256::max_value(), 0); - ins_add(s); - expect_stack_value(s, U256::max_value(), "add nothing to max value"); - - push_2(s, 2, 2); - ins_add(s); - expect_stack_value(s, 4, "2 plus 2 equals 5 (???)"); - - push_2(s, u64::MAX, 32); - ins_add(s); - expect_stack_value(s, u64::MAX as u128 + 32, "add 32 past a single (u64) limb of u256"); - + 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 - push_2(s, U256::max_value(), 1); - ins_add(s); - expect_stack_value(s, 0, "overflow by one"); - + assert_eq!(add(U256::max_value(), 1.into()), 0, "overflow by one"); // wrap all limbs - push_2(s, U256::max_value(), U256::max_value()); - ins_add(s); - expect_stack_value(s, U256::max_value() - 1, "overflow by max, should be 2^256-1"); + assert_eq!( + add(U256::max_value(), U256::max_value()), + U256::max_value() - 1, + "overflow by max, should be 2^256-1" + ); } #[test] - fn mul() { - let mut s = Stack::default(); - let s = &mut s; - - push_2(s, 0, 0); - ins_mul(s); - expect_stack_value(s, 0, "multiply nothing by nothing"); - - push_2(s, 2, 3); - ins_mul(s); - expect_stack_value(s, 6, "multiply 2 by 3"); - - push_2(s, u64::MAX, 2); - ins_mul(s); - expect_stack_value(s, (u64::MAX as u128) * 2, "2^64 x 2"); + 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 sub() { - let mut s = Stack::default(); - let s = &mut s; - - push_2(s, 0, 0); - ins_sub(s); - expect_stack_value(s, 0, "subtract nothing by nothing"); - - push_2(s, 2, 1); - ins_sub(s); - expect_stack_value(s, 1, "subtract 2 by 1"); - - push_2(s, (u64::MAX as u128) + 32, 64); - ins_sub(s); - expect_stack_value(s, u64::MAX - 32, "subtract 64 from a value 32 over a single limb"); - + 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 - push_2(s, 0, 1); - ins_sub(s); - expect_stack_value(s, U256::max_value(), "wrap around to max by one"); - + assert_eq!(sub(0.into(), 1.into()), U256::max_value(), "wrap around to max by one"); // wrap all limbs - push_2(s, U256::max_value(), U256::max_value()); - ins_sub(s); - expect_stack_value(s, 0, "wrap around zero by 2^256"); + assert_eq!(sub(U256::max_value(), U256::max_value()), 0, "wrap around zero by 2^256"); } #[test] - fn div() { - let mut s = Stack::default(); - let s = &mut s; - - push_2(s, 0, 0); - ins_div(s); - expect_stack_value(s, 0, "divide nothing by nothing (yes)"); - - push_2(s, 4, 1); - ins_div(s); - expect_stack_value(s, 4, "divide 4 by 1"); - - push_2(s, u128::MAX, 2); - ins_div(s); - expect_stack_value(s, u128::MAX / 2, "divide 2^128 by 2 (uses >1 limb)"); + 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_signextend() { macro_rules! assert_exp { ($num:expr, $byte:expr, $result:expr) => { - let mut stack = Stack::new(); - unsafe { - stack.push(($num).into()); - stack.push(($byte).into()); - } - ins_signextend(&mut stack); - let res: U256 = ($result).into(); - assert_eq!(res, unsafe { stack.pop() }); + let res: U256 = $result.into(); + assert_eq!(res, signextend(($byte).into(), ($num).into())); }; } assert_exp!(0xff, 0, U256::MAX); @@ -318,14 +203,8 @@ mod test { fn test_exp() { macro_rules! assert_exp { ($base:expr, $exp:expr, $result:expr) => { - let mut stack = Stack::new(); - unsafe { - stack.push(($exp).into()); - stack.push(($base).into()); - } - ins_exp(&mut stack); - let res: U256 = ($result).into(); - assert_eq!(res, unsafe { stack.pop() }); + let res: U256 = $result.into(); + assert_eq!(res, exp(($base).into(), ($exp).into())); }; } diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs index b9800c0f7..1b51ea6df 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -58,52 +58,21 @@ pub fn sar(shift: U256, mut value: U256) -> U256 { #[cfg(test)] mod tests { use super::*; - use crate::interpreter::stack::Stack; - - pub fn ins_byte(s: &mut Stack) { - unsafe { - let a = s.pop(); - let b = s.pop(); - let c = crate::interpreter::instructions::bitwise::byte(a, b); - s.push(c); - } - } #[test] fn test_instruction_byte() { let value = U256::from_big_endian(&(1u8..=32u8).map(|x| 5 * x).collect::>()); for i in 0u16..32 { - let mut stack = Stack::new(); - unsafe { - stack.push(value); - stack.push(U256::from(i)); - } - - ins_byte(&mut stack); - let result = unsafe { stack.pop() }; + let result = byte(U256::from(i), value); assert_eq!(result, U256::from(5 * (i + 1))); } - let mut stack = Stack::new(); - unsafe { - stack.push(value); - stack.push(U256::from(100u128)); - } - - ins_byte(&mut stack); - let result = unsafe { stack.pop() }; + let result = byte(U256::from(100u128), value); assert_eq!(result, U256::zero()); - let mut stack = Stack::new(); - unsafe { - stack.push(value); - stack.push(U256::from_u128_words(1, 0)); - } - - ins_byte(&mut stack); - let result = unsafe { stack.pop() }; + let result = byte(U256::from_u128_words(1, 0), value); assert_eq!(result, U256::zero()); } } From 4f7b89f18656c82bea6296a156498f8f4836efc2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Dec 2022 07:01:26 -0800 Subject: [PATCH 147/339] feat: correctly implement staticcall (#879) * feat: correctly implement staticcall * fix: correctly handle staticcall in callactor * fix: clippy --- .../evm/src/interpreter/instructions/call.rs | 23 +-- actors/evm/src/interpreter/precompiles.rs | 23 ++- actors/evm/src/interpreter/system.rs | 19 ++- actors/evm/src/lib.rs | 22 +-- runtime/src/runtime/fvm.rs | 137 ++++++++++------- runtime/src/runtime/mod.rs | 17 +++ runtime/src/test_utils.rs | 143 ++++++++++++------ test_vm/src/lib.rs | 114 +++++++++++--- 8 files changed, 321 insertions(+), 177 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 8549c80f0..88f2eb831 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -244,20 +244,17 @@ pub fn call_generic( // switch to a basic "send" so the call will still work even if the // target actor would reject a normal ethereum call. METHOD_SEND - } else if system.readonly || kind == CallKind::StaticCall { - // Invoke, preserving read-only mode. - Method::InvokeContractReadOnly as u64 } else { // Otherwise, invoke normally. Method::InvokeContract as u64 }; - system.send( - &dst_addr, - method, - // TODO: support IPLD codecs #758 - RawBytes::serialize(BytesSer(input_data))?, - TokenAmount::from(&value), - ) + // TODO: support IPLD codecs #758 + let params = RawBytes::serialize(BytesSer(input_data))?; + if kind == CallKind::StaticCall { + system.send_read_only(&dst_addr, method, params) + } else { + system.send(&dst_addr, method, params, TokenAmount::from(&value)) + } } } CallKind::DelegateCall => { @@ -267,11 +264,7 @@ pub fn call_generic( )?; // and then invoke self with delegate; readonly context is sticky - let params = DelegateCallParams { - code, - input: input_data.to_vec(), - readonly: system.readonly, - }; + let params = DelegateCallParams { code, input: input_data.to_vec() }; system.send( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 3b68bfeaa..98176d6c5 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -417,13 +417,6 @@ fn resolve_address(rt: &RT, input: &[u8], _: PrecompileContext) -> /// - positive are user errors (from the called actor) /// - 0 is success pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> PrecompileResult { - // TODO Until we support readonly (static) calls at the fvm level, we disallow callactor - // when in static mode as it is sticky and there are no guarantee of preserving the - // static invariant - if ctx.is_static { - return Err(PrecompileError::CallActorError(StatusCode::StaticModeViolation)); - } - // ----- Input Parameters ------- let mut input_params = U256Reader::new(input); @@ -451,12 +444,16 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; // TODO passing underlying gas into send when implemented in FVM - rt.send( - &address, - method, - RawBytes::from(input_data.to_vec()), - TokenAmount::from(&ctx.value), - ) + if ctx.is_static { + rt.send_read_only(&address, method, RawBytes::from(input_data.to_vec())) + } else { + rt.send( + &address, + method, + RawBytes::from(input_data.to_vec()), + TokenAmount::from(&ctx.value), + ) + } }; // ------ Build Output ------- diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 25461efab..c854298d9 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -50,6 +50,7 @@ impl<'r, RT: Runtime> System<'r, RT> { 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")); @@ -61,15 +62,16 @@ impl<'r, RT: Runtime> System<'r, RT> { nonce: 1, saved_state_root: None, bytecode: None, - readonly: false, + readonly: read_only, }) } /// Load the actor from state. - pub fn load(rt: &'r mut RT, readonly: bool) -> Result + pub fn load(rt: &'r mut RT) -> Result where RT::Blockstore: Clone, { + let read_only = rt.read_only(); let store = rt.store().clone(); let state_root = rt.get_state_root()?; let state: State = store @@ -84,7 +86,7 @@ impl<'r, RT: Runtime> System<'r, RT> { nonce: state.nonce, saved_state_root: Some(state_root), bytecode: Some(state.bytecode), - readonly, + readonly: read_only, }) } @@ -109,6 +111,17 @@ impl<'r, RT: Runtime> System<'r, RT> { Ok(result) } + /// Send a message in "read-only" mode (for staticcall). + pub fn send_read_only( + &mut self, + to: &Address, + method: MethodNum, + params: RawBytes, + ) -> Result { + self.flush()?; + self.rt.send_read_only(to, method, params) + } + /// Flush the actor state (bytecode, nonce, and slots). pub fn flush(&mut self) -> Result<(), ActorError> { if self.saved_state_root.is_some() { diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 356760a30..de373a062 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -52,8 +52,7 @@ pub enum Method { InvokeContract = 2, GetBytecode = 3, GetStorageAt = 4, - InvokeContractReadOnly = 5, - InvokeContractDelegate = 6, + InvokeContractDelegate = 5, } pub struct EvmContractActor; @@ -140,7 +139,6 @@ impl EvmContractActor { pub fn invoke_contract( rt: &mut RT, input_data: &[u8], - readonly: bool, with_code: Option, ) -> Result, ActorError> where @@ -153,7 +151,7 @@ impl EvmContractActor { rt.validate_immediate_caller_accept_any()?; } - let mut system = System::load(rt, readonly).map_err(|e| { + let mut system = System::load(rt).map_err(|e| { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; @@ -218,7 +216,7 @@ impl EvmContractActor { RT::Blockstore: Clone, { let input = handle_filecoin_method_input(method, codec, params); - Self::invoke_contract(rt, &input, false, None) + Self::invoke_contract(rt, &input, None) } pub fn bytecode(rt: &mut impl Runtime) -> Result { @@ -238,7 +236,7 @@ impl EvmContractActor { // access arbitrary storage keys from a contract. rt.validate_immediate_caller_is([&Address::new_id(0)])?; - System::load(rt, true)? + System::load(rt)? .get_storage(params.storage_key) .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to get storage key") } @@ -289,7 +287,7 @@ impl ActorCode for EvmContractActor { } Some(Method::InvokeContract) => { let BytesDe(params) = params.deserialize()?; - let value = Self::invoke_contract(rt, ¶ms, false, None)?; + let value = Self::invoke_contract(rt, ¶ms, None)?; Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::GetBytecode) => { @@ -300,15 +298,9 @@ impl ActorCode for EvmContractActor { let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(value)?) } - Some(Method::InvokeContractReadOnly) => { - let BytesDe(params) = params.deserialize()?; - let value = Self::invoke_contract(rt, ¶ms, true, None)?; - Ok(RawBytes::serialize(BytesSer(&value))?) - } Some(Method::InvokeContractDelegate) => { let params: DelegateCallParams = cbor::deserialize_params(params)?; - let value = - Self::invoke_contract(rt, ¶ms.input, params.readonly, Some(params.code))?; + let value = Self::invoke_contract(rt, ¶ms.input, Some(params.code))?; Ok(RawBytes::serialize(BytesSer(&value))?) } None => Err(actor_error!(unhandled_message; "Invalid method")), @@ -330,8 +322,6 @@ pub struct DelegateCallParams { /// The contract invocation parameters #[serde(with = "strict_bytes")] pub input: Vec, - /// Whether the call is within a read only (static) call context - pub readonly: bool, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 72dcb6eb9..c0e738d8b 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Error}; use cid::multihash::Code; use cid::Cid; +use fvm::SyscallResult; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; @@ -16,6 +17,7 @@ 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::receipt::Receipt; use fvm_shared::sector::{ AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo, @@ -99,6 +101,68 @@ impl MessageInfo for FvmMessage { } } +fn handle_send_result( + to: &Address, + method: MethodNum, + res: SyscallResult, +) -> Result { + match res { + Ok(ret) => { + if ret.exit_code.is_success() { + Ok(ret.return_data) + } else { + // The returned code can't be simply propagated as it may be a system exit code. + // TODO: improve propagation once we return a RuntimeError. + // Ref https://github.com/filecoin-project/builtin-actors/issues/144 + let exit_code = match ret.exit_code { + // This means the called actor did something wrong. We can't "make up" a + // reasonable exit code. + ExitCode::SYS_MISSING_RETURN + | ExitCode::SYS_ILLEGAL_INSTRUCTION + | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, + // We don't expect any other system errors. + code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, + // Otherwise, pass it through. + code => code, + }; + Err(ActorError::unchecked_with_data( + exit_code, + format!("send to {} method {} aborted with code {}", to, method, ret.exit_code), + ret.return_data, + )) + } + } + Err(err) => Err(match err { + // Some of these errors are from operations in the Runtime or SDK layer + // before or after the underlying VM send syscall. + ErrorNumber::NotFound => { + // This means that the receiving actor doesn't exist. + // TODO: we can't reasonably determine the correct "exit code" here. + actor_error!(unspecified; "receiver not found") + } + ErrorNumber::InsufficientFunds => { + // This means that the send failed because we have insufficient funds. We will + // get a _syscall error_, not an exit code, because the target actor will not + // run (and therefore will not exit). + actor_error!(insufficient_funds; "not enough funds") + } + ErrorNumber::LimitExceeded => { + // This means we've exceeded the recursion limit. + // TODO: Define a better exit code. + actor_error!(assertion_failed; "recursion limit exceeded") + } + ErrorNumber::ReadOnly => ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "attempted to mutate state while in readonly mode".into(), + ), + err => { + // We don't expect any other syscall exit codes. + actor_error!(assertion_failed; "unexpected error: {}", err) + } + }), + } +} + impl Runtime for FvmRuntime where B: Blockstore, @@ -306,64 +370,19 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - match fvm::send::send(to, method, params, value, None) { - Ok(ret) => { - if ret.exit_code.is_success() { - Ok(ret.return_data) - } else { - // The returned code can't be simply propagated as it may be a system exit code. - // TODO: improve propagation once we return a RuntimeError. - // Ref https://github.com/filecoin-project/builtin-actors/issues/144 - let exit_code = match ret.exit_code { - // This means the called actor did something wrong. We can't "make up" a - // reasonable exit code. - ExitCode::SYS_MISSING_RETURN - | ExitCode::SYS_ILLEGAL_INSTRUCTION - | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, - // We don't expect any other system errors. - code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, - // Otherwise, pass it through. - code => code, - }; - Err(ActorError::unchecked_with_data( - exit_code, - format!( - "send to {} method {} aborted with code {}", - to, method, ret.exit_code - ), - ret.return_data, - )) - } - } - Err(err) => Err(match err { - // Some of these errors are from operations in the Runtime or SDK layer - // before or after the underlying VM send syscall. - ErrorNumber::NotFound => { - // This means that the receiving actor doesn't exist. - // TODO: we can't reasonably determine the correct "exit code" here. - actor_error!(unspecified; "receiver not found") - } - ErrorNumber::InsufficientFunds => { - // This means that the send failed because we have insufficient funds. We will - // get a _syscall error_, not an exit code, because the target actor will not - // run (and therefore will not exit). - actor_error!(insufficient_funds; "not enough funds") - } - ErrorNumber::LimitExceeded => { - // This means we've exceeded the recursion limit. - // TODO: Define a better exit code. - actor_error!(assertion_failed; "recursion limit exceeded") - } - ErrorNumber::ReadOnly => ActorError::unchecked( - ExitCode::USR_READ_ONLY, - "attempted to mutate state while in readonly mode".into(), - ), - err => { - // We don't expect any other syscall exit codes. - actor_error!(assertion_failed; "unexpected error: {}", err) - } - }), + handle_send_result(to, method, fvm::send::send(to, method, params, value, None)) + } + + fn send_read_only( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + ) -> Result { + if self.in_transaction { + return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } + handle_send_result(to, method, fvm::send::send_read_only(to, method, params, None)) } fn new_actor_address(&mut self) -> Result { @@ -431,6 +450,10 @@ where fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> ! { fvm::vm::exit(code, data, msg) } + + fn read_only(&self) -> bool { + fvm::vm::read_only() + } } impl Primitives for FvmRuntime diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 605da403c..5d15e2d4c 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -189,6 +189,20 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { value: TokenAmount, ) -> Result; + /// Like [`Runtime::send`] except that neither the called actor nor any recursively called actor + /// can make any state changes or emit any events. Specifically: + /// + /// - Value transfers are rejected. + /// - Events are discarded. + /// - State changes are rejected when the called actor attempts to update its state-root. + /// - Actor deletion is forbidden. + fn send_read_only( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + ) -> Result; + /// Computes an address for a new actor. The returned address is intended to uniquely refer to /// the actor even in the event of a chain re-org (whereas an ID-address might refer to a /// different actor after messages are re-ordered). @@ -248,6 +262,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Exit the current computation with an error code and optionally data and a debugging /// message. fn exit(&self, code: u32, data: RawBytes, msg: Option<&str>) -> !; + + /// Returns true if the system is in read-only mode. + fn read_only(&self) -> bool; } /// Message information available to the actor about executing message. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index c74733271..d99cbe566 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -362,6 +362,7 @@ pub struct ExpectedMessage { pub method: MethodNum, pub params: RawBytes, pub value: TokenAmount, + pub read_only: bool, // returns from applying expectedMessage pub send_return: RawBytes, @@ -533,6 +534,66 @@ impl MockRuntime { self.delegated_addresses_source.insert(target, source); } + fn send_inner( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + read_only: bool, + ) -> Result { + self.require_in_call(); + if self.in_transaction { + return Err(actor_error!(assertion_failed; "side-effect within transaction")); + } + + assert!( + !self.expectations.borrow_mut().expect_sends.is_empty(), + "unexpected message to: {:?} method: {:?}, value: {:?}, params: {:?}", + to, + method, + value, + params + ); + + let expected_msg = self.expectations.borrow_mut().expect_sends.pop_front().unwrap(); + + assert!( + expected_msg.to == *to + && expected_msg.method == method + && expected_msg.params == params + && expected_msg.value == value + && expected_msg.read_only == read_only, + "message sent does not match expectation.\n\ + message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ + expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", + to, + method, + value, + params, + expected_msg.to, + expected_msg.method, + expected_msg.value, + expected_msg.params, + ); + + { + let mut balance = self.balance.borrow_mut(); + if value > *balance { + return Err(ActorError::unchecked( + ExitCode::SYS_SENDER_STATE_INVALID, + format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance), + )); + } + *balance -= value; + } + + match expected_msg.exit_code { + ExitCode::OK => Ok(expected_msg.send_return), + x => Err(ActorError::unchecked(x, "Expected message Fail".to_string())), + } + } + pub fn call( &mut self, method_num: MethodNum, @@ -662,6 +723,27 @@ impl MockRuntime { value, send_return, exit_code, + read_only: false, + }) + } + + #[allow(dead_code)] + pub fn expect_send_read_only( + &mut self, + to: Address, + method: MethodNum, + params: RawBytes, + send_return: RawBytes, + exit_code: ExitCode, + ) { + self.expectations.borrow_mut().expect_sends.push_back(ExpectedMessage { + to, + method, + params, + send_return, + exit_code, + value: TokenAmount::default(), + read_only: true, }) } @@ -1075,55 +1157,16 @@ impl Runtime for MockRuntime { params: RawBytes, value: TokenAmount, ) -> Result { - self.require_in_call(); - if self.in_transaction { - return Err(actor_error!(assertion_failed; "side-effect within transaction")); - } - - assert!( - !self.expectations.borrow_mut().expect_sends.is_empty(), - "unexpected message to: {:?} method: {:?}, value: {:?}, params: {:?}", - to, - method, - value, - params - ); - - let expected_msg = self.expectations.borrow_mut().expect_sends.pop_front().unwrap(); - - assert!( - expected_msg.to == *to - && expected_msg.method == method - && expected_msg.params == params - && expected_msg.value == value, - "message sent does not match expectation.\n\ - message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ - expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", - to, - method, - value, - params, - expected_msg.to, - expected_msg.method, - expected_msg.value, - expected_msg.params, - ); - - { - let mut balance = self.balance.borrow_mut(); - if value > *balance { - return Err(ActorError::unchecked( - ExitCode::SYS_SENDER_STATE_INVALID, - format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance), - )); - } - *balance -= value; - } + self.send_inner(to, method, params, value, false) + } - match expected_msg.exit_code { - ExitCode::OK => Ok(expected_msg.send_return), - x => Err(ActorError::unchecked(x, "Expected message Fail".to_string())), - } + fn send_read_only( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + ) -> Result { + self.send_inner(to, method, params, TokenAmount::default(), true) } fn new_actor_address(&mut self) -> Result { @@ -1302,6 +1345,10 @@ impl Runtime for MockRuntime { self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); std::panic::panic_any("actor exit"); } + + fn read_only(&self) -> bool { + false + } } impl Primitives for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index c054015d1..087bad0d2 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -455,6 +455,7 @@ impl<'bs> VM<'bs> { policy: &Policy::default(), subinvocations: RefCell::new(vec![]), actor_exit: RefCell::new(None), + read_only: false, }; let res = new_ctx.invoke_actor(); @@ -590,6 +591,7 @@ pub struct InvocationCtx<'invocation, 'bs> { msg: InternalMessage, allow_side_effects: bool, caller_validated: bool, + read_only: bool, policy: &'invocation Policy, subinvocations: RefCell>, actor_exit: RefCell>, @@ -608,6 +610,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { return Ok((act, a)); } }; + // Address does not yet exist, create it let is_account = match target.payload() { Payload::Secp256k1(_) | Payload::BLS(_) => true, @@ -625,6 +628,15 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { )); } }; + + // But only if we're not in read-only mode. + if self.read_only { + return Err(ActorError::unchecked( + ExitCode::USR_READ_ONLY, + format!("cannot create actor {target} in read-only mode"), + )); + } + let mut st = self.v.get_state::(INIT_ACTOR_ADDR).unwrap(); let target_id = st.map_address_to_new_id(self.v.store, target).unwrap(); let target_id_addr = Address::new_id(target_id); @@ -649,6 +661,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { policy: self.policy, subinvocations: RefCell::new(vec![]), actor_exit: RefCell::new(None), + read_only: false, }; if is_account { new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); @@ -722,6 +735,12 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { "insufficient balance to transfer".to_string(), )); } + if self.read_only { + return Err(ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "cannot transfer value in read-only mode".to_string(), + )); + } } // Load, deduct, store from actor before loading to actor to handle self-send case @@ -769,6 +788,42 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { res } + + fn send_inner( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + read_only: bool, + ) -> Result { + if !self.allow_side_effects { + return Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "Calling send is not allowed during side-effect lock".to_string(), + )); + } + + let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; + let mut new_ctx = InvocationCtx { + v: self.v, + top: self.top.clone(), + msg: new_actor_msg, + allow_side_effects: true, + caller_validated: false, + policy: self.policy, + subinvocations: RefCell::new(vec![]), + actor_exit: RefCell::new(None), + read_only, + }; + let res = new_ctx.invoke_actor(); + let invoc = new_ctx.gather_trace(res.clone()); + RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { + subinvocs.push(invoc); + subinvocs + }); + res + } } impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { @@ -804,6 +859,14 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { )); } }; + + if self.read_only { + return Err(ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "cannot send value in read-only mode".into(), + )); + } + self.v.set_actor(addr, actor); Ok(()) } @@ -948,31 +1011,16 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { params: RawBytes, value: TokenAmount, ) -> Result { - if !self.allow_side_effects { - return Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, - "Calling send is not allowed during side-effect lock".to_string(), - )); - } + self.send_inner(to, method, params, value, self.read_only) + } - let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; - let mut new_ctx = InvocationCtx { - v: self.v, - top: self.top.clone(), - msg: new_actor_msg, - allow_side_effects: true, - caller_validated: false, - policy: self.policy, - subinvocations: RefCell::new(vec![]), - actor_exit: RefCell::new(None), - }; - let res = new_ctx.invoke_actor(); - let invoc = new_ctx.gather_trace(res.clone()); - RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { - subinvocs.push(invoc); - subinvocs - }); - res + fn send_read_only( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + ) -> Result { + self.send_inner(to, method, params, TokenAmount::zero(), true) } fn get_randomness_from_tickets( @@ -1022,11 +1070,15 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { ExitCode::SYS_ASSERTION_FAILED, "actor does not exist".to_string(), )), - Some(mut act) => { + Some(mut act) if !self.read_only => { act.head = *root; self.v.set_actor(self.to(), act); Ok(()) } + _ => Err(ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "actor is read-only".to_string(), + )), } } @@ -1042,6 +1094,14 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { let ret = result?; let mut act = self.v.get_actor(self.to()).unwrap(); act.head = self.v.store.put_cbor(&st, Code::Blake2b256).unwrap(); + + if self.read_only { + return Err(ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "actor is read-only".to_string(), + )); + } + self.v.set_actor(self.to(), act); Ok(ret) } @@ -1102,6 +1162,10 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); std::panic::panic_any("actor exit"); } + + fn read_only(&self) -> bool { + self.read_only + } } impl Primitives for VM<'_> { From a382dc982f5b2afdf832ccdf27e541d3b1a0f4f2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 1 Dec 2022 20:18:08 +0200 Subject: [PATCH 148/339] EVM: thread gas limit in send (#885) * use gas limit for sends when supplied * implement send_with_gas in mock runtime and integration framework * fix read_only flag determination * fix test_vm handling of read only is send_with_gas * use U256::to_u64_saturating instead of panicky as_u64 * remove unnecessary sticky bit check * rustfmt --- .../evm/src/interpreter/instructions/call.rs | 12 ++++------ actors/evm/src/interpreter/precompiles.rs | 19 +++++++-------- actors/evm/src/interpreter/system.rs | 18 +++++++++++++++ actors/evm/src/interpreter/uints.rs | 9 ++++++++ runtime/src/runtime/fvm.rs | 23 +++++++++++++++++++ runtime/src/runtime/mod.rs | 12 ++++++++++ runtime/src/test_utils.rs | 18 +++++++++++++-- test_vm/src/lib.rs | 18 +++++++++++++-- 8 files changed, 107 insertions(+), 22 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 88f2eb831..d50a801f6 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -176,8 +176,6 @@ pub fn call_generic( ) -> Result { let ExecutionState { stack: _, memory, .. } = state; - // NOTE gas is currently ignored as FVM's send doesn't allow the caller to specify a gas - // limit (external invocation gas limit applies). This may changed in the future. let (gas, dst, value, input_offset, input_size, output_offset, output_size) = params; if system.readonly && value > U256::zero() { @@ -250,11 +248,11 @@ pub fn call_generic( }; // TODO: support IPLD codecs #758 let params = RawBytes::serialize(BytesSer(input_data))?; - if kind == CallKind::StaticCall { - system.send_read_only(&dst_addr, method, params) - } else { - system.send(&dst_addr, method, params, TokenAmount::from(&value)) - } + let value = TokenAmount::from(&value); + let gas_limit = + if !gas.is_zero() { Some(gas.to_u64_saturating()) } else { None }; + let read_only = kind == CallKind::StaticCall; + system.send_with_gas(&dst_addr, method, params, value, gas_limit, read_only) } } CallKind::DelegateCall => { diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 98176d6c5..90252974f 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -443,17 +443,14 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> let address = &bytes[send_data_size..send_data_size + address_size]; let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; - // TODO passing underlying gas into send when implemented in FVM - if ctx.is_static { - rt.send_read_only(&address, method, RawBytes::from(input_data.to_vec())) - } else { - rt.send( - &address, - method, - RawBytes::from(input_data.to_vec()), - TokenAmount::from(&ctx.value), - ) - } + rt.send_with_gas( + &address, + method, + RawBytes::from(input_data.to_vec()), + TokenAmount::from(&ctx.value), + if !ctx.gas.is_zero() { Some(ctx.gas.to_u64_saturating()) } else { None }, + ctx.is_static, + ) }; // ------ Build Output ------- diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index c854298d9..c8f722309 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -122,6 +122,24 @@ impl<'r, RT: Runtime> System<'r, RT> { self.rt.send_read_only(to, method, params) } + /// Generalized send + pub fn send_with_gas( + &mut self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + gas_limit: Option, + read_only: bool, + ) -> Result { + self.flush()?; + let result = self.rt.send_with_gas(to, method, params, value, gas_limit, read_only)?; + if !read_only { + self.reload()?; + } + Ok(result) + } + /// Flush the actor state (bytecode, nonce, and slots). pub fn flush(&mut self) -> Result<(), ActorError> { if self.saved_state_root.is_some() { diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 507309914..77c75efa0 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -63,6 +63,15 @@ impl U256 { 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 { diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index c0e738d8b..6dcb72f7f 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -385,6 +385,29 @@ where handle_send_result(to, method, fvm::send::send_read_only(to, method, params, None)) } + fn send_with_gas( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + gas_limit: Option, + read_only: bool, + ) -> Result { + if self.in_transaction { + return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); + } + handle_send_result( + to, + method, + if read_only { + fvm::send::send_read_only(to, method, params, gas_limit) + } else { + fvm::send::send(to, method, params, value, gas_limit) + }, + ) + } + fn new_actor_address(&mut self) -> Result { Ok(fvm::actor::next_actor_address()) } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 5d15e2d4c..853883866 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -203,6 +203,18 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { params: RawBytes, ) -> Result; + /// Generailizes [`Runtime::send`] and [`Runtime::send_read_only`] to allow the caller to + /// specify a gas limit. + fn send_with_gas( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + gas_limit: Option, + read_only: bool, + ) -> Result; + /// Computes an address for a new actor. The returned address is intended to uniquely refer to /// the actor even in the event of a chain re-org (whereas an ID-address might refer to a /// different actor after messages are re-ordered). diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index d99cbe566..94c2c10a9 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -540,8 +540,10 @@ impl MockRuntime { method: MethodNum, params: RawBytes, value: TokenAmount, + _gas_limit: Option, read_only: bool, ) -> Result { + // TODO gas_limit is currently ignored, what should we do about it? self.require_in_call(); if self.in_transaction { return Err(actor_error!(assertion_failed; "side-effect within transaction")); @@ -1157,7 +1159,7 @@ impl Runtime for MockRuntime { params: RawBytes, value: TokenAmount, ) -> Result { - self.send_inner(to, method, params, value, false) + self.send_inner(to, method, params, value, None, false) } fn send_read_only( @@ -1166,7 +1168,19 @@ impl Runtime for MockRuntime { method: MethodNum, params: RawBytes, ) -> Result { - self.send_inner(to, method, params, TokenAmount::default(), true) + self.send_inner(to, method, params, TokenAmount::default(), None, true) + } + + fn send_with_gas( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + gas_limit: Option, + read_only: bool, + ) -> Result { + self.send_inner(to, method, params, value, gas_limit, read_only) } fn new_actor_address(&mut self) -> Result { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 087bad0d2..3caf551aa 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -795,8 +795,10 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { method: MethodNum, params: RawBytes, value: TokenAmount, + _gas_limit: Option, read_only: bool, ) -> Result { + // TODO gas_limit is current ignored, what should we do about it? if !self.allow_side_effects { return Err(ActorError::unchecked( ExitCode::SYS_ASSERTION_FAILED, @@ -1011,7 +1013,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { params: RawBytes, value: TokenAmount, ) -> Result { - self.send_inner(to, method, params, value, self.read_only) + self.send_inner(to, method, params, value, None, self.read_only) } fn send_read_only( @@ -1020,7 +1022,19 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { method: MethodNum, params: RawBytes, ) -> Result { - self.send_inner(to, method, params, TokenAmount::zero(), true) + self.send_inner(to, method, params, TokenAmount::zero(), None, true) + } + + fn send_with_gas( + &self, + to: &Address, + method: MethodNum, + params: RawBytes, + value: TokenAmount, + gas_limit: Option, + read_only: bool, + ) -> Result { + self.send_inner(to, method, params, value, gas_limit, read_only || self.read_only) } fn get_randomness_from_tickets( From 49f927c077693eb32cab27ce0cabfcab44fb48ee Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 1 Dec 2022 10:50:38 -0800 Subject: [PATCH 149/339] EVM: fix EXTCODE_ ops (#870) * wip fixes * cleanup implementation * return 0 for non existient address in extcodesize * use enum instead of result result maddness * fmt & cleanup some logic * update tests for the remaining two ext opcodes * redo logic to make things a bit more clearn * fix bugs & keep bytecode logic seperate, verify expectations before asserting for better debugging help * add another test case for ext op * use closure for early return * nit: reduce indentation * fix: formatting * remove unnecessary closure * fix: make delegatecall behave more like the EVM - address todo - use the new get_cid_type * fix some review nits Co-authored-by: Steven Allen --- .../evm/src/interpreter/instructions/call.rs | 38 ++- .../evm/src/interpreter/instructions/ext.rs | 87 ++++-- actors/evm/tests/asm.rs | 9 + actors/evm/tests/create.rs | 10 - actors/evm/tests/ext_opcodes.rs | 268 ++++++++++++++---- actors/evm/tests/util.rs | 27 +- 6 files changed, 326 insertions(+), 113 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index d50a801f6..a4a7bff0f 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -5,6 +5,8 @@ use fvm_shared::{address::Address, METHOD_SEND}; use crate::interpreter::precompiles::PrecompileContext; +use super::ext::{get_cid_type, get_evm_bytecode_cid, ContractType}; + use { super::memory::{copy_to_memory, get_memory_region}, crate::interpreter::address::EthAddress, @@ -255,22 +257,28 @@ pub fn call_generic( system.send_with_gas(&dst_addr, method, params, value, gas_limit, read_only) } } - CallKind::DelegateCall => { - // first invoke GetBytecode to get the code CID from the target - let code = crate::interpreter::instructions::ext::get_evm_bytecode_cid( - system.rt, dst, - )?; - - // and then invoke self with delegate; readonly context is sticky - let params = DelegateCallParams { code, input: input_data.to_vec() }; - system.send( - &system.rt.message().receiver(), - Method::InvokeContractDelegate as u64, - RawBytes::serialize(¶ms)?, - TokenAmount::from(&value), - ) - } + CallKind::DelegateCall => match get_cid_type(system.rt, dst) { + ContractType::EVM(dst_addr) => { + // If we're calling an actual EVM actor, get it's code. + let code = get_evm_bytecode_cid(system.rt, &dst_addr)?; + // and then invoke self with delegate; readonly context is sticky + let params = DelegateCallParams { code, input: input_data.into() }; + system.send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + RawBytes::serialize(¶ms)?, + TokenAmount::from(&value), + ) + } + // 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(RawBytes::default()), + // If we're calling a "native" actor, always revert. + ContractType::Native(_) => { + Err(ActorError::forbidden("cannot delegate-call to native actors".into())) + } + }, CallKind::CallCode => Err(ActorError::unchecked( EVM_CONTRACT_EXECUTION_ERROR, "unsupported opcode".to_string(), diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index e5234ca06..c448d8754 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -12,18 +12,22 @@ use { fil_actors_runtime::runtime::Runtime, }; -#[inline] pub fn extcodesize( _state: &mut ExecutionState, system: &System, addr: U256, ) -> Result { - // TODO we're fetching the entire block here just to get its size. We should instead use + // 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 = get_evm_bytecode_cid(system.rt, addr) - .and_then(|cid| get_evm_bytecode(system.rt, &cid)) - .map(|bytecode| bytecode.len())?; + let len = match get_cid_type(system.rt, addr) { + ContractType::EVM(addr) => { + get_evm_bytecode(system.rt, &addr).map(|bytecode| bytecode.len())? + } + ContractType::Native(_) => 1, + // account and not found are flattened to 0 size + _ => 0, + }; Ok(len.into()) } @@ -33,8 +37,16 @@ pub fn extcodehash( system: &System, addr: U256, ) -> Result { - let cid = get_evm_bytecode_cid(system.rt, addr)?; - let digest = cid.hash().digest(); + let addr = match get_cid_type(system.rt, addr) { + ContractType::EVM(a) => Ok(a), + _ => Err(StatusCode::InvalidArgument( + "Cannot invoke EXTCODEHASH for non-EVM actor.".to_string(), + )), + }?; + let bytecode_cid = get_evm_bytecode_cid(system.rt, &addr)?; + + let digest = bytecode_cid.hash().digest(); + // Take the first 32 bytes of the Multihash let digest_len = digest.len().min(32); Ok(digest[..digest_len].into()) @@ -48,40 +60,55 @@ pub fn extcodecopy( data_offset: U256, size: U256, ) -> Result<(), StatusCode> { - let bytecode = - get_evm_bytecode_cid(system.rt, addr).and_then(|cid| get_evm_bytecode(system.rt, &cid))?; + let bytecode = match get_cid_type(system.rt, addr) { + ContractType::EVM(addr) => get_evm_bytecode(system.rt, &addr)?, + ContractType::NotFound | ContractType::Account => 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) } -pub fn get_evm_bytecode_cid(rt: &impl Runtime, addr: U256) -> Result { - let addr: EthAddress = addr.into(); - let addr: Address = addr.try_into()?; - // TODO: just return none in most of these cases? - let actor_id = rt.resolve_address(&addr).ok_or_else(|| { - StatusCode::InvalidArgument("failed to resolve address".to_string()) - // TODO better error code - })?; +#[derive(Debug)] +pub enum ContractType { + /// EVM Address and the CID of the actor (not the bytecode) + EVM(Address), + Native(Cid), + Account, + NotFound, +} - let evm_cid = rt.get_code_cid_for_type(Type::EVM); - let target_cid = rt.get_actor_code_cid(&actor_id); +/// Resolves an address to the address type +pub fn get_cid_type(rt: &impl Runtime, addr: U256) -> ContractType { + let addr: EthAddress = addr.into(); - if Some(evm_cid) != target_cid { - return Err(StatusCode::InvalidArgument( - "cannot invoke EXTCODESIZE for non-EVM actor".to_string(), - )); // TODO better error code - } + addr.try_into() + .ok() // into filecoin address + .and_then(|addr| 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 embryos are accounts + Some(Type::Account | Type::Embryo) => ContractType::Account, + Some(Type::EVM) => ContractType::EVM(Address::new_id(id)), + // remaining builtin actors are native + _ => ContractType::Native(cid), + }) + .unwrap_or(ContractType::NotFound) +} - let cid = rt - .send(&addr, crate::Method::GetBytecode as u64, Default::default(), TokenAmount::zero())? - .deserialize()?; - Ok(cid) +pub fn get_evm_bytecode_cid(rt: &impl Runtime, addr: &Address) -> Result { + Ok(rt + .send(addr, crate::Method::GetBytecode as u64, Default::default(), TokenAmount::zero())? + .deserialize()?) } -pub fn get_evm_bytecode(rt: &impl Runtime, cid: &Cid) -> Result, StatusCode> { +pub fn get_evm_bytecode(rt: &impl Runtime, addr: &Address) -> Result, StatusCode> { + let cid = get_evm_bytecode_cid(rt, addr)?; let raw_bytecode = rt .store() - .get(cid) // TODO this is inefficient; should call stat here. + .get(&cid) // TODO this is inefficient; should call stat here. .map_err(|e| StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)))? .ok_or_else(|| ActorError::not_found("bytecode block not found".to_string()))?; Ok(raw_bytecode) diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index b661b525b..c8b03d65b 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -23,6 +23,15 @@ const PRELUDE: &str = r#" 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)] diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index 5fd8135b7..9c69c4e41 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -16,16 +16,6 @@ mod util; pub fn magic_precompile_contract() -> Vec { let init = ""; let body = r#" - - -%macro return_stack_word() - push1 0x00 - mstore - push1 0x20 # always return a full word - push1 0x00 - return -%end - # magic value, used as initcode push16 0x666F6F206261722062617A20626F7879 # foo bar baz boxy push2 0x0100 # offset of input data diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 03a015407..7eadf5cdf 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -10,51 +10,133 @@ use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::{IDENTITY_HASH, IPLD_RAW}; +use lazy_static::lazy_static; mod util; +lazy_static! { + pub static ref DUMMY_ACTOR_CODE_ID: Cid = + Cid::new_v1(IPLD_RAW, Multihash::wrap(IDENTITY_HASH, b"foobarboxy").unwrap()); +} + #[test] fn test_extcodesize() { let bytecode = { let init = ""; - let body = r#" - # get code size of address f088 - push20 0xff00000000000000000000000000000000000088 - extcodesize - # store size at location 0x00 - push1 0x00 - mstore - # return 0x00..0x20 - push1 0x20 - push1 0x00 - return - "#; + 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_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() +"#; 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 target = FILAddress::new_id(0x88); - rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + let evm_contract = FILAddress::new_id(0x88); + rt.set_address_actor_type(evm_contract, *EVM_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(); + // 0x89 is a native actor + let native_actor = FILAddress::new_id(0x89); + rt.set_address_actor_type(native_actor, *DUMMY_ACTOR_CODE_ID); - rt.expect_send( - target, - evm::Method::GetBytecode as u64, - Default::default(), - TokenAmount::zero(), - RawBytes::serialize(&bytecode_cid).unwrap(), - ExitCode::OK, - ); + // 0x0101 is an EVM EOA account + let evm_account = FILAddress::new_id(0x0101); + // TODO this is part of the account abstraction hack, where embryos are magically accounts + rt.set_address_actor_type(evm_account, *EMBRYO_ACTOR_CODE_ID); - let result = util::invoke_contract(&mut rt, &[]); - assert_eq!(U256::from_big_endian(&result), U256::from(0x04)); + // 0x0102 is a native account + let native_account = FILAddress::new_id(0x0102); + rt.set_address_actor_type(native_account, *ACCOUNT_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(), + RawBytes::serialize(&bytecode_cid).unwrap(), + ExitCode::OK, + ); + + 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(); + } } #[test] @@ -62,17 +144,26 @@ fn test_extcodehash() { let bytecode = { let init = ""; let body = r#" - # get code hash of address f088 - push20 0xff00000000000000000000000000000000000088 - extcodehash - # store size at location 0x00 - push1 0x00 - mstore - # return 0x00..0x20 - push1 0x20 - push1 0x00 - return - "#; +%dispatch_begin() +%dispatch(0x00, evm_contract) +%dispatch(0x01, native_actor) +%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() + +"#; asm::new_contract("extcodehash", init, body).unwrap() }; @@ -80,15 +171,19 @@ fn test_extcodehash() { let mut rt = util::construct_and_verify(bytecode); // 0x88 is an EVM actor - let target = FILAddress::new_id(0x88); - rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + let evm_target = FILAddress::new_id(0x88); + rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); + + // 0x88 is an EVM 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("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); rt.expect_send( - target, + evm_target, evm::Method::GetBytecode as u64, Default::default(), TokenAmount::zero(), @@ -96,8 +191,18 @@ fn test_extcodehash() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, &[]); + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); + rt.verify(); assert_eq!(U256::from_big_endian(&result), U256::from(&bytecode_cid.hash().digest()[..32])); + rt.reset(); + + util::invoke_contract_expect_err( + &mut rt, + &util::dispatch_num_word(1), + evm::interpreter::StatusCode::InvalidArgument( + "Cannot invoke EXTCODEHASH for non-EVM actor.".to_string(), + ), + ); } #[test] @@ -105,15 +210,49 @@ fn test_extcodecopy() { let bytecode = { let init = ""; let body = r#" - push1 0xff - push1 0x00 - push1 0x00 - push20 0xff00000000000000000000000000000000000088 - extcodecopy - # return 0x00..0x04 - push1 0x04 - push1 0x00 - return + +%dispatch_begin() +%dispatch(0x00, evm_contract) +%dispatch(0x01, native_actor) +%dispatch(0x02, invalid_address) +%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 + "#; asm::new_contract("extcodecopy", init, body).unwrap() @@ -122,8 +261,12 @@ fn test_extcodecopy() { let mut rt = util::construct_and_verify(bytecode); // 0x88 is an EVM actor - let target = FILAddress::new_id(0x88); - rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + 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(); @@ -131,7 +274,7 @@ fn test_extcodecopy() { rt.store.put_keyed(&bytecode_cid, other_bytecode.as_slice()).unwrap(); rt.expect_send( - target, + evm_target, evm::Method::GetBytecode as u64, Default::default(), TokenAmount::zero(), @@ -139,6 +282,19 @@ fn test_extcodecopy() { ExitCode::OK, ); - let result = util::invoke_contract(&mut rt, &[]); + 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)); } diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index ebb3d2dde..6f820ca91 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,6 +1,8 @@ -use evm::interpreter::address::EthAddress; +use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; -use fil_actors_runtime::{runtime::builtins::Type, test_utils::*, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{ + runtime::builtins::Type, test_utils::*, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, +}; use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; use fvm_shared::address::Address; @@ -59,3 +61,24 @@ pub fn invoke_contract(rt: &mut MockRuntime, input_data: &[u8]) -> Vec { .unwrap(); res } + +#[allow(dead_code)] +pub fn invoke_contract_expect_err(rt: &mut MockRuntime, input_data: &[u8], expect: StatusCode) { + rt.expect_validate_caller_any(); + let err = rt + .call::( + evm::Method::InvokeContract as u64, + &RawBytes::serialize(BytesSer(input_data)).unwrap(), + ) + .expect_err(&format!("expected contract to fail with {}", expect)); + rt.verify(); + // REMOVEME so this is jank... (just copies err creation from execute in lib.rs) + assert_eq!(err, ActorError::unspecified(format!("EVM execution error: {expect:?}"))) +} + +#[allow(dead_code)] +pub fn dispatch_num_word(method_num: u8) -> [u8; 32] { + let mut word = [0u8; 32]; + word[3] = method_num; + word +} From 0f8394834e3b14a6bb1d4451dba43d9a5ae1ca56 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Dec 2022 11:10:11 -0800 Subject: [PATCH 150/339] fix: save state before callactor (#886) Unfortunately, this means we need to pass the "system" to the precompiles, which means we need an unsafe dance to pass a mutable pointer through. But it's not _too_ bad. --- .../evm/src/interpreter/instructions/call.rs | 2 +- actors/evm/src/interpreter/precompiles.rs | 214 ++++++++++++------ 2 files changed, 143 insertions(+), 73 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index a4a7bff0f..8e83e9106 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -203,7 +203,7 @@ pub fn call_generic( value, }; - match precompiles::Precompiles::call_precompile(system.rt, dst, input_data, context) + match precompiles::Precompiles::call_precompile(system, dst, input_data, context) .map_err(StatusCode::from) { Ok(return_data) => (1, return_data), diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index 90252974f..f5d3edde1 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -1,8 +1,8 @@ use std::{borrow::Cow, convert::TryInto, marker::PhantomData, slice::ChunksExact}; -use super::{StatusCode, U256}; +use super::{StatusCode, System, U256}; -use fil_actors_runtime::runtime::{Primitives, Runtime}; +use fil_actors_runtime::runtime::Runtime; use fvm_ipld_encoding::RawBytes; use fvm_shared::{ address::Address, @@ -255,12 +255,31 @@ pub struct PrecompileContext { } // really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy -pub type PrecompileFn = fn(&RT, &[u8], PrecompileContext) -> PrecompileResult; +type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec /// Generates a list of precompile smart contracts, index + 1 is the address (another option is to make an enum) const fn gen_precompiles() -> [PrecompileFn; 14] { - [ + macro_rules! precompiles { + ($($precompile:ident,)*) => { + mod trampolines { + use fil_actors_runtime::runtime::Runtime; + use crate::System; + use super::{PrecompileContext, PrecompileResult}; + $( + #[inline(always)] + pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { + super::$precompile(&mut *s, inp, ctx) + } + )* + } + [ + $(trampolines::$precompile,)* + ] + } + } + + precompiles! { ec_recover, // ecrecover 0x01 sha256, // SHA2-256 0x02 ripemd160, // ripemd160 0x03 @@ -276,7 +295,7 @@ const fn gen_precompiles() -> [PrecompileFn; 14] { get_actor_code_cid, // get code cid 0x0c get_randomness, // rand 0x0d call_actor, // call_actor 0x0e - ] + } } pub struct Precompiles(PhantomData); @@ -291,12 +310,12 @@ impl Precompiles { // Precompile Context will be flattened to None if not calling the call_actor precompile pub fn call_precompile( - runtime: &RT, + system: &mut System, precompile_addr: U256, input: &[u8], context: PrecompileContext, ) -> PrecompileResult { - Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](runtime, input, context) + unsafe { Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](system, input, context) } } #[inline] @@ -320,13 +339,13 @@ fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u /// Read right padded BE encoded low u64 ID address from a u256 word /// returns encoded CID or an empty array if actor not found fn get_actor_code_cid( - rt: &RT, + system: &mut System, input: &[u8], _: PrecompileContext, ) -> PrecompileResult { let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); let id = Parameter::::try_from(&id_bytes)?.0; - Ok(rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) + Ok(system.rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) } /// Params: @@ -343,7 +362,11 @@ fn get_actor_code_cid( /// /// Returns empty array if invalid randomness type /// Errors if unable to fetch randomness -fn get_randomness(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn get_randomness( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let mut input_params = U256Reader::new(input); #[derive(num_derive::FromPrimitive)] @@ -363,10 +386,12 @@ fn get_randomness(rt: &RT, input: &[u8], _: PrecompileContext) -> P let entropy = read_right_pad(input_params.remaining_slice(), entropy_len as usize); let randomness = match randomness_type { - Some(RandomnessType::Chain) => rt + Some(RandomnessType::Chain) => system + .rt .user_get_randomness_from_chain(personalization, rand_epoch, &entropy) .map(|a| a.to_vec()), - Some(RandomnessType::Beacon) => rt + Some(RandomnessType::Beacon) => system + .rt .user_get_randomness_from_beacon(personalization, rand_epoch, &entropy) .map(|a| a.to_vec()), None => Ok(Vec::new()), @@ -377,11 +402,15 @@ fn get_randomness(rt: &RT, input: &[u8], _: PrecompileContext) -> P /// Read BE encoded low u64 ID address from a u256 word /// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found -fn lookup_address(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn lookup_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let mut id_bytes = U256Reader::new(input); let id = id_bytes.next_param_padded::()?; - let address = rt.lookup_address(id); + let address = system.rt.lookup_address(id); let ab = match address { Some(a) => a.to_bytes(), None => Vec::new(), @@ -392,7 +421,11 @@ fn lookup_address(rt: &RT, input: &[u8], _: PrecompileContext) -> P /// Reads a FIL encoded address /// Resolves a FIL encoded address into an ID address /// returns BE encoded u64 or empty array if nothing found -fn resolve_address(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn resolve_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let mut input_params = U256Reader::new(input); let len = input_params.next_param_padded::()? as usize; @@ -400,7 +433,7 @@ fn resolve_address(rt: &RT, input: &[u8], _: PrecompileContext) -> Ok(o) => o, Err(_) => return Ok(Vec::new()), }; - Ok(rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) + Ok(system.rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) } /// Errors: @@ -416,7 +449,11 @@ fn resolve_address(rt: &RT, input: &[u8], _: PrecompileContext) -> /// - negative values are system errors /// - positive are user errors (from the called actor) /// - 0 is success -pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> PrecompileResult { +pub fn call_actor( + system: &mut System, + input: &[u8], + ctx: PrecompileContext, +) -> PrecompileResult { // ----- Input Parameters ------- let mut input_params = U256Reader::new(input); @@ -443,7 +480,7 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> let address = &bytes[send_data_size..send_data_size + address_size]; let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; - rt.send_with_gas( + system.send_with_gas( &address, method, RawBytes::from(input_data.to_vec()), @@ -498,7 +535,11 @@ pub fn call_actor(rt: &RT, input: &[u8], ctx: PrecompileContext) -> // ---------------- Normal EVM Precompiles ------------------ /// recover a secp256k1 pubkey from a hash, recovery byte, and a signature -fn ec_recover(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn ec_recover( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let mut input_params = U256Reader::new(input); let hash: [u8; SECP_SIG_MESSAGE_HASH_SIZE] = input_params.next_padded(); @@ -535,36 +576,48 @@ fn ec_recover(rt: &RT, input: &[u8], _: PrecompileContext) -> Pr sig[32..64].copy_from_slice(&s); sig[64] = v; - let pubkey = if let Ok(key) = rt.recover_secp_public_key(&hash, &sig) { + let pubkey = if let Ok(key) = system.rt.recover_secp_public_key(&hash, &sig) { key } else { return Ok(Vec::new()); }; - let mut address = rt.hash(SupportedHashes::Keccak256, &pubkey[1..]); + let mut address = system.rt.hash(SupportedHashes::Keccak256, &pubkey[1..]); address[..12].copy_from_slice(&[0u8; 12]); Ok(address) } /// hash with sha2-256 -fn sha256(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { - Ok(rt.hash(SupportedHashes::Sha2_256, input)) +fn sha256( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + Ok(system.rt.hash(SupportedHashes::Sha2_256, input)) } /// hash with ripemd160 -fn ripemd160(rt: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { - Ok(rt.hash(SupportedHashes::Ripemd160, input)) +fn ripemd160( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + Ok(system.rt.hash(SupportedHashes::Ripemd160, input)) } /// data copy -fn identity(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn identity( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { Ok(Vec::from(input)) } // https://eips.ethereum.org/EIPS/eip-198 /// modulus exponent a number -fn modexp(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn modexp(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { let input = read_right_pad(input, 96); // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. @@ -629,7 +682,7 @@ fn curve_to_vec(curve: G1) -> Vec { } /// add 2 points together on an elliptic curve -fn ec_add(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn ec_add(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { let mut input_params: PaddedChunks = PaddedChunks::new(input); let point1 = input_params.next_param_padded()?; let point2 = input_params.next_param_padded()?; @@ -638,7 +691,7 @@ fn ec_add(_: &RT, input: &[u8], _: PrecompileContext) -> Precomp } /// multiply a point on an elliptic curve by a scalar value -fn ec_mul(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn ec_mul(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { let input = read_right_pad(input, 96); let mut input_params: PaddedChunks = PaddedChunks::new(&input); let point = input_params.next_param_padded()?; @@ -652,7 +705,11 @@ fn ec_mul(_: &RT, input: &[u8], _: PrecompileContext) -> Precomp } /// pairs multple groups of twisted bn curves -fn ec_pairing(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn ec_pairing( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { let mut i_in = U256Reader::new(input); @@ -710,7 +767,11 @@ fn ec_pairing(_: &RT, input: &[u8], _: PrecompileContext) -> Pre } /// https://eips.ethereum.org/EIPS/eip-152 -fn blake2f(_: &RT, input: &[u8], _: PrecompileContext) -> PrecompileResult { +fn blake2f( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { if input.len() != 213 { return Err(PrecompileError::IncorrectInputSize); } @@ -784,7 +845,8 @@ mod tests { #[test] fn bn_recover() { - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let input = &hex!( "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) @@ -795,7 +857,7 @@ mod tests { ); let expected = hex!("0000000000000000000000007156526fbd7a3c72969b54f64e42c10fbb768c8a"); - let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -806,7 +868,7 @@ mod tests { "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s ); // wrong signature - let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); let input = &hex!( @@ -817,7 +879,7 @@ mod tests { "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s ); // invalid recovery byte - let res = ec_recover(&rt, input, PrecompileContext::default()).unwrap(); + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); } @@ -826,10 +888,11 @@ mod tests { use super::sha256 as hash; let input = "foo bar baz boxy".as_bytes(); - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let expected = hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); - let res = hash(&rt, input, PrecompileContext::default()).unwrap(); + let res = hash(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); } @@ -838,10 +901,11 @@ mod tests { use super::ripemd160 as hash; let input = "foo bar baz boxy".as_bytes(); - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let expected = hex!("4cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); - let res = hash(&rt, input, PrecompileContext::default()).unwrap(); + let res = hash(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); } @@ -856,10 +920,11 @@ mod tests { "0A" // mod ); - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let expected = hex!("08"); - let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -871,7 +936,7 @@ mod tests { "012345678910" // mod ); let expected = hex!("00358eac8f30"); // left padding & 230026940208 - let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); + 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] @@ -884,7 +949,7 @@ mod tests { "03" // mod ); // input smaller than expected - let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); let input = &hex!( @@ -895,20 +960,22 @@ mod tests { "09" // exp ); // no mod is invalid - let res = modexp(&rt, input, PrecompileContext::default()).unwrap(); + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(res, Vec::new()); } // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { use super::{GroupError, MockRuntime}; - use crate::interpreter::precompiles::{ - ec_add, ec_mul, ec_pairing, PrecompileContext, PrecompileError, + use crate::interpreter::{ + precompiles::{ec_add, ec_mul, ec_pairing, PrecompileContext, PrecompileError}, + System, }; #[test] fn bn_add() { - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let input = hex::decode( "\ @@ -924,7 +991,7 @@ mod tests { 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", ) .unwrap(); - let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // zero sum test let input = hex::decode( @@ -941,7 +1008,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // no input test @@ -952,7 +1019,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_add(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -963,13 +1030,14 @@ mod tests { 1111111111111111111111111111111111111111111111111111111111111111", ) .unwrap(); - let res = ec_add(&rt, &input, PrecompileContext::default()); + let res = ec_add(&mut system, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } #[test] fn bn_mul() { - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let input = hex::decode( "\ @@ -984,7 +1052,7 @@ mod tests { 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", ) .unwrap(); - let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // out of gas test @@ -995,7 +1063,7 @@ mod tests { 0200000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &vec![0; 64]); // no input test @@ -1006,7 +1074,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -1016,13 +1084,14 @@ mod tests { 0f00000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&rt, &input, PrecompileContext::default()); + let res = ec_mul(&mut system, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); } #[test] fn bn_pair() { - let rt = MockRuntime::default(); + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); let input = hex::decode( "\ @@ -1045,7 +1114,7 @@ mod tests { hex::decode("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(); - let res = ec_pairing(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // out of gas test @@ -1065,14 +1134,14 @@ mod tests { 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", ) .unwrap(); - let res = ec_pairing(&rt, &input, PrecompileContext::default()).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(&rt, &input, PrecompileContext::default()).unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // point not on curve fail let input = hex::decode( @@ -1085,7 +1154,7 @@ mod tests { 1111111111111111111111111111111111111111111111111111111111111111", ) .unwrap(); - let res = ec_pairing(&rt, &input, PrecompileContext::default()); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); // invalid input length let input = hex::decode( @@ -1096,7 +1165,7 @@ mod tests { ", ) .unwrap(); - let res = ec_pairing(&rt, &input, PrecompileContext::default()); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()); assert!(matches!(res, Err(PrecompileError::IncorrectInputSize))); } } @@ -1105,7 +1174,8 @@ mod tests { #[test] fn blake2() { use super::blake2f; - let rt = MockRuntime::default(); + 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) { @@ -1128,7 +1198,7 @@ mod tests { // T0 invalid input len assert!(matches!( - blake2f(&rt, &[], PrecompileContext::default()), + blake2f(&mut system, &[], PrecompileContext::default()), Err(PrecompileError::IncorrectInputSize) )); @@ -1142,7 +1212,7 @@ mod tests { "02" ); assert!(matches!( - blake2f(&rt, input, PrecompileContext::default()), + blake2f(&mut system, input, PrecompileContext::default()), Err(PrecompileError::IncorrectInputSize) )); @@ -1156,7 +1226,7 @@ mod tests { "02" ); assert!(matches!( - blake2f(&rt, input, PrecompileContext::default()), + blake2f(&mut system, input, PrecompileContext::default()), Err(PrecompileError::IncorrectInputSize) )); @@ -1170,7 +1240,7 @@ mod tests { "02" ); assert!(matches!( - blake2f(&rt, input, PrecompileContext::default()), + blake2f(&mut system, input, PrecompileContext::default()), Err(PrecompileError::IncorrectInputSize) )); @@ -1187,7 +1257,7 @@ mod tests { "01" ); assert!( - matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) ); // T5 @@ -1201,7 +1271,7 @@ mod tests { "01" ); assert!( - matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) ); // T6 @@ -1215,7 +1285,7 @@ mod tests { "00" ); assert!( - matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) ); // T7 @@ -1229,7 +1299,7 @@ mod tests { "01" ); assert!( - matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) ); // T8 @@ -1248,7 +1318,7 @@ mod tests { "01" ); assert!( - matches!(blake2f(&rt, input, PrecompileContext::default()), Ok(v) if v == expected) + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) ); } } From dd76d3ead3cf039814b5b5efda5232ab7d530f16 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 1 Dec 2022 21:37:01 +0200 Subject: [PATCH 151/339] EVM: thread gas go delegatecall (#888) thread gas go delegatecall --- actors/evm/src/interpreter/instructions/call.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 8e83e9106..4cca99ad6 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -264,11 +264,13 @@ pub fn call_generic( // and then invoke self with delegate; readonly context is sticky let params = DelegateCallParams { code, input: input_data.into() }; - system.send( + system.send_with_gas( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, RawBytes::serialize(¶ms)?, TokenAmount::from(&value), + if !gas.is_zero() { Some(gas.to_u64_saturating()) } else { None }, + system.readonly, ) } // If we're calling an account or a non-existent actor, return nothing because From 0509cdedcdf57912455fe9d09503fa765854d61c Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 1 Dec 2022 22:15:33 +0200 Subject: [PATCH 152/339] EVM: further dispatch tweaks (#890) * use while insteas of loop * remove unnecessary bounde checking * fix typo * rustfmt --- actors/evm/src/interpreter/execution.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 2bdf434d4..d45b0656e 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -154,13 +154,11 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } pub fn execute(&mut self) -> Result<(), StatusCode> { - loop { - if self.pc >= self.bytecode.len() { - break; + while self.pc < self.bytecode.len() { + unsafe { + self.step(); } - self.step(); - if self.exit.is_some() { break; } @@ -173,10 +171,12 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } } - fn step(&mut self) { - let op = OpCode::try_from(self.bytecode[self.pc]); + unsafe fn step(&mut self) { + // Note: safe because we check bounds in execute (only caller) + let op = OpCode::try_from(*self.bytecode.get_unchecked(self.pc)); match op { - Ok(op) => Self::JMPTABLE[op as usize](self), + // Note: safe because u8 index + Ok(op) => Self::JMPTABLE.get_unchecked(op as usize)(self), Err(err) => self.exit = Some(err), } } From 77f6e8f9be74473fcbc82977299a77abe1a65656 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Dec 2022 12:43:49 -0800 Subject: [PATCH 153/339] feat: further optimize instruction dispatch (#891) 1. Lift the "is none" check into the loop. This is actually _slower_ for short programs, but faster over the long-run. I measured that by moving the check _above_ the step operation and determining that it was slower. 2. Remove the unsafe and inline. 1. Inlining is faster overall (at least in terms of gas usage). 2. Inlining lets rust elide the bounds checks (no difference in gas usage). 3. Don't bother checking instructions. Instead, we just dispatch and let run the "undefined instruction" function on undefined instructions. It gives the same result, but saves some time. --- actors/evm/src/interpreter/execution.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index d45b0656e..91722c58d 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -154,14 +154,8 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } pub fn execute(&mut self) -> Result<(), StatusCode> { - while self.pc < self.bytecode.len() { - unsafe { - self.step(); - } - - if self.exit.is_some() { - break; - } + while self.exit.is_none() && self.pc < self.bytecode.len() { + self.step(); } match &self.exit { @@ -171,14 +165,10 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } } - unsafe fn step(&mut self) { - // Note: safe because we check bounds in execute (only caller) - let op = OpCode::try_from(*self.bytecode.get_unchecked(self.pc)); - match op { - // Note: safe because u8 index - Ok(op) => Self::JMPTABLE.get_unchecked(op as usize)(self), - Err(err) => self.exit = Some(err), - } + #[inline(always)] + fn step(&mut self) { + let op = self.bytecode[self.pc]; + Self::JMPTABLE[op as usize](self) } def_opcodes! { From 68a2b1b4f85cc8560b8beb751993ab96d58d4b71 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Dec 2022 18:24:30 -0800 Subject: [PATCH 154/339] fix: make some EVM math a bit more robust (#892) 1. Avoid `as_usize` entirely. 2. Add some more explicit casts and overflow checking. I still hate it, but I now hate it a bit less. I'd love to get rid of the memory region abstraction, or replace it with something a little less brittle. fixes https://github.com/filecoin-project/ref-fvm/issues/1185 --- .../evm/src/interpreter/instructions/call.rs | 22 +++--- .../src/interpreter/instructions/control.rs | 8 +- .../src/interpreter/instructions/memory.rs | 78 ++++++++----------- 3 files changed, 48 insertions(+), 60 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 4cca99ad6..7cac8065a 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -39,17 +39,17 @@ pub fn calldataload( index: U256, ) -> Result { let input_len = state.input_data.len(); - Ok(if index > U256::from(input_len) { - U256::zero() - } else { - let index_usize = index.as_usize(); - let end = core::cmp::min(index_usize + 32, input_len); - - let mut data = [0; 32]; - data[..end - index_usize].copy_from_slice(&state.input_data[index_usize..end]); - - U256::from_big_endian(&data) - }) + Ok(index + .try_into() + .ok() + .filter(|&start| start < input_len) + .map(|start: usize| { + let end = core::cmp::min(start.saturating_add(32usize), input_len); + let mut data = [0; 32]; + data[..end - start].copy_from_slice(&state.input_data[start..end]); + U256::from_big_endian(&data) + }) + .unwrap_or_default()) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 59f42d52f..92565f975 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -42,10 +42,10 @@ pub fn returndatacopy( let region = get_memory_region(&mut state.memory, mem_index, size) .map_err(|_| StatusCode::InvalidMemoryAccess)?; - if input_index > U256::from(state.return_data.len()) { + let src = input_index.try_into().map_err(|_| StatusCode::InvalidMemoryAccess)?; + if src > state.return_data.len() { return Err(StatusCode::InvalidMemoryAccess); } - let src = input_index.as_usize(); if src + region.as_ref().map(|r| r.size.get()).unwrap_or(0) > state.return_data.len() { return Err(StatusCode::InvalidMemoryAccess); @@ -61,7 +61,7 @@ pub fn returndatacopy( #[inline] pub fn jump(bytecode: &Bytecode, dest: U256) -> Result, StatusCode> { - let dst = dest.as_usize(); + let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; if !bytecode.valid_jump_destination(dst) { return Err(StatusCode::BadJumpDestination); } @@ -71,7 +71,7 @@ pub fn jump(bytecode: &Bytecode, dest: U256) -> Result, StatusCode #[inline] pub fn jumpi(bytecode: &Bytecode, dest: U256, test: U256) -> Result, StatusCode> { if !test.is_zero() { - let dst = dest.as_usize(); + let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; if !bytecode.valid_jump_destination(dst) { return Err(StatusCode::BadJumpDestination); } diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 82318d766..f974a0a50 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -16,56 +16,43 @@ pub struct MemoryRegion { pub size: NonZeroUsize, } -/// Returns number of words what would fit to provided number of bytes, -/// i.e. it rounds up the number bytes to number of words. #[inline] -pub fn num_words(size_in_bytes: usize) -> usize { - (size_in_bytes + (WORD_SIZE - 1)) / WORD_SIZE -} - -#[inline] -fn grow_memory(mem: &mut Memory, new_size: usize) -> Result<(), ()> { - let new_words = num_words(new_size); - mem.grow((new_words * WORD_SIZE) as usize); - Ok(()) -} - -#[inline] -fn get_memory_region_u64( - mem: &mut Memory, - offset: U256, - size: NonZeroUsize, -) -> Result { - if offset.bits() >= 32 { - return Err(()); +fn grow_memory(mem: &mut Memory, mut new_size: usize) -> Result<(), ()> { + // Align to the next u256. + // Guaranteed to not overflow. + let alignment = new_size % WORD_SIZE; + if alignment > 0 { + new_size += WORD_SIZE - alignment; } - - let offset_usize = offset.as_usize(); - let new_size = offset_usize + size.get(); - let current_size = mem.len(); - if new_size > current_size { - grow_memory(mem, new_size)?; - } - - Ok(MemoryRegion { offset: offset_usize, size }) + mem.grow(new_size); + Ok(()) } #[inline] #[allow(clippy::result_unit_err)] pub fn get_memory_region( mem: &mut Memory, - offset: U256, - size: U256, + offset: impl TryInto, + size: impl TryInto, ) -> Result, ()> { - if size.is_zero() { + // 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(|_| ())?; + if size == 0 { return Ok(None); } + let offset: u32 = offset.try_into().map_err(|_| ())?; + let new_size: u32 = offset.checked_add(size).ok_or(())?; - if size.bits() >= 32 { - return Err(()); + let current_size = mem.len(); + if new_size as usize > current_size { + grow_memory(mem, new_size as usize)?; } - get_memory_region_u64(mem, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some) + Ok(Some(MemoryRegion { + offset: offset as usize, + size: unsafe { NonZeroUsize::new_unchecked(size as usize) }, + })) } pub fn copy_to_memory( @@ -82,7 +69,7 @@ pub fn copy_to_memory( #[inline(always)] fn min(a: U256, b: usize) -> usize { if a < (b as u64) { - a.as_usize() + a.low_u64() as usize } else { b } @@ -112,9 +99,9 @@ pub fn mload( _system: &System, index: U256, ) -> Result { - let region = - get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, index, WORD_SIZE) + .map_err(|_| StatusCode::InvalidMemoryAccess)? + .expect("empty region"); let value = U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); @@ -128,9 +115,9 @@ pub fn mstore( index: U256, value: U256, ) -> Result<(), StatusCode> { - let region = - get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(WORD_SIZE).unwrap()) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, index, WORD_SIZE) + .map_err(|_| StatusCode::InvalidMemoryAccess)? + .expect("empty region"); let mut bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut bytes); @@ -146,8 +133,9 @@ pub fn mstore8( index: U256, value: U256, ) -> Result<(), StatusCode> { - let region = get_memory_region_u64(&mut state.memory, index, NonZeroUsize::new(1).unwrap()) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + let region = get_memory_region(&mut state.memory, index, 1) + .map_err(|_| StatusCode::InvalidMemoryAccess)? + .expect("empty region"); let value = (value.low_u32() & 0xff) as u8; state.memory[region.offset] = value; From 57d1054cb7748c39bf17934c0926e6ba595b6e34 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 2 Dec 2022 04:56:07 +0200 Subject: [PATCH 155/339] EVM: rework jump instructions (#893) * jmp instructions uncondintionally return the next pc No need for the option and the branches that come from it * jmp instructions can skip the noop sled saves a noop cycle, as it is guaranteed to be there. --- actors/evm/src/interpreter/execution.rs | 17 ++++------------- .../evm/src/interpreter/instructions/control.rs | 12 +++++++----- actors/evm/src/interpreter/instructions/mod.rs | 4 ++-- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 91722c58d..7382bd2b1 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -333,23 +333,14 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { }} JUMP: {(m) => { - try_ins! { JUMP(m) => (res) { - if let Some(dest) = res { - m.pc = dest; - } else { - // cant happen, unless it's a cosmic ray - m.exit = Some(StatusCode::Failure); - } + try_ins! { JUMP(m) => (dest) { + m.pc = dest; }} }} JUMPI: {(m) => { - try_ins! { JUMPI(m) => (res) { - if let Some(dest) = res { - m.pc = dest; - } else { - m.pc += 1; - } + try_ins! { JUMPI(m) => (dest) { + m.pc = dest; }} }} diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 92565f975..6c18f4f17 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -60,23 +60,25 @@ pub fn returndatacopy( } #[inline] -pub fn jump(bytecode: &Bytecode, dest: U256) -> Result, StatusCode> { +pub fn jump(bytecode: &Bytecode, _pc: usize, dest: U256) -> Result { let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; if !bytecode.valid_jump_destination(dst) { return Err(StatusCode::BadJumpDestination); } - Ok(Some(dst)) + // skip the JMPDEST noop sled + Ok(dst + 1) } #[inline] -pub fn jumpi(bytecode: &Bytecode, dest: U256, test: U256) -> Result, StatusCode> { +pub fn jumpi(bytecode: &Bytecode, pc: usize, dest: U256, test: U256) -> Result { if !test.is_zero() { let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; if !bytecode.valid_jump_destination(dst) { return Err(StatusCode::BadJumpDestination); } - Ok(Some(dst)) + // skip the JMPDEST noop sled + Ok(dst + 1) } else { - Ok(None) + Ok(pc + 1) } } diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 67da737b9..d76d8778e 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -146,11 +146,11 @@ macro_rules! def_stdlog { macro_rules! def_jmp { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result, StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* - $impl(m.bytecode, $($arg),*) + $impl(m.bytecode, m.pc, $($arg),*) } } From 6d7c99ea6770ef4d739d9fb78128f3e3fdf03df4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Dec 2022 23:01:15 -0800 Subject: [PATCH 156/339] fix: allow creating empty contracts (#895) This is legal in the EVM. --- actors/evm/src/interpreter/instructions/lifecycle.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 0fd367210..f45fb10b9 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,4 +1,3 @@ -use fil_actors_runtime::ActorError; use fil_actors_runtime::EAM_ACTOR_ADDR; use fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}; use fvm_shared::MethodNum; @@ -58,9 +57,7 @@ pub fn create( let input_data = if let Some(MemoryRegion { offset, size }) = input_region { &memory[offset..][..size.get()] } else { - return Err(StatusCode::ActorError(ActorError::assertion_failed( - "inicode not in memory range".to_string(), - ))); + &[] }; let nonce = system.increment_nonce(); @@ -93,9 +90,7 @@ pub fn create2( let input_data = if let Some(MemoryRegion { offset, size }) = input_region { &memory[offset..][..size.get()] } else { - return Err(StatusCode::ActorError(ActorError::illegal_argument( - "initcode not in memory range".to_string(), - ))); + &[] }; let params = Create2Params { code: input_data.to_vec(), salt }; From 07b3ddbd9b7b275ecaba9b44552112a2e0c409b6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 07:16:25 -0800 Subject: [PATCH 157/339] refactor: rework opcodes definitions and control flow (#896) My goal here was simplify the logic around "exiting" so that I could reason about it. Specifically, so I could fix some issues with SELFDESTRUCT. We had multiple error and success paths and that made me nervous. In the process, I ended up changing a bunch of other things: 1. Exit early by setting the PC. 2. Go back to propagating the error through `step()` (makes things easier to read). 3. Fix some rust unsafety things. 4. Move _all_ of the instruction specific logic into the instruction definitions. This will let us eventually remove one of these tables. - This means that instructions manage their own PCs. 5. Aggressively inline. --- actors/evm/src/interpreter/execution.rs | 436 +++++++----------- .../src/interpreter/instructions/control.rs | 62 ++- .../src/interpreter/instructions/lifecycle.rs | 16 +- .../evm/src/interpreter/instructions/mod.rs | 52 ++- actors/evm/src/interpreter/output.rs | 45 +- actors/evm/src/lib.rs | 72 ++- 6 files changed, 324 insertions(+), 359 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 7382bd2b1..ff2e5a198 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -21,7 +21,6 @@ pub struct ExecutionState { pub memory: Memory, pub input_data: Bytes, pub return_data: Bytes, - pub output_data: Bytes, /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. pub selfdestroyed: Option, /// The EVM address of the caller. @@ -37,7 +36,6 @@ impl ExecutionState { memory: Memory::default(), input_data, return_data: Default::default(), - output_data: Bytes::new(), selfdestroyed: None, caller, receiver, @@ -50,22 +48,24 @@ pub struct Machine<'r, 'a, RT: Runtime + 'a> { pub state: &'r mut ExecutionState, pub bytecode: &'r Bytecode, pub pc: usize, - - // control flow - reverted: bool, - exit: Option, + pub output: Output, } -type Instruction = fn(*mut M); +type Instruction = unsafe fn(*mut M) -> Result<(), StatusCode>; macro_rules! def_opcodes { - ($($op:ident: $body:tt)*) => { + ($($op:ident)*) => { def_ins_raw! { - UNDEFINED(m) { - m.exit = Some(StatusCode::UndefinedInstruction); + UNDEFINED(_m) { + Err(StatusCode::UndefinedInstruction) } } - $(def_ins! { $op $body })* + $(def_ins_raw! { + $op (m) { + instructions::$op(m) + } + })* + def_jmptable! { $($op)* } @@ -82,294 +82,215 @@ macro_rules! def_jmptable { } } -macro_rules! def_ins { - ($ins:ident {intrinsic}) => { - def_ins_intrinsic! { $ins } - }; - - ($ins:ident {=> $expr:expr}) => { - def_ins_raw! { $ins (m) { m.exit = Some($expr); } } - }; - - ($ins:ident {($arg:ident) => $body:block}) => { - def_ins_raw! { $ins ($arg) $body } - }; -} - macro_rules! def_ins_raw { ($ins:ident ($arg:ident) $body:block) => { #[allow(non_snake_case)] - fn $ins(p: *mut Self) { - // 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 Self = unsafe { p.as_mut().unwrap() }; + unsafe fn $ins(p: *mut Self) -> Result<(), StatusCode> { + // 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 Self = &mut *p; $body } }; } -macro_rules! def_ins_intrinsic { - ($ins:ident) => { - def_ins_raw! { - $ins (m) { - match instructions::$ins(m) { - Ok(_) => { - m.pc += 1; - }, - Err(err) => { - m.exit = Some(err); - } - } - } - } - }; -} - -macro_rules! try_ins { - ($ins:ident($m:ident) => ($res:ident) $body:block) => { - match instructions::$ins($m) { - Ok($res) => $body, - Err(err) => { - $m.exit = Some(err); - } - } - }; - - ($ins:ident($m:ident) => $body:block) => { - match instructions::$ins($m) { - Ok(_) => $body, - Err(err) => { - $m.exit = Some(err); - } - } - }; -} - 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, reverted: false, exit: None } + Machine { system, state, bytecode, pc: 0, output: Output::default() } } - pub fn execute(&mut self) -> Result<(), StatusCode> { - while self.exit.is_none() && self.pc < self.bytecode.len() { - self.step(); + pub fn execute(mut self) -> Result { + while self.pc < self.bytecode.len() { + // This is faster than the question mark operator, and speed counts here. + if let Err(e) = self.step() { + return Err(e); + } } - match &self.exit { - None => Ok(()), - Some(StatusCode::Success) => Ok(()), - Some(err) => Err(err.clone()), - } + Ok(self.output) } #[inline(always)] - fn step(&mut self) { + fn step(&mut self) -> Result<(), StatusCode> { let op = self.bytecode[self.pc]; - Self::JMPTABLE[op as usize](self) + unsafe { Self::JMPTABLE[op as usize](self) } } def_opcodes! { - STOP: {=> StatusCode::Success} - // primops - ADD: {intrinsic} - MUL: {intrinsic} - SUB: {intrinsic} - DIV: {intrinsic} - SDIV: {intrinsic} - MOD: {intrinsic} - SMOD: {intrinsic} - ADDMOD: {intrinsic} - MULMOD: {intrinsic} - EXP: {intrinsic} - SIGNEXTEND: {intrinsic} - LT: {intrinsic} - GT: {intrinsic} - SLT: {intrinsic} - SGT: {intrinsic} - EQ: {intrinsic} - ISZERO: {intrinsic} - AND: {intrinsic} - OR: {intrinsic} - XOR: {intrinsic} - NOT: {intrinsic} - BYTE: {intrinsic} - SHL: {intrinsic} - SHR: {intrinsic} - SAR: {intrinsic} + 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 // std call convenction functionoids - KECCAK256: {intrinsic} - ADDRESS: {intrinsic} - BALANCE: {intrinsic} - ORIGIN: {intrinsic} - CALLER: {intrinsic} - CALLVALUE: {intrinsic} - CALLDATALOAD: {intrinsic} - CALLDATASIZE: {intrinsic} - CALLDATACOPY: {intrinsic} - CODESIZE: {intrinsic} - CODECOPY: {intrinsic} - GASPRICE: {intrinsic} - EXTCODESIZE: {intrinsic} - EXTCODECOPY: {intrinsic} - RETURNDATASIZE: {intrinsic} - RETURNDATACOPY: {intrinsic} - EXTCODEHASH: {intrinsic} - BLOCKHASH: {intrinsic} - COINBASE: {intrinsic} - TIMESTAMP: {intrinsic} - NUMBER: {intrinsic} - DIFFICULTY: {intrinsic} - GASLIMIT: {intrinsic} - CHAINID: {intrinsic} - BASEFEE: {intrinsic} - SELFBALANCE: {intrinsic} - MLOAD: {intrinsic} - MSTORE: {intrinsic} - MSTORE8: {intrinsic} - SLOAD: {intrinsic} - SSTORE: {intrinsic} - MSIZE: {intrinsic} - GAS: {intrinsic} + KECCAK256 + ADDRESS + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY + RETURNDATASIZE + RETURNDATACOPY + EXTCODEHASH + BLOCKHASH + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT + CHAINID + BASEFEE + SELFBALANCE + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + MSIZE + GAS // stack ops - POP: {intrinsic} + POP // push variants - PUSH1: {intrinsic} - PUSH2: {intrinsic} - PUSH3: {intrinsic} - PUSH4: {intrinsic} - PUSH5: {intrinsic} - PUSH6: {intrinsic} - PUSH7: {intrinsic} - PUSH8: {intrinsic} - PUSH9: {intrinsic} - PUSH10: {intrinsic} - PUSH11: {intrinsic} - PUSH12: {intrinsic} - PUSH13: {intrinsic} - PUSH14: {intrinsic} - PUSH15: {intrinsic} - PUSH16: {intrinsic} - PUSH17: {intrinsic} - PUSH18: {intrinsic} - PUSH19: {intrinsic} - PUSH20: {intrinsic} - PUSH21: {intrinsic} - PUSH22: {intrinsic} - PUSH23: {intrinsic} - PUSH24: {intrinsic} - PUSH25: {intrinsic} - PUSH26: {intrinsic} - PUSH27: {intrinsic} - PUSH28: {intrinsic} - PUSH29: {intrinsic} - PUSH30: {intrinsic} - PUSH31: {intrinsic} - PUSH32: {intrinsic} + 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 // dup variants - DUP1: {intrinsic} - DUP2: {intrinsic} - DUP3: {intrinsic} - DUP4: {intrinsic} - DUP5: {intrinsic} - DUP6: {intrinsic} - DUP7: {intrinsic} - DUP8: {intrinsic} - DUP9: {intrinsic} - DUP10: {intrinsic} - DUP11: {intrinsic} - DUP12: {intrinsic} - DUP13: {intrinsic} - DUP14: {intrinsic} - DUP15: {intrinsic} - DUP16: {intrinsic} + DUP1 + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 // swap variants - SWAP1: {intrinsic} - SWAP2: {intrinsic} - SWAP3: {intrinsic} - SWAP4: {intrinsic} - SWAP5: {intrinsic} - SWAP6: {intrinsic} - SWAP7: {intrinsic} - SWAP8: {intrinsic} - SWAP9: {intrinsic} - SWAP10: {intrinsic} - SWAP11: {intrinsic} - SWAP12: {intrinsic} - SWAP13: {intrinsic} - SWAP14: {intrinsic} - SWAP15: {intrinsic} - SWAP16: {intrinsic} + SWAP1 + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 + + // pc + PC // event logs - LOG0: {intrinsic} - LOG1: {intrinsic} - LOG2: {intrinsic} - LOG3: {intrinsic} - LOG4: {intrinsic} + LOG0 + LOG1 + LOG2 + LOG3 + LOG4 // create variants - CREATE: {intrinsic} - CREATE2: {intrinsic} + CREATE + CREATE2 // call variants - CALL: {intrinsic} - CALLCODE: {intrinsic} - DELEGATECALL: {intrinsic} - STATICCALL: {intrinsic} + CALL + CALLCODE + DELEGATECALL + STATICCALL + + // exiting ops + STOP + RETURN + REVERT + SELFDESTRUCT // control flow magic // noop marker opcode for valid jumps addresses - JUMPDEST: {(m) => { - m.pc += 1; - }} - - JUMP: {(m) => { - try_ins! { JUMP(m) => (dest) { - m.pc = dest; - }} - }} - - JUMPI: {(m) => { - try_ins! { JUMPI(m) => (dest) { - m.pc = dest; - }} - }} - - PC: {(m) => { - try_ins! { PC(m) => { - m.pc += 1; - }} - }} - - RETURN: {(m) => { - try_ins! { RETURN(m) => { - m.exit = Some(StatusCode::Success); - }} - }} - - REVERT: {(m) => { - try_ins! { REVERT(m) => { - m.reverted = true; - m.exit = Some(StatusCode::Success); - }} - }} - - SELFDESTRUCT: {(m) => { - try_ins! { SELFDESTRUCT(m) => { - m.exit = Some(StatusCode::Success); - }} - }} + JUMPDEST + // reserved invalid instruction + INVALID - INVALID: {=> StatusCode::InvalidInstruction} + // jump ops + JUMP + JUMPI } const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); @@ -380,12 +301,5 @@ pub fn execute( runtime: &mut ExecutionState, system: &mut System, ) -> Result { - let mut m = Machine::new(system, runtime, bytecode); - m.execute()?; - Ok(Output { - reverted: m.reverted, - status_code: StatusCode::Success, - output_data: m.state.output_data.clone(), - selfdestroyed: m.state.selfdestroyed, - }) + Machine::new(system, runtime, bytecode).execute() } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 6c18f4f17..156ac582e 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -1,3 +1,7 @@ +use bytes::Bytes; + +use crate::interpreter::{memory::Memory, output::Outcome, Output}; + use { super::memory::get_memory_region, crate::interpreter::output::StatusCode, @@ -7,20 +11,60 @@ use { }; #[inline] -pub fn output( +pub fn nop(_state: &mut ExecutionState, _system: &System) -> Result<(), StatusCode> { + Ok(()) +} + +#[inline] +pub fn invalid( + _state: &mut ExecutionState, + _system: &System, +) -> Result<(), StatusCode> { + Err(StatusCode::InvalidInstruction) +} + +#[inline] +pub fn ret( state: &mut ExecutionState, _system: &System, offset: U256, size: U256, -) -> Result<(), StatusCode> { - if let Some(region) = super::memory::get_memory_region(&mut state.memory, offset, size) - .map_err(|_| StatusCode::InvalidMemoryAccess)? - { - state.output_data = - state.memory[region.offset..region.offset + region.size.get()].to_vec().into(); - } +) -> Result { + exit(&mut state.memory, offset, size, Outcome::Return) +} - Ok(()) +#[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: Bytes::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_err(|_| StatusCode::InvalidMemoryAccess)? + .map(|region| memory[region.offset..region.offset + region.size.get()].to_vec().into()) + .unwrap_or_default(), + }) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index f45fb10b9..f82686263 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,9 +1,12 @@ +use bytes::Bytes; + use fil_actors_runtime::EAM_ACTOR_ADDR; use fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}; use fvm_shared::MethodNum; use fvm_shared::{address::Address, econ::TokenAmount}; use serde_tuple::{Deserialize_tuple, Serialize_tuple}; +use crate::interpreter::Output; use crate::interpreter::{address::EthAddress, U256}; use super::memory::{get_memory_region, MemoryRegion}; @@ -139,15 +142,16 @@ fn create_init( #[inline] pub fn selfdestruct( - state: &mut ExecutionState, + _state: &mut ExecutionState, system: &mut System, beneficiary: U256, -) -> Result<(), StatusCode> { +) -> Result { + use crate::interpreter::output::Outcome; + if system.readonly { return Err(StatusCode::StaticModeViolation); } - let beneficiary_addr: EthAddress = beneficiary.into(); - // TODO: how do we handle errors here? Just ignore them? - state.selfdestroyed = beneficiary_addr.try_into().ok(); - Ok(()) + let beneficiary: Address = EthAddress::from(beneficiary).try_into()?; + system.rt.delete_actor(&beneficiary)?; + Ok(Output { outcome: Outcome::Delete, return_data: Bytes::new() }) } diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index d76d8778e..be427f097 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -26,12 +26,14 @@ use fil_actors_runtime::runtime::Runtime; macro_rules! def_primop { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* let result = $impl($($arg),*); unsafe {m.state.stack.push(result);} + m.pc += 1; Ok(()) } } @@ -41,6 +43,7 @@ macro_rules! def_primop { macro_rules! def_stackop { ($op:ident => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>( m: &mut Machine<'r, 'a, RT>, ) -> Result<(), StatusCode> { @@ -48,6 +51,7 @@ macro_rules! def_stackop { unsafe { $impl(&mut m.state.stack); } + m.pc += 1; Ok(()) } }; @@ -58,11 +62,13 @@ macro_rules! def_stackop { macro_rules! def_push { ($op:ident => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>( m: &mut Machine<'r, 'a, RT>, ) -> Result<(), StatusCode> { check_stack!($op, m.state.stack); - let code = &m.bytecode[m.pc + 1..]; + m.pc += 1; + let code = &m.bytecode[m.pc..]; m.pc += unsafe { $impl(&mut m.state.stack, code) }; Ok(()) } @@ -74,12 +80,14 @@ macro_rules! def_push { macro_rules! def_stdfun { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* let result = $impl(&mut m.state, &mut m.system, $($arg),*)?; unsafe {m.state.stack.push(result);} + m.pc += 1; Ok(()) } } @@ -89,11 +97,13 @@ macro_rules! def_stdfun { macro_rules! def_stdproc { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* $impl(&mut m.state, &mut m.system, $($arg),*)?; + m.pc += 1; Ok(()) } } @@ -103,12 +113,14 @@ macro_rules! def_stdproc { macro_rules! def_stdfun_code { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* let result = $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; unsafe {m.state.stack.push(result);} + m.pc += 1; Ok(()) } } @@ -118,11 +130,13 @@ macro_rules! def_stdfun_code { macro_rules! def_stdproc_code { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; + m.pc += 1; Ok(()) } } @@ -132,12 +146,15 @@ macro_rules! def_stdproc_code { macro_rules! def_stdlog { ($op:ident ($ntopics:literal, ($($topic:ident),*))) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_stack!($op, m.state.stack); let a = unsafe {m.state.stack.pop()}; let b = unsafe {m.state.stack.pop()}; $(let $topic = unsafe {m.state.stack.pop()};)* - log::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*]) + log::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*])?; + m.pc += 1; + Ok(()) } } } @@ -146,26 +163,44 @@ macro_rules! def_stdlog { macro_rules! def_jmp { ($op:ident ($($arg:ident),*) => $impl:path) => { #[allow(non_snake_case)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result { + #[inline(always)] + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { check_arity!($op, ($($arg),*)); check_stack!($op, m.state.stack); $(let $arg = unsafe {m.state.stack.pop()};)* - $impl(m.bytecode, m.pc, $($arg),*) + m.pc = $impl(m.bytecode, m.pc, $($arg),*)?; + Ok(()) } } } +macro_rules! def_exit { + ($op:ident ($($arg:ident),*) => $impl:path) => { + #[allow(non_snake_case)] + #[inline(always)] + pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { + check_arity!($op, ($($arg),*)); + check_stack!($op, m.state.stack); + $(let $arg = unsafe {m.state.stack.pop()};)* + 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) => { #[allow(non_snake_case)] + #[inline(always)] pub fn $op<'r, 'a, RT: Runtime + 'a>( $m: &mut Machine<'r, 'a, RT>, ) -> Result<(), StatusCode> { check_stack!($op, $m.state.stack); let result = $value; unsafe { $m.state.stack.push(result) }; + $m.pc += 1; Ok(()) } }; @@ -349,9 +384,12 @@ 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! { RETURN(a, b) => control::output } -def_stdproc! { REVERT(a, b) => control::output } -def_stdproc! { SELFDESTRUCT(a) => lifecycle::selfdestruct } +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/output.rs b/actors/evm/src/interpreter/output.rs index 1c052aa65..d052df8ea 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,46 +1,27 @@ use fil_actors_runtime::ActorError as RTActorError; -use fvm_shared::address::Address as FilecoinAddress; use {bytes::Bytes, std::fmt::Debug, strum_macros::Display}; -/// Output of EVM execution. -#[derive(Clone, PartialEq, Eq)] -pub struct Output { - /// EVM exited with this status code. - pub status_code: StatusCode, - /// Output data returned. - pub output_data: Bytes, - /// Indicates if revert was requested - pub reverted: bool, - /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. - pub selfdestroyed: Option, +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub enum Outcome { + #[default] + Return, + Revert, + Delete, } -impl Debug for Output { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Output") - .field("status_code", &self.status_code) - .field("output_data", &hex::encode(&self.output_data)) - .field("reverted", &self.reverted) - .finish() - } +/// 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: Bytes, } /// Message status code. #[must_use] #[derive(Clone, Debug, Display, PartialEq, Eq)] pub enum StatusCode { - /// Execution finished with success. - #[strum(serialize = "success")] - Success, - - /// Generic execution failure. - #[strum(serialize = "failure")] - Failure, - - /// Execution terminated with REVERT opcode. - #[strum(serialize = "revert")] - Revert, - /// The designated INVALID instruction has been hit during execution. /// /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index de373a062..84c1b3c02 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -5,6 +5,8 @@ use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; use fvm_shared::address::{Address, Payload}; use interpreter::{address::EthAddress, system::load_bytecode}; +use crate::interpreter::output::Outcome; + pub mod interpreter; mod state; @@ -110,29 +112,22 @@ impl EvmContractActor { let initcode = Bytecode::new(params.initcode.into()); // invoke the contract constructor - let exec_status = - execute(&initcode, &mut exec_state, &mut system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; + let output = execute(&initcode, &mut exec_state, &mut system).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; - // TODO this does not return revert data yet, but it has correct semantics. - if exec_status.reverted { - Err(ActorError::unchecked_with_data( + 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(), - RawBytes::from(exec_status.output_data.to_vec()), - )) - } else if exec_status.status_code == StatusCode::Success { - system.set_bytecode(&exec_status.output_data)?; - system.flush() - } else if let StatusCode::ActorError(e) = exec_status.status_code { - Err(e) - } else { - Err(ActorError::unchecked( - EVM_CONTRACT_EXECUTION_ERROR, - format!("constructor execution error: {}", exec_status.status_code), - )) + RawBytes::from(output.return_data.to_vec()), + )), + Outcome::Delete => Ok(()), } } @@ -175,34 +170,23 @@ impl EvmContractActor { let mut exec_state = ExecutionState::new(caller_eth_addr, receiver_eth_addr, input_data.to_vec().into()); - let exec_status = - execute(&bytecode, &mut exec_state, &mut system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; + let output = execute(&bytecode, &mut exec_state, &mut system).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; - if exec_status.reverted { - return Err(ActorError::unchecked_with_data( + 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(), - RawBytes::from(exec_status.output_data.to_vec()), - )); - } else if exec_status.status_code == StatusCode::Success { - system.flush()?; - } else if let StatusCode::ActorError(e) = exec_status.status_code { - return Err(e); - } else { - return Err(ActorError::unchecked( - EVM_CONTRACT_EXECUTION_ERROR, - format!("contract execution error: {}", exec_status.status_code), - )); + RawBytes::from(output.return_data.to_vec()), + )), + Outcome::Delete => Ok(Vec::new()), } - - if let Some(addr) = exec_status.selfdestroyed { - rt.delete_actor(&addr)? - } - - Ok(exec_status.output_data.to_vec()) } pub fn handle_filecoin_method( From eea68aa3cb62839ecd666b6a88707af53e3fd8e9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 07:42:15 -0800 Subject: [PATCH 158/339] fix: correctly encode revert data (#899) This will be _properly_ fixed in https://github.com/filecoin-project/ref-fvm/issues/987, but we need to return proper CBOR till then. --- actors/evm/src/lib.rs | 4 ++-- actors/evm/tests/revert.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 84c1b3c02..1d28794c9 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -125,7 +125,7 @@ impl EvmContractActor { Outcome::Revert => Err(ActorError::unchecked_with_data( EVM_CONTRACT_REVERTED, "constructor reverted".to_string(), - RawBytes::from(output.return_data.to_vec()), + RawBytes::serialize(BytesSer(&output.return_data)).unwrap(), )), Outcome::Delete => Ok(()), } @@ -183,7 +183,7 @@ impl EvmContractActor { Outcome::Revert => Err(ActorError::unchecked_with_data( EVM_CONTRACT_REVERTED, "contract reverted".to_string(), - RawBytes::from(output.return_data.to_vec()), + RawBytes::serialize(BytesSer(&output.return_data)).unwrap(), )), Outcome::Delete => Ok(Vec::new()), } diff --git a/actors/evm/tests/revert.rs b/actors/evm/tests/revert.rs index 4af6fe810..e0d72826e 100644 --- a/actors/evm/tests/revert.rs +++ b/actors/evm/tests/revert.rs @@ -30,5 +30,5 @@ revert assert!(result.is_err()); let e = result.unwrap_err(); assert_eq!(e.exit_code(), evm::EVM_CONTRACT_REVERTED); - assert_eq!(e.data(), RawBytes::from(vec![0xde, 0xad, 0xbe, 0xef])); + assert_eq!(e.data(), RawBytes::serialize(BytesSer(&[0xde, 0xad, 0xbe, 0xef])).unwrap()); } From 0cc262a840bbfc6066ebb811706e18612dba5d15 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 07:42:50 -0800 Subject: [PATCH 159/339] fix: handle errors and non-existent addresses in selfdestruct (#897) In the EVM: 1. Self destruct continues even if it sends funds into oblivion. 2. Self destruct will auto-create a beneficiary. This lets us punt https://github.com/filecoin-project/ref-fvm/issues/736 to M2.2. --- .../src/interpreter/instructions/lifecycle.rs | 17 ++++++-- actors/evm/tests/selfdestruct.rs | 43 +++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index f82686263..9af802b37 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,8 +1,9 @@ use bytes::Bytes; -use fil_actors_runtime::EAM_ACTOR_ADDR; +use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, EAM_ACTOR_ADDR}; use fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}; use fvm_shared::MethodNum; +use fvm_shared::METHOD_SEND; use fvm_shared::{address::Address, econ::TokenAmount}; use serde_tuple::{Deserialize_tuple, Serialize_tuple}; @@ -151,7 +152,17 @@ pub fn selfdestruct( if system.readonly { return Err(StatusCode::StaticModeViolation); } - let beneficiary: Address = EthAddress::from(beneficiary).try_into()?; - system.rt.delete_actor(&beneficiary)?; + + // Try to give funds to the beneficiary. We don't use the `delete_actor` syscall to do this + // because that won't auto-create the beneficiary (and will fail if, for some reason, we can't + // send them the funds). + // + // If we fail, we'll just burn the funds. Yes, this is what the EVM does. + if let Ok(addr) = EthAddress::from(beneficiary).try_into() { + let balance = system.rt.current_balance(); + let _ = system.rt.send(&addr, METHOD_SEND, RawBytes::default(), balance); + } + // Now try to delete ourselves. If this fails, we abort execution. + system.rt.delete_actor(&BURNT_FUNDS_ACTOR_ADDR)?; Ok(Output { outcome: Outcome::Delete, return_data: Bytes::new() }) } diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index a4b077ff0..795d4f316 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -1,5 +1,6 @@ -use fil_actors_runtime::test_utils::*; -use fvm_shared::address::Address; +use fil_actors_runtime::{test_utils::*, BURNT_FUNDS_ACTOR_ADDR}; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::{address::Address, error::ExitCode, METHOD_SEND}; mod util; @@ -17,7 +18,43 @@ fn test_selfdestruct() { let solidity_params = hex::decode("35f46994").unwrap(); rt.expect_validate_caller_any(); - rt.expect_delete_actor(beneficiary); + rt.expect_send( + beneficiary, + METHOD_SEND, + RawBytes::default(), + rt.get_balance(), + RawBytes::default(), + ExitCode::OK, + ); + rt.expect_delete_actor(BURNT_FUNDS_ACTOR_ADDR); + + assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); + rt.verify(); +} + +#[test] +fn test_selfdestruct_missing() { + 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, + RawBytes::default(), + rt.get_balance(), + RawBytes::default(), + ExitCode::SYS_INVALID_RECEIVER, + ); + rt.expect_delete_actor(BURNT_FUNDS_ACTOR_ADDR); assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); rt.verify(); From 75dee66f6c74818589cdca9e2eb3dfa45a71ca0e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 09:01:44 -0800 Subject: [PATCH 160/339] test: fix buggy test after merge --- actors/evm/tests/call.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 012cf12a4..c1699f320 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -181,9 +181,6 @@ fn test_reserved_method() { let contract = filecoin_fallback_contract(); let mut rt = util::construct_and_verify(contract); - // invoke the contract - rt.expect_validate_caller_any(); - let code = rt.call::(0x42, &RawBytes::default()).unwrap_err().exit_code(); assert_eq!(ExitCode::USR_UNHANDLED_MESSAGE, code); From 63860145145c8040f0adfd72b8c2bab58177a68e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 13:45:09 -0800 Subject: [PATCH 161/339] fix: right-pad when pushing at end of bytecode (#903) --- .../evm/src/interpreter/instructions/stack.rs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index f30837a2f..d0a7898e0 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -3,15 +3,12 @@ use {crate::interpreter::stack::Stack, crate::interpreter::U256}; #[inline] pub(crate) unsafe fn push(stack: &mut Stack, code: &[u8]) -> usize { - let pushval = &code[..LEN]; - stack.push(match pushval.len() { - 0 => U256::zero(), - 32 => U256::from_big_endian(pushval), - _ => { - let mut padded = [0; 32]; - padded[32 - pushval.len()..].copy_from_slice(pushval); - U256::from_big_endian(&padded) - } + stack.push(if code.len() < LEN { + let mut padded = [0; LEN]; + padded[..code.len()].copy_from_slice(code); + U256::from_big_endian(&padded) + } else { + U256::from_big_endian(&code[..LEN]) }); LEN } @@ -30,3 +27,14 @@ pub(crate) unsafe fn swap(stack: &mut Stack) { pub(crate) unsafe fn pop(stack: &mut Stack) { stack.pop(); } + +#[test] +fn test_push_pad_right() { + let mut stack = Stack::new(); + unsafe { + assert!(stack.ensure(1)); + assert_eq!(push::<4>(&mut stack, &[0xde, 0xad]), 4); + assert_eq!(stack.len(), 1); + assert_eq!(stack.get(0), &U256::from(0xdead0000u64)); + } +} From 80bfa09f107cd5b8cea23f2e944d7e8722d50377 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 3 Dec 2022 01:18:40 +0200 Subject: [PATCH 162/339] EVM: value + magic gas limit 23k implies METHOD_SEND (#887) value + magic gas limit 23k implies METHOD_SEND --- actors/evm/src/interpreter/instructions/call.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 7cac8065a..e72e33bb3 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -239,6 +239,9 @@ pub fn call_generic( } else { let method = if !actor_exists || matches!(target_actor_type, Some(Type::Embryo | Type::Account)) + // See https://github.com/filecoin-project/ref-fvm/issues/980 for this + // magic value + || (!value.is_zero() && !gas.is_zero() && gas <= U256::from(21_000)) { // If the target actor doesn't exist or is an account or an embryo, // switch to a basic "send" so the call will still work even if the From d7c1f31e9c8a1e55cbc4e2889595db298a9e2c01 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 17:00:46 -0800 Subject: [PATCH 163/339] fix: return data from callactor on revert as well (#905) * fix: return data from callactor on revert as well Also, avoid cloning unnecessarily. * test: callactor return data on revert --- .../evm/src/interpreter/instructions/call.rs | 2 +- actors/evm/src/interpreter/precompiles.rs | 6 +++--- actors/evm/tests/call.rs | 18 +++++++++++++++--- actors/evm/tests/revert.rs | 2 +- runtime/src/actor_error.rs | 9 +++++++-- runtime/src/runtime/fvm.rs | 5 +++-- runtime/src/test_utils.rs | 6 +++++- test_vm/src/lib.rs | 4 ++-- 8 files changed, 37 insertions(+), 15 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index e72e33bb3..e4293a857 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -301,7 +301,7 @@ pub fn call_generic( (1, result) } } - Err(ae) => (0, ae.data().to_vec()), + Err(mut ae) => (0, ae.take_data().into()), } } }; diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index f5d3edde1..f541a576a 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -497,14 +497,14 @@ pub fn call_actor( // positive values are user/actor errors // success is 0 let (exit_code, data) = match result { - Err(ae) => { + Err(mut ae) => { // TODO handle revert // TODO https://github.com/filecoin-project/ref-fvm/issues/1020 // put error number from call into revert let exit_code = U256::from(ae.exit_code().value()); // no return only exit code - (exit_code, RawBytes::default()) + (exit_code, ae.take_data()) } Ok(ret) => (U256::zero(), ret), }; @@ -525,7 +525,7 @@ pub fn call_actor( // NOTE: // we dont pad out to 32 bytes here, the idea being that users will already be in the "everythig is bytes" mode // and will want re-pack align and whatever else by themselves - output.extend_from_slice(&data); + output.extend_from_slice(data.bytes()); output }; diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index c1699f320..6a5751259 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -2,6 +2,7 @@ mod asm; use evm::interpreter::address::EthAddress; use evm::interpreter::U256; +use evm::EVM_CONTRACT_REVERTED; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::{BytesSer, RawBytes, DAG_CBOR}; @@ -248,7 +249,18 @@ return } #[test] -fn test_callactor() { +fn test_callactor_success() { + // Should work if the called actor succeeds. + test_callactor_inner(ExitCode::OK) +} + +#[test] +fn test_callactor_revert() { + // Should propegate the return value if the called actor fails. + test_callactor_inner(EVM_CONTRACT_REVERTED) +} + +fn test_callactor_inner(exit_code: ExitCode) { let contract = callactor_proxy_contract(); // construct the proxy @@ -289,7 +301,7 @@ fn test_callactor() { proxy_call_input_data, TokenAmount::zero(), RawBytes::from(return_data), - ExitCode::OK, + exit_code, ); // invoke @@ -343,7 +355,7 @@ fn test_callactor() { let result = CallActorReturn::read(&result); let expected = CallActorReturn { - exit_code: ExitCode::new(0), + exit_code, codec: DAG_CBOR, data_offset: 128, data_size: 32, diff --git a/actors/evm/tests/revert.rs b/actors/evm/tests/revert.rs index e0d72826e..fa260647c 100644 --- a/actors/evm/tests/revert.rs +++ b/actors/evm/tests/revert.rs @@ -30,5 +30,5 @@ revert assert!(result.is_err()); let e = result.unwrap_err(); assert_eq!(e.exit_code(), evm::EVM_CONTRACT_REVERTED); - assert_eq!(e.data(), RawBytes::serialize(BytesSer(&[0xde, 0xad, 0xbe, 0xef])).unwrap()); + assert_eq!(e.data(), RawBytes::serialize(BytesSer(&[0xde, 0xad, 0xbe, 0xef])).unwrap().bytes()); } diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 7df13c753..4eda79d18 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -67,8 +67,13 @@ impl ActorError { } /// Returns the optional data that might be associated with the error - pub fn data(&self) -> RawBytes { - self.data.clone() + pub fn data(&self) -> &[u8] { + &self.data + } + + /// Extracts the optional associated data without copying. + pub fn take_data(&mut self) -> RawBytes { + std::mem::take(&mut self.data) } /// Prefix error message with a string message. diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 6dcb72f7f..2d572c552 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -684,8 +684,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, ¶ms) - .unwrap_or_else(|err| fvm::vm::exit(err.exit_code().value(), err.data(), Some(err.msg()))); + let ret = C::invoke_method(&mut rt, method, ¶ms).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/test_utils.rs b/runtime/src/test_utils.rs index 53c89d733..e9be430a4 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -597,7 +597,11 @@ impl MockRuntime { match expected_msg.exit_code { ExitCode::OK => Ok(expected_msg.send_return), - x => Err(ActorError::unchecked(x, "Expected message Fail".to_string())), + x => Err(ActorError::unchecked_with_data( + x, + "Expected message Fail".to_string(), + expected_msg.send_return, + )), } } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 3caf551aa..07541685d 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -465,12 +465,12 @@ impl<'bs> VM<'bs> { invocs }); match res { - Err(ae) => { + Err(mut ae) => { self.rollback(prior_root); Ok(MessageResult { code: ae.exit_code(), message: ae.msg().to_string(), - ret: ae.data(), + ret: ae.take_data(), }) } Ok(ret) => { From 1a1c109932c7af2c84f99a286ff517192a2140cf Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Dec 2022 18:34:42 -0800 Subject: [PATCH 164/339] refactor: remove OpCode enum and make the stack safe (#904) --- actors/evm/src/interpreter/bytecode.rs | 10 +- actors/evm/src/interpreter/execution.rs | 374 ++++++++---------- .../evm/src/interpreter/instructions/mod.rs | 227 +++++------ .../evm/src/interpreter/instructions/stack.rs | 42 +- actors/evm/src/interpreter/mod.rs | 1 - actors/evm/src/interpreter/opcode.rs | 227 ----------- actors/evm/src/interpreter/stack.rs | 140 ++++--- actors/evm/tests/asm.rs | 16 +- 8 files changed, 383 insertions(+), 654 deletions(-) delete mode 100644 actors/evm/src/interpreter/opcode.rs diff --git a/actors/evm/src/interpreter/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs index 3a3b30f3e..6c34aa3e7 100644 --- a/actors/evm/src/interpreter/bytecode.rs +++ b/actors/evm/src/interpreter/bytecode.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] -use {super::opcode::OpCode, std::ops::Deref}; +use std::ops::Deref; + +use super::execution::opcodes; #[derive(Clone, Debug)] pub struct Bytecode { @@ -15,11 +17,11 @@ impl Bytecode { let mut jumpdest = vec![false; bytecode.len()]; let mut i = 0; while i < bytecode.len() { - if bytecode[i] == OpCode::JUMPDEST as u8 { + if bytecode[i] == opcodes::JUMPDEST as u8 { jumpdest[i] = true; i += 1; - } else if bytecode[i] >= OpCode::PUSH1 as u8 && bytecode[i] <= OpCode::PUSH32 as u8 { - i += (bytecode[i] - OpCode::PUSH1 as u8) as usize + 2; + } else if bytecode[i] >= opcodes::PUSH1 && bytecode[i] <= opcodes::PUSH32 { + i += (bytecode[i] - opcodes::PUSH1) as usize + 2; } else { i += 1; } diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index ff2e5a198..6d0276a5c 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -5,7 +5,6 @@ use fvm_shared::address::Address as FilecoinAddress; use super::address::EthAddress; use { super::instructions, - super::opcode::OpCode, super::StatusCode, crate::interpreter::memory::Memory, crate::interpreter::stack::Stack, @@ -51,49 +50,197 @@ pub struct Machine<'r, 'a, RT: Runtime + 'a> { pub output: Output, } -type Instruction = unsafe fn(*mut M) -> Result<(), StatusCode>; - macro_rules! def_opcodes { - ($($op:ident)*) => { - def_ins_raw! { - UNDEFINED(_m) { - Err(StatusCode::UndefinedInstruction) - } - } - $(def_ins_raw! { - $op (m) { - instructions::$op(m) + ($($code:literal: $op:ident,)*) => { + pub const fn jumptable<'r, 'a, RT: Runtime>() -> [Instruction<'r, 'a, RT>; 256] { + def_ins_raw! { + UNDEFINED(_m) { + Err(StatusCode::UndefinedInstruction) + } } - })* - - def_jmptable! { - $($op)* - } - } -} + $(def_ins_raw! { + $op (m) { + instructions::$op(m) + } + })* -macro_rules! def_jmptable { - ($($op:ident)*) => { - const fn jmptable() -> [Instruction>; 256] { - let mut table: [Instruction>; 256] = [Machine::<'r, 'a, RT>::UNDEFINED; 256]; - $(table[OpCode::$op as usize] = Machine::<'r, 'a, RT>::$op;)* + 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(p: *mut Self) -> Result<(), StatusCode> { + unsafe fn $ins<'r, 'a, RT: Runtime>(p: *mut Machine<'r, 'a, RT>) -> Result<(), StatusCode> { // 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 Self = &mut *p; + let $arg: &mut Machine<'r, 'a, RT> = &mut *p; $body } }; } +pub mod opcodes { + use super::instructions; + use super::Machine; + use crate::interpreter::StatusCode; + use fil_actors_runtime::runtime::Runtime; + + pub type Instruction<'r, 'a, RT> = + unsafe fn(*mut Machine<'r, 'a, RT>) -> Result<(), StatusCode>; + + 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: DIFFICULTY, + 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, + 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, + 0xf2: CALLCODE, + 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>, @@ -120,180 +267,7 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { unsafe { Self::JMPTABLE[op as usize](self) } } - def_opcodes! { - // primops - 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 - - // std call convenction functionoids - KECCAK256 - ADDRESS - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY - RETURNDATASIZE - RETURNDATACOPY - EXTCODEHASH - BLOCKHASH - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT - CHAINID - BASEFEE - SELFBALANCE - MLOAD - MSTORE - MSTORE8 - SLOAD - SSTORE - MSIZE - GAS - - // stack ops - POP - - // push variants - 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 - - // dup variants - DUP1 - DUP2 - DUP3 - DUP4 - DUP5 - DUP6 - DUP7 - DUP8 - DUP9 - DUP10 - DUP11 - DUP12 - DUP13 - DUP14 - DUP15 - DUP16 - - // swap variants - SWAP1 - SWAP2 - SWAP3 - SWAP4 - SWAP5 - SWAP6 - SWAP7 - SWAP8 - SWAP9 - SWAP10 - SWAP11 - SWAP12 - SWAP13 - SWAP14 - SWAP15 - SWAP16 - - // pc - PC - - // event logs - LOG0 - LOG1 - LOG2 - LOG3 - LOG4 - - // create variants - CREATE - CREATE2 - - // call variants - CALL - CALLCODE - DELEGATECALL - STATICCALL - - // exiting ops - STOP - RETURN - REVERT - SELFDESTRUCT - - // control flow magic - // noop marker opcode for valid jumps addresses - JUMPDEST - // reserved invalid instruction - INVALID - - // jump ops - JUMP - JUMPI - } - - const JMPTABLE: [Instruction>; 256] = Machine::<'r, 'a, RT>::jmptable(); + const JMPTABLE: [opcodes::Instruction<'r, 'a, RT>; 256] = opcodes::jumptable::<'r, 'a, RT>(); } pub fn execute( diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index be427f097..8badacd74 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -16,44 +16,64 @@ pub mod state; pub mod storage; use crate::interpreter::execution::Machine; -use crate::interpreter::opcode::{OpCode, StackSpec}; use crate::interpreter::output::StatusCode; use crate::interpreter::U256; use fil_actors_runtime::runtime::Runtime; +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<(), StatusCode> { + $($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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + ($op:ident ($($arg:ident),+) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; let result = $impl($($arg),*); - unsafe {m.state.stack.push(result);} + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>( - m: &mut Machine<'r, 'a, RT>, - ) -> Result<(), StatusCode> { - check_stack!($op, m.state.stack); - unsafe { - $impl(&mut m.state.stack); - } + def_op! { $op (m) => { + $impl(&mut m.state.stack)?; m.pc += 1; Ok(()) - } + }} }; } @@ -61,186 +81,133 @@ macro_rules! def_stackop { // makes you want to cry because it really is a stack op. macro_rules! def_push { ($op:ident => $impl:path) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>( - m: &mut Machine<'r, 'a, RT>, - ) -> Result<(), StatusCode> { - check_stack!($op, m.state.stack); + def_op! { $op (m) => { m.pc += 1; let code = &m.bytecode[m.pc..]; - m.pc += unsafe { $impl(&mut m.state.stack, code) }; + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + ($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),*)?; - unsafe {m.state.stack.push(result);} + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + ($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),*)?; - unsafe {m.state.stack.push(result);} + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + 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),*))) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_stack!($op, m.state.stack); - let a = unsafe {m.state.stack.pop()}; - let b = unsafe {m.state.stack.pop()}; - $(let $topic = unsafe {m.state.stack.pop()};)* + def_op!{ $op (m) => { + let &rev![a, b $(,$topic)*] = m.state.stack.pop_many()?; log::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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>(m: &mut Machine<'r, 'a, RT> ) -> Result<(), StatusCode> { - check_arity!($op, ($($arg),*)); - check_stack!($op, m.state.stack); - $(let $arg = unsafe {m.state.stack.pop()};)* + 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) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $op<'r, 'a, RT: Runtime + 'a>( - $m: &mut Machine<'r, 'a, RT>, - ) -> Result<(), StatusCode> { - check_stack!($op, $m.state.stack); - let result = $value; - unsafe { $m.state.stack.push(result) }; - $m.pc += 1; + def_op! { $op (m) => { + let result = { + let $m = &mut *m; + $value + }; + m.state.stack.push(result)?; + m.pc += 1; Ok(()) - } + }} }; } -// auxiliary macros -macro_rules! check_stack { - ($op:ident, $sk:expr) => {{ - const SPEC: StackSpec = OpCode::$op.spec(); - if SPEC.required > 0 { - if !$sk.require(SPEC.required as usize) { - return Err(StatusCode::StackUnderflow); - } - } - if SPEC.changed > 0 { - if !$sk.ensure(SPEC.changed as usize) { - return Err(StatusCode::StackOverflow); - } - } - }}; -} - -macro_rules! check_arity { - ($op:ident, ($($arg:ident),*)) => {{ - #[allow(dead_code)] - const fn checkargs() { - const SPEC: StackSpec = OpCode::$op.spec(); - // the error message is super ugly, but this static asserts we got the - // arity of the primop right. - const _: [();(arg_count!($($arg)*)) - SPEC.required as usize] = []; - } - checkargs(); - }} -} - -macro_rules! arg_count { - () => { 0 }; - ($arg:ident $($rest:ident)*) => { 1 + arg_count!($($rest)*) }; -} - // IMPLEMENTATION // arithmetic def_primop! { ADD(a, b) => arithmetic::add } diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index d0a7898e0..fc7f68923 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -1,40 +1,40 @@ #![allow(clippy::missing_safety_doc)] + +use crate::interpreter::StatusCode; use {crate::interpreter::stack::Stack, crate::interpreter::U256}; #[inline] -pub(crate) unsafe fn push(stack: &mut Stack, code: &[u8]) -> usize { - stack.push(if code.len() < LEN { - let mut padded = [0; LEN]; - padded[..code.len()].copy_from_slice(code); - U256::from_big_endian(&padded) - } else { - U256::from_big_endian(&code[..LEN]) - }); - LEN +pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result { + stack + .push(if code.len() < LEN { + let mut padded = [0; LEN]; + padded[..code.len()].copy_from_slice(code); + U256::from_big_endian(&padded) + } else { + U256::from_big_endian(&code[..LEN]) + }) + .map(|_| LEN) } #[inline] -pub(crate) unsafe fn dup(stack: &mut Stack) { - stack.push(*stack.get(HEIGHT - 1)); +pub(crate) fn dup(stack: &mut Stack) -> Result<(), StatusCode> { + stack.dup(HEIGHT) } #[inline] -pub(crate) unsafe fn swap(stack: &mut Stack) { - stack.swap_top(HEIGHT); +pub(crate) fn swap(stack: &mut Stack) -> Result<(), StatusCode> { + stack.swap_top(HEIGHT) } #[inline] -pub(crate) unsafe fn pop(stack: &mut Stack) { - stack.pop(); +pub(crate) fn pop(stack: &mut Stack) -> Result<(), StatusCode> { + stack.drop() } #[test] fn test_push_pad_right() { let mut stack = Stack::new(); - unsafe { - assert!(stack.ensure(1)); - assert_eq!(push::<4>(&mut stack, &[0xde, 0xad]), 4); - assert_eq!(stack.len(), 1); - assert_eq!(stack.get(0), &U256::from(0xdead0000u64)); - } + assert_eq!(push::<4>(&mut stack, &[0xde, 0xad]).unwrap(), 4); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::from(0xdead0000u64)); } diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 8b48e5fba..95ca0ea89 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -3,7 +3,6 @@ pub mod bytecode; pub mod execution; pub mod instructions; pub mod memory; -pub mod opcode; pub mod output; pub mod precompiles; pub mod stack; diff --git a/actors/evm/src/interpreter/opcode.rs b/actors/evm/src/interpreter/opcode.rs deleted file mode 100644 index 15b6a20dc..000000000 --- a/actors/evm/src/interpreter/opcode.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! EVM Opcodes as of Berlin Hard Fork -//! -//! On filecoin we will never have to replay blocks that are older -//! than the release date of the FVM-EVM runtime, so supporting -//! historic behavior is not needed. - -use crate::interpreter::output::StatusCode; - -macro_rules! def_opcodes { - ($($code:literal: $name:ident($stack:literal, $change:literal),)*) => { - #[repr(u8)] - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum OpCode { - $($name = $code,)* - } - #[derive(Copy, Clone, Debug)] - pub struct StackSpec { - pub required: u8, - pub changed: i8, - } - - impl std::fmt::Display for OpCode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.name()) - } - } - - impl From for u8 { - #[inline(always)] - fn from(op: OpCode) -> Self { - op as u8 - } - } - - impl TryFrom for OpCode { - type Error = StatusCode; - - fn try_from(op: u8) -> Result { - const fn codes() -> [bool; 256] { - let mut table = [false; 256]; - $(table[$code] = true;)* - table - } - const CODES: [bool; 256] = codes(); - if !CODES[op as usize] { - return Err(StatusCode::UndefinedInstruction); - } - - Ok(unsafe { std::mem::transmute(op) }) - } - } - - impl PartialEq for OpCode { - fn eq(&self, other: &u8) -> bool { - (*self as u8) == *other - } - } - - impl OpCode { - pub const fn spec(self) -> StackSpec { - const fn specs() -> [StackSpec; 256] { - let mut table = [StackSpec{required: 0, changed: 0}; 256]; - $(table[$code] = StackSpec{required: $stack, changed: $change};)* - table - } - const SPECS: [StackSpec; 256] = specs(); - SPECS[self as usize] - } - pub const fn name(self) -> &'static str { - const fn names() -> [&'static str; 256] { - let mut table = ["RESERVED"; 256]; - $(table[$code] = stringify!($name);)* - table - } - const NAMES: [&'static str; 256] = names(); - NAMES[self as usize] - } - } - } -} - -def_opcodes! { - 0x00: STOP(0, 0), - 0x01: ADD(2, -1), - 0x02: MUL(2, -1), - 0x03: SUB(2, -1), - 0x04: DIV(2, -1), - 0x05: SDIV(2, -1), - 0x06: MOD(2, -1), - 0x07: SMOD(2, -1), - 0x08: ADDMOD(3, -2), - 0x09: MULMOD(3, -2), - 0x0a: EXP(2, -1), - 0x0b: SIGNEXTEND(2, -1), - 0x10: LT(2, -1), - 0x11: GT(2, -1), - 0x12: SLT(2, -1), - 0x13: SGT(2, -1), - 0x14: EQ(2, -1), - 0x15: ISZERO(1, 0), - 0x16: AND(2, -1), - 0x17: OR(2, -1), - 0x18: XOR(2, -1), - 0x19: NOT(1, 0), - 0x1a: BYTE(2, -1), - 0x1b: SHL(2, -1), - 0x1c: SHR(2, -1), - 0x1d: SAR(2, -1), - 0x20: KECCAK256(2, -1), // SHA3 - 0x30: ADDRESS(0, 1), - 0x31: BALANCE(1, 0), - 0x32: ORIGIN(0, 1), - 0x33: CALLER(0, 1), - 0x34: CALLVALUE(0, 1), - 0x35: CALLDATALOAD(1, 0), - 0x36: CALLDATASIZE(0, 1), - 0x37: CALLDATACOPY(3, -3), - 0x38: CODESIZE(0, 1), - 0x39: CODECOPY(3, -3), - 0x3a: GASPRICE(0, 1), - 0x3b: EXTCODESIZE(1, 0), - 0x3c: EXTCODECOPY(4, -4), - 0x3d: RETURNDATASIZE(0, 1), - 0x3e: RETURNDATACOPY(3, -3), - 0x3f: EXTCODEHASH(1, 0), - 0x40: BLOCKHASH(1, 0), - 0x41: COINBASE(0, 1), - 0x42: TIMESTAMP(0, 1), - 0x43: NUMBER(0, 1), - 0x44: DIFFICULTY(0, 1), - 0x45: GASLIMIT(0, 1), - 0x46: CHAINID(0, 1), - 0x47: SELFBALANCE(0, 1), - 0x48: BASEFEE(0, 1), - 0x50: POP(1, -1), - 0x51: MLOAD(1, 0), - 0x52: MSTORE(2, -2), - 0x53: MSTORE8(2, -2), - 0x54: SLOAD(1, 0), - 0x55: SSTORE(2, -2), - 0x56: JUMP(1, -1), - 0x57: JUMPI(2, -2), - 0x58: PC(0, 1), - 0x59: MSIZE(0, 1), - 0x5a: GAS(0, 1), - 0x5b: JUMPDEST(0, 0), - 0x60: PUSH1(0, 1), - 0x61: PUSH2(0, 1), - 0x62: PUSH3(0, 1), - 0x63: PUSH4(0, 1), - 0x64: PUSH5(0, 1), - 0x65: PUSH6(0, 1), - 0x66: PUSH7(0, 1), - 0x67: PUSH8(0, 1), - 0x68: PUSH9(0, 1), - 0x69: PUSH10(0, 1), - 0x6a: PUSH11(0, 1), - 0x6b: PUSH12(0, 1), - 0x6c: PUSH13(0, 1), - 0x6d: PUSH14(0, 1), - 0x6e: PUSH15(0, 1), - 0x6f: PUSH16(0, 1), - 0x70: PUSH17(0, 1), - 0x71: PUSH18(0, 1), - 0x72: PUSH19(0, 1), - 0x73: PUSH20(0, 1), - 0x74: PUSH21(0, 1), - 0x75: PUSH22(0, 1), - 0x76: PUSH23(0, 1), - 0x77: PUSH24(0, 1), - 0x78: PUSH25(0, 1), - 0x79: PUSH26(0, 1), - 0x7a: PUSH27(0, 1), - 0x7b: PUSH28(0, 1), - 0x7c: PUSH29(0, 1), - 0x7d: PUSH30(0, 1), - 0x7e: PUSH31(0, 1), - 0x7f: PUSH32(0, 1), - 0x80: DUP1(1, 1), - 0x81: DUP2(2, 1), - 0x82: DUP3(3, 1), - 0x83: DUP4(4, 1), - 0x84: DUP5(5, 1), - 0x85: DUP6(6, 1), - 0x86: DUP7(7, 1), - 0x87: DUP8(8, 1), - 0x88: DUP9(9, 1), - 0x89: DUP10(10, 1), - 0x8a: DUP11(11, 1), - 0x8b: DUP12(12, 1), - 0x8c: DUP13(13, 1), - 0x8d: DUP14(14, 1), - 0x8e: DUP15(15, 1), - 0x8f: DUP16(16, 1), - 0x90: SWAP1(2, 0), - 0x91: SWAP2(3, 0), - 0x92: SWAP3(4, 0), - 0x93: SWAP4(5, 0), - 0x94: SWAP5(6, 0), - 0x95: SWAP6(7, 0), - 0x96: SWAP7(8, 0), - 0x97: SWAP8(9, 0), - 0x98: SWAP9(10, 0), - 0x99: SWAP10(11, 0), - 0x9a: SWAP11(12, 0), - 0x9b: SWAP12(13, 0), - 0x9c: SWAP13(14, 0), - 0x9d: SWAP14(15, 0), - 0x9e: SWAP15(16, 0), - 0x9f: SWAP16(17, 0), - 0xa0: LOG0(2, -2), - 0xa1: LOG1(3, -3), - 0xa2: LOG2(4, -4), - 0xa3: LOG3(5, -5), - 0xa4: LOG4(6, -6), - // 0xEF Reserved for EIP-3541 - 0xf0: CREATE(3, -2), - 0xf1: CALL(7, -6), - 0xf2: CALLCODE(7, -6), - 0xf3: RETURN(2, -2), - 0xf4: DELEGATECALL(6, -5), - 0xf5: CREATE2(4, -3), - 0xfa: STATICCALL(6, -5), - 0xfd: REVERT(2, -2), - 0xfe: INVALID(0, 0), - 0xff: SELFDESTRUCT(1, -1), -} diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index 27e24ca54..c220304c5 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -2,6 +2,8 @@ use crate::interpreter::U256; +use super::StatusCode; + /// Ethereum Yellow Paper (9.1) pub const STACK_SIZE: usize = 1024; @@ -10,78 +12,111 @@ const INITIAL_STACK_SIZE: usize = 32; /// EVM stack. #[derive(Clone, Debug)] pub struct Stack { - sk: Vec, - d: usize, + stack: Vec, } impl Stack { #[inline] pub fn new() -> Self { - Stack { sk: Vec::from([U256::zero(); INITIAL_STACK_SIZE]), d: 0 } + Stack { stack: Vec::with_capacity(INITIAL_STACK_SIZE) } } - #[inline] - pub fn require(&self, required: usize) -> bool { - required <= self.d + #[inline(always)] + pub fn len(&self) -> usize { + self.stack.len() } - #[inline] - pub fn ensure(&mut self, space: usize) -> bool { - let required = self.d + space; - - if required > self.sk.len() { - if required > STACK_SIZE { - return false; - } - - while required > self.sk.len() { - self.sk.resize(2 * self.sk.len(), U256::zero()); - } - } - true + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.stack.is_empty() } #[inline(always)] - pub fn len(&self) -> usize { - self.d + pub fn push_unchecked(&mut self, value: U256) { + self.stack.push(value); } #[inline(always)] - pub fn is_empty(&self) -> bool { - self.d == 0 + pub fn push(&mut self, value: U256) -> Result<(), StatusCode> { + if self.stack.len() >= STACK_SIZE { + Err(StatusCode::StackOverflow) + } else { + self.stack.push(value); + Ok(()) + } } #[inline] - pub unsafe fn get(&self, i: usize) -> &U256 { - let pos = self.d - i - 1; - self.sk.get_unchecked(pos) + pub fn pop_many(&mut self) -> Result<&[U256; S], StatusCode> { + if self.len() < S { + return Err(StatusCode::StackUnderflow); + } + 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<(), StatusCode> { + if self.stack.len() >= STACK_SIZE { + Err(StatusCode::StackOverflow) + } else { + Ok(()) + } } #[inline] - pub unsafe fn get_mut(&mut self, i: usize) -> &mut U256 { - let pos = self.d - i - 1; - self.sk.get_unchecked_mut(pos) + pub fn dup(&mut self, i: usize) -> Result<(), StatusCode> { + let len = self.stack.len(); + if len >= STACK_SIZE { + Err(StatusCode::StackOverflow) + } else if i > len { + Err(StatusCode::StackUnderflow) + } 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 unsafe fn push(&mut self, v: U256) { - *self.sk.get_unchecked_mut(self.d) = v; - self.d += 1; + pub fn swap_top(&mut self, i: usize) -> Result<(), StatusCode> { + let len = self.stack.len(); + if len < i { + return Err(StatusCode::StackUnderflow); + } + self.stack.swap(len - i - 1, len - 1); + Ok(()) } #[inline] - pub unsafe fn pop(&mut self) -> U256 { - self.d -= 1; - *self.sk.get_unchecked(self.d) + pub fn pop(&mut self) -> Result { + self.stack.pop().ok_or(StatusCode::StackUnderflow) } #[inline] - pub unsafe fn swap_top(&mut self, i: usize) { - let top = self.d - 1; - let pos = self.d - i - 1; - let tmp = *self.sk.get_unchecked(top); - *self.sk.get_unchecked_mut(top) = *self.sk.get_unchecked(pos); - *self.sk.get_unchecked_mut(pos) = tmp; + pub fn drop(&mut self) -> Result<(), StatusCode> { + if self.stack.pop().is_some() { + Ok(()) + } else { + Err(StatusCode::StackUnderflow) + } } } @@ -90,24 +125,3 @@ impl Default for Stack { Self::new() } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn stack() { - let mut stack = Stack::new(); - - let items: [u128; 4] = [0xde, 0xad, 0xbe, 0xef]; - - for (i, item) in items.iter().copied().enumerate() { - unsafe { stack.push(item.into()) }; - assert_eq!(stack.len(), i + 1); - } - - assert_eq!(unsafe { *stack.get(2) }, U256::from(0xad)); - assert_eq!(unsafe { stack.pop() }, U256::from(0xef)); - assert_eq!(unsafe { *stack.get(2) }, U256::from(0xde)); - } -} diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index c8b03d65b..b8b000e94 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -1,5 +1,5 @@ use etk_asm::ingest::Ingest; -use evm::interpreter::opcode::OpCode::*; +use evm::interpreter::execution::opcodes; use fil_actor_evm as evm; const PRELUDE: &str = r#" @@ -67,23 +67,23 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a + 1 // RETURN ; let mut constructor_code = vec![ - PUSH4 as u8, + 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, - DUP1 as u8, - PUSH4 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, - PUSH1 as u8, + opcodes::PUSH1, 0x00, - CODECOPY as u8, - PUSH1 as u8, + opcodes::CODECOPY, + opcodes::PUSH1, 0x00, - RETURN as u8, + opcodes::RETURN, ]; // the actual contract code let mut contract_code = Vec::new(); From 8b6ab831157fad4e2035d68b595b795b6c65fef9 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 3 Dec 2022 20:22:00 +0200 Subject: [PATCH 165/339] EVM: optimize push ins for u64 sizes (#907) * optimize push for u64 * somewhat nicer macro expansion --- .../evm/src/interpreter/instructions/stack.rs | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index fc7f68923..77c8ab662 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -3,17 +3,45 @@ use crate::interpreter::StatusCode; use {crate::interpreter::stack::Stack, crate::interpreter::U256}; +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] pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result { - stack - .push(if code.len() < LEN { - let mut padded = [0; LEN]; - padded[..code.len()].copy_from_slice(code); - U256::from_big_endian(&padded) - } else { - U256::from_big_endian(&code[..LEN]) - }) - .map(|_| LEN) + 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 + 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] From 8d920b79fd761dfd14280183eb350587c772286d Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 5 Dec 2022 23:16:50 +0200 Subject: [PATCH 166/339] reserve gas for return on CALL (#908) * reserve gas for return on CALL better handling of edge cases handle 0 gas limit correctly, we still have to reserve simplify * don't be sexist * fix tests; we call gas_available now on every call with gas 0. * user gas is not sacred it turns out * use gas avail for min when user specifies gas * take user's thing if it already reserves. * use min * lift common code --- actors/evm/src/interpreter/instructions/call.rs | 17 +++++++++++++---- actors/evm/src/interpreter/precompiles.rs | 4 ++-- actors/evm/tests/call.rs | 3 +++ actors/evm/tests/precompile.rs | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index e4293a857..0adde4ed8 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -199,7 +199,7 @@ pub fn call_generic( if precompiles::Precompiles::::is_precompile(&dst) { let context = PrecompileContext { is_static: matches!(kind, CallKind::StaticCall) || system.readonly, - gas, + gas_limit: effective_gas_limit(system, gas), value, }; @@ -254,8 +254,7 @@ pub fn call_generic( // TODO: support IPLD codecs #758 let params = RawBytes::serialize(BytesSer(input_data))?; let value = TokenAmount::from(&value); - let gas_limit = - if !gas.is_zero() { Some(gas.to_u64_saturating()) } else { None }; + let gas_limit = effective_gas_limit(system, gas); let read_only = kind == CallKind::StaticCall; system.send_with_gas(&dst_addr, method, params, value, gas_limit, read_only) } @@ -272,7 +271,7 @@ pub fn call_generic( Method::InvokeContractDelegate as u64, RawBytes::serialize(¶ms)?, TokenAmount::from(&value), - if !gas.is_zero() { Some(gas.to_u64_saturating()) } else { None }, + effective_gas_limit(system, gas), system.readonly, ) } @@ -313,3 +312,13 @@ pub fn call_generic( Ok(U256::from(call_result)) } + +fn effective_gas_limit(system: &System, gas: U256) -> Option { + let gas_rsvp = (63 * system.rt.gas_available()) / 64; + Some(if gas.is_zero() { + gas_rsvp + } else { + let gas = gas.to_u64_saturating(); + std::cmp::min(gas, gas_rsvp) + }) +} diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index f541a576a..f2fe07e31 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -250,7 +250,7 @@ impl From for PrecompileError { #[derive(Debug, PartialEq, Eq, Default)] pub struct PrecompileContext { pub is_static: bool, - pub gas: U256, + pub gas_limit: Option, pub value: U256, } @@ -485,7 +485,7 @@ pub fn call_actor( method, RawBytes::from(input_data.to_vec()), TokenAmount::from(&ctx.value), - if !ctx.gas.is_zero() { Some(ctx.gas.to_u64_saturating()) } else { None }, + ctx.gas_limit, ctx.is_static, ) }; diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 6a5751259..23db2c15a 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -89,6 +89,7 @@ fn test_call() { 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, @@ -157,6 +158,7 @@ fn test_call_convert_to_send() { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; + rt.expect_gas_available(10_000_000_000u64); rt.expect_send( target, METHOD_SEND, @@ -295,6 +297,7 @@ fn test_callactor_inner(exit_code: ExitCode) { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; + rt.expect_gas_available(10_000_000_000u64); rt.expect_send( target, 0x42, diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 2227001e3..bf294df9b 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -44,6 +44,7 @@ fn test_precompile_hash() { // 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"); From 020e1a56c57318ba39f1ceb3be9bc3706ac84992 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 5 Dec 2022 13:21:44 -0800 Subject: [PATCH 167/339] Error early on insufficient balance for `Create`/`Create2` (#906) * wip * exit early on insufficient balance * rustfmt --- .../src/interpreter/instructions/lifecycle.rs | 7 +++++++ actors/evm/tests/create.rs | 20 +------------------ actors/evm/tests/ext_opcodes.rs | 2 +- actors/evm/tests/util.rs | 2 +- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 9af802b37..2c6c8662a 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -55,6 +55,9 @@ pub fn create( 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).map_err(|_| StatusCode::InvalidMemoryAccess)?; @@ -85,6 +88,10 @@ pub fn create2( // see `create()` overall TODOs let endowment = TokenAmount::from(&endowment); + if endowment > system.rt.current_balance() { + return Ok(U256::zero()); + } + let input_region = get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index 9c69c4e41..0f33dde11 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -128,7 +128,7 @@ fn test_create() { rt.expect_send( EAM_ACTOR_ADDR, CREATE2_METHOD_NUM, - RawBytes::serialize(create2_params.clone()).unwrap(), + RawBytes::serialize(create2_params).unwrap(), TokenAmount::from_atto(1), RawBytes::serialize(fake_ret).unwrap(), ExitCode::OK, @@ -143,15 +143,6 @@ fn test_create() { // not enough funds -- create2 { - rt.expect_send( - EAM_ACTOR_ADDR, - CREATE2_METHOD_NUM, - RawBytes::serialize(create2_params).unwrap(), - TokenAmount::from_atto(1), - RawBytes::serialize(fake_ret).unwrap(), - ExitCode::OK, - ); - let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(&result[..], &[0; 32]); } @@ -160,16 +151,7 @@ fn test_create() { // not enough funds -- create { - // TODO this nonce is broken create_params.nonce += 3; - rt.expect_send( - EAM_ACTOR_ADDR, - CREATE_METHOD_NUM, - RawBytes::serialize(create_params).unwrap(), - TokenAmount::from_atto(1), - RawBytes::serialize(fake_ret).unwrap(), - ExitCode::OK, - ); let result = util::invoke_contract(&mut rt, &contract_params); assert_eq!(&result[..], &[0; 32]); diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 7eadf5cdf..24d2d0bdb 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -196,7 +196,7 @@ native_actor: assert_eq!(U256::from_big_endian(&result), U256::from(&bytecode_cid.hash().digest()[..32])); rt.reset(); - util::invoke_contract_expect_err( + util::invoke_contract_expect_abort( &mut rt, &util::dispatch_num_word(1), evm::interpreter::StatusCode::InvalidArgument( diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 6f820ca91..aa0d56767 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -63,7 +63,7 @@ pub fn invoke_contract(rt: &mut MockRuntime, input_data: &[u8]) -> Vec { } #[allow(dead_code)] -pub fn invoke_contract_expect_err(rt: &mut MockRuntime, input_data: &[u8], expect: StatusCode) { +pub fn invoke_contract_expect_abort(rt: &mut MockRuntime, input_data: &[u8], expect: StatusCode) { rt.expect_validate_caller_any(); let err = rt .call::( From 43ebbffa12970d2f01dfce042d76707458945794 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 6 Dec 2022 11:17:35 -0800 Subject: [PATCH 168/339] EVM: rewrite get_actor_code_cid to get_actor_type (#911) * rewrite get_actor_code_cid to get_actor_type * fix review comments & write tests * fix more review its * update tests and use a helper fuction --- actors/evm/src/interpreter/precompiles.rs | 68 ++++++++++++-- actors/evm/tests/ext_opcodes.rs | 8 +- actors/evm/tests/precompile.rs | 105 ++++++++++++++++++++++ actors/evm/tests/util.rs | 9 +- 4 files changed, 176 insertions(+), 14 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles.rs index f2fe07e31..27859bf2b 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, convert::TryInto, marker::PhantomData, slice::ChunksExact use super::{StatusCode, System, U256}; -use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::runtime::{builtins::Type, Runtime}; use fvm_ipld_encoding::RawBytes; use fvm_shared::{ address::Address, @@ -33,6 +33,26 @@ pub fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileErr } } +/// Native Type of a given contract +#[repr(u32)] +pub enum NativeType { + NonExistent = 0, + // user actors are flattened to "system" + /// System includes any singletons not otherwise defined. + System = 1, + Embryo = 2, + Account = 3, + StorageProvider = 4, + EVMContract = 5, + OtherTypes = 6, +} + +impl NativeType { + fn word_vec(self) -> Vec { + U256::from(self as u32).to_bytes().to_vec() + } +} + struct Parameter(pub T); impl<'a> TryFrom<&'a [u8; 64]> for Parameter { @@ -292,7 +312,7 @@ const fn gen_precompiles() -> [PrecompileFn; 14] { // FIL precompiles resolve_address, // lookup_address 0x0a lookup_address, // resolve_address 0x0b - get_actor_code_cid, // get code cid 0x0c + get_actor_type, // get actor type 0x0c get_randomness, // rand 0x0d call_actor, // call_actor 0x0e } @@ -336,16 +356,52 @@ fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u // --- Precompiles --- -/// Read right padded BE encoded low u64 ID address from a u256 word -/// returns encoded CID or an empty array if actor not found -fn get_actor_code_cid( +/// Read right padded BE encoded low u64 ID address from a u256 word. +/// Returns variant of [`BuiltinType`] encoded as a u256 word. +fn get_actor_type( system: &mut System, input: &[u8], _: PrecompileContext, ) -> PrecompileResult { + const LAST_SYSTEM_ACTOR_ID: u64 = 32; + let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); let id = Parameter::::try_from(&id_bytes)?.0; - Ok(system.rt.get_actor_code_cid(&id).unwrap_or_default().to_bytes()) + + if id < LAST_SYSTEM_ACTOR_ID { + // known to be system actors + Ok(NativeType::System.word_vec()) + } else { + // resolve type from code CID + let builtin_type = system + .rt + .get_actor_code_cid(&id) + .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); + + let builtin_type = match builtin_type { + Some(t) => match t { + Type::Account => NativeType::Account, + Type::System => NativeType::System, + Type::Embryo => NativeType::Embryo, + Type::EVM => NativeType::EVMContract, + Type::Miner => NativeType::StorageProvider, + // Others + Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, + // Singletons + Type::Market + | Type::Power + | Type::Init + | Type::Cron + | Type::Reward + | Type::VerifiedRegistry + | Type::DataCap + | Type::EAM => NativeType::System, + }, + None => NativeType::NonExistent, + }; + + Ok(builtin_type.word_vec()) + } } /// Params: diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 24d2d0bdb..0df406098 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -10,15 +10,9 @@ use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use fvm_shared::{IDENTITY_HASH, IPLD_RAW}; -use lazy_static::lazy_static; mod util; - -lazy_static! { - pub static ref DUMMY_ACTOR_CODE_ID: Cid = - Cid::new_v1(IPLD_RAW, Multihash::wrap(IDENTITY_HASH, b"foobarboxy").unwrap()); -} +use util::DUMMY_ACTOR_CODE_ID; #[test] fn test_extcodesize() { diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index bf294df9b..ce2d33d87 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -2,9 +2,16 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; +use fil_actors_runtime::test_utils::{ + MockRuntime, ACCOUNT_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, + MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, +}; +use fvm_shared::address::Address as FILAddress; mod util; +use util::DUMMY_ACTOR_CODE_ID; + #[allow(dead_code)] pub fn magic_precompile_contract() -> Vec { let init = r#" @@ -56,3 +63,101 @@ fn test_precompile_hash() { hex::encode(expected) ); } + +#[test] +fn test_native_actor_type() { + let bytecode = { + 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 (get_actor_type precompile) +push1 0x0c + +# gas +push1 0x00 + +call + +# copy result to mem 0x00 (overwrites input data) +returndatasize +push1 0x00 +push1 0x00 +returndatacopy + +# return +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("native_precompiles", init, body).unwrap() + }; + + use evm::interpreter::precompiles::NativeType; + + 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); + + // f0 31 is a system actor + let system_target = FILAddress::new_id(31); + rt.set_address_actor_type(system_target, *DUMMY_ACTOR_CODE_ID); + + // f0 101 is an account + let account_target = FILAddress::new_id(101); + rt.set_address_actor_type(account_target, *ACCOUNT_ACTOR_CODE_ID); + + // f0 102 is an embryo + let embryo_target = FILAddress::new_id(102); + rt.set_address_actor_type(embryo_target, *EMBRYO_ACTOR_CODE_ID); + + // f0 103 is a storage provider + let miner_target = FILAddress::new_id(103); + rt.set_address_actor_type(miner_target, *MINER_ACTOR_CODE_ID); + + // f0 104 is a multisig + let other_target = FILAddress::new_id(104); + rt.set_address_actor_type(other_target, *MULTISIG_ACTOR_CODE_ID); + + fn id_to_vec(src: &FILAddress) -> Vec { + U256::from(src.id().unwrap()).to_bytes().to_vec() + } + + fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(rt, &id_to_vec(&id)); + rt.verify(); + assert_eq!(&U256::from(expected as u32).to_bytes(), result.as_slice()); + rt.reset(); + } + + test_type(&mut rt, evm_target, NativeType::EVMContract); + test_type(&mut rt, system_target, NativeType::System); + test_type(&mut rt, account_target, NativeType::Account); + test_type(&mut rt, embryo_target, NativeType::Embryo); + test_type(&mut rt, miner_target, NativeType::StorageProvider); + test_type(&mut rt, other_target, NativeType::OtherTypes); + test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); +} diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index aa0d56767..29bf340b5 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,10 +1,12 @@ +use cid::Cid; use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; use fil_actors_runtime::{ runtime::builtins::Type, test_utils::*, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, }; use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; -use fvm_shared::address::Address; +use fvm_shared::{address::Address, IDENTITY_HASH, IPLD_RAW}; +use lazy_static::lazy_static; #[allow(dead_code)] pub fn construct_and_verify(initcode: Vec) -> MockRuntime { @@ -82,3 +84,8 @@ pub fn dispatch_num_word(method_num: u8) -> [u8; 32] { word[3] = method_num; word } + +lazy_static! { + pub static ref DUMMY_ACTOR_CODE_ID: Cid = + Cid::new_v1(IPLD_RAW, Multihash::wrap(IDENTITY_HASH, b"foobarboxy").unwrap()); +} From 0aa4a4cc746a74398f3660c8fde15f6fa68dcd38 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 6 Dec 2022 16:26:33 -0800 Subject: [PATCH 169/339] EVM: Split precompiles into smaller, more readable files (#916) split precompiles into smaller, more readable files --- .../{precompiles.rs => precompiles/evm.rs} | 623 +----------------- actors/evm/src/interpreter/precompiles/fvm.rs | 246 +++++++ actors/evm/src/interpreter/precompiles/mod.rs | 129 ++++ .../src/interpreter/precompiles/parameter.rs | 215 ++++++ actors/evm/tests/call.rs | 2 +- 5 files changed, 627 insertions(+), 588 deletions(-) rename actors/evm/src/interpreter/{precompiles.rs => precompiles/evm.rs} (63%) create mode 100644 actors/evm/src/interpreter/precompiles/fvm.rs create mode 100644 actors/evm/src/interpreter/precompiles/mod.rs create mode 100644 actors/evm/src/interpreter/precompiles/parameter.rs diff --git a/actors/evm/src/interpreter/precompiles.rs b/actors/evm/src/interpreter/precompiles/evm.rs similarity index 63% rename from actors/evm/src/interpreter/precompiles.rs rename to actors/evm/src/interpreter/precompiles/evm.rs index 27859bf2b..7596e440a 100644 --- a/actors/evm/src/interpreter/precompiles.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -1,597 +1,31 @@ -use std::{borrow::Cow, convert::TryInto, marker::PhantomData, slice::ChunksExact}; - -use super::{StatusCode, System, U256}; - -use fil_actors_runtime::runtime::{builtins::Type, Runtime}; -use fvm_ipld_encoding::RawBytes; +use fil_actors_runtime::runtime::Runtime; use fvm_shared::{ - address::Address, bigint::BigUint, crypto::{ hash::SupportedHashes, signature::{SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, }, - econ::TokenAmount, }; -use num_traits::FromPrimitive; use num_traits::{One, Zero}; use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1, G2}; use uint::byteorder::{ByteOrder, LE}; -pub use substrate_bn::{CurveError, FieldError, GroupError}; +use crate::interpreter::{ + precompiles::{parameter::read_right_pad, PrecompileError}, + System, U256, +}; + +use super::{ + parameter::{PaddedChunks, U256Reader}, + PrecompileContext, PrecompileResult, +}; lazy_static::lazy_static! { pub(crate) static ref SECP256K1: BigUint = BigUint::from_bytes_be(&hex_literal::hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); } -/// ensures top bits are zeroed -pub fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { - if src[..S] != [0u8; S] { - Err(PrecompileError::InvalidInput) - } else { - Ok(()) - } -} - -/// Native Type of a given contract -#[repr(u32)] -pub enum NativeType { - NonExistent = 0, - // user actors are flattened to "system" - /// System includes any singletons not otherwise defined. - System = 1, - Embryo = 2, - Account = 3, - StorageProvider = 4, - EVMContract = 5, - OtherTypes = 6, -} - -impl NativeType { - fn word_vec(self) -> Vec { - U256::from(self as u32).to_bytes().to_vec() - } -} - -struct Parameter(pub T); - -impl<'a> TryFrom<&'a [u8; 64]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 64]) -> Result { - let x = Fq::from_u256(U256::from_big_endian(&value[0..32]).into())?; - let y = Fq::from_u256(U256::from_big_endian(&value[32..64]).into())?; - - Ok(if x.is_zero() && y.is_zero() { - Parameter(G1::zero()) - } else { - Parameter(AffineG1::new(x, y)?.into()) - }) - } -} - -impl<'a> From<&'a [u8; 32]> for Parameter<[u8; 32]> { - fn from(value: &'a [u8; 32]) -> Self { - Self(*value) - } -} - -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<28>(value)?; - // Type ensures our remaining len == 4 - Ok(Self(u32::from_be_bytes(value[28..].try_into().unwrap()))) - } -} - -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<28>(value)?; - // Type ensures our remaining len == 4 - Ok(Self(i32::from_be_bytes(value[28..].try_into().unwrap()))) - } -} - -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<31>(value)?; - Ok(Self(value[31])) - } -} - -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<24>(value)?; - // Type ensures our remaining len == 8 - Ok(Self(u64::from_be_bytes(value[24..].try_into().unwrap()))) - } -} - -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<24>(value)?; - // Type ensures our remaining len == 8 - Ok(Self(i64::from_be_bytes(value[24..].try_into().unwrap()))) - } -} - -impl<'a> From<&'a [u8; 32]> for Parameter { - fn from(value: &'a [u8; 32]) -> Self { - Self(U256::from_big_endian(value)) - } -} - -type U256Reader<'a> = PaddedChunks<'a, u8, 32>; - -// will be nicer with https://github.com/rust-lang/rust/issues/74985 -/// Wrapper around `ChunksExact` that pads instead of overflowing. -/// Also provides a nice API interface for reading Parameters from input -struct PaddedChunks<'a, T: Sized + Copy, const CHUNK_SIZE: usize> { - slice: &'a [T], - chunks: ChunksExact<'a, T>, - exhausted: bool, -} - -impl<'a, T: Sized + Copy, const CHUNK_SIZE: usize> PaddedChunks<'a, T, CHUNK_SIZE> { - pub(super) fn new(slice: &'a [T]) -> Self { - Self { slice, chunks: slice.chunks_exact(CHUNK_SIZE), exhausted: false } - } - - pub fn next(&mut self) -> Option<&[T; CHUNK_SIZE]> { - self.chunks.next().map(|s| s.try_into().unwrap()) - } - - pub fn next_padded(&mut self) -> [T; CHUNK_SIZE] - where - T: Default, - { - if self.chunks.len() > 0 { - self.next().copied().unwrap_or([T::default(); CHUNK_SIZE]) - } else if self.exhausted() { - [T::default(); CHUNK_SIZE] - } else { - self.exhausted = true; - let mut buf = [T::default(); CHUNK_SIZE]; - let remainder = self.chunks.remainder(); - buf[..remainder.len()].copy_from_slice(remainder); - buf - } - } - - pub fn exhausted(&self) -> bool { - self.exhausted - } - - pub fn remaining_len(&self) -> usize { - if self.exhausted { - 0 - } else { - self.chunks.len() * CHUNK_SIZE + self.chunks.remainder().len() - } - } - - pub fn chunks_read(&self) -> usize { - let total_chunks = self.slice.len() / CHUNK_SIZE; - let unread_chunks = self.chunks.len(); - total_chunks - unread_chunks - } - - // remaining unpadded slice of unread items - pub fn remaining_slice(&self) -> &[T] { - let start = self.slice.len() - self.remaining_len(); - &self.slice[start..] - } - - // // tries to read an unpadded and exact (aligned) parameter - #[allow(unused)] - pub fn next_param(&mut self) -> Result - where - Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, - { - Parameter::::try_from(self.next().ok_or(PrecompileError::IncorrectInputSize)?) - .map(|a| a.0) - } - - // tries to read a parameter with padding - pub fn next_param_padded(&mut self) -> Result - where - T: Default, - Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, - { - Parameter::::try_from(&self.next_padded()).map(|a| a.0) - } - - #[allow(unused)] - pub fn next_into_param_padded(&mut self) -> V - where - T: Default, - Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, - { - Parameter::::from(&self.next_padded()).0 - } - - // read a parameter with padding - pub fn next_into_param(&mut self) -> Result - where - T: Default, - Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, - { - self.next().map(|p| Parameter::::from(p).0).ok_or(PrecompileError::IncorrectInputSize) - } -} - -#[derive(Debug)] -pub enum PrecompileError { - EcErr(CurveError), - EcGroupErr(GroupError), - InvalidInput, // TODO merge with below? - IncorrectInputSize, - OutOfGas, - CallActorError(StatusCode), -} - -impl From for StatusCode { - fn from(src: PrecompileError) -> Self { - match src { - PrecompileError::CallActorError(e) => e, - _ => StatusCode::PrecompileFailure, - } - } -} - -impl From for PrecompileError { - fn from(src: CurveError) -> Self { - PrecompileError::EcErr(src) - } -} - -impl From for PrecompileError { - fn from(src: FieldError) -> Self { - PrecompileError::EcErr(src.into()) - } -} - -impl From for PrecompileError { - fn from(src: GroupError) -> Self { - PrecompileError::EcGroupErr(src) - } -} - -#[derive(Debug, PartialEq, Eq, Default)] -pub struct PrecompileContext { - pub is_static: bool, - pub gas_limit: Option, - pub value: U256, -} - -// really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy -type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; -pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec - -/// Generates a list of precompile smart contracts, index + 1 is the address (another option is to make an enum) -const fn gen_precompiles() -> [PrecompileFn; 14] { - macro_rules! precompiles { - ($($precompile:ident,)*) => { - mod trampolines { - use fil_actors_runtime::runtime::Runtime; - use crate::System; - use super::{PrecompileContext, PrecompileResult}; - $( - #[inline(always)] - pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { - super::$precompile(&mut *s, inp, ctx) - } - )* - } - [ - $(trampolines::$precompile,)* - ] - } - } - - precompiles! { - ec_recover, // ecrecover 0x01 - sha256, // SHA2-256 0x02 - ripemd160, // ripemd160 0x03 - identity, // identity 0x04 - modexp, // modexp 0x05 - ec_add, // ecAdd 0x06 - ec_mul, // ecMul 0x07 - ec_pairing, // ecPairing 0x08 - blake2f, // blake2f 0x09 - // FIL precompiles - resolve_address, // lookup_address 0x0a - lookup_address, // resolve_address 0x0b - get_actor_type, // get actor type 0x0c - get_randomness, // rand 0x0d - call_actor, // call_actor 0x0e - } -} - -pub struct Precompiles(PhantomData); - -impl Precompiles { - const PRECOMPILES: [PrecompileFn; 14] = gen_precompiles(); - const MAX_PRECOMPILE: U256 = { - let mut limbs = [0u64; 4]; - limbs[0] = Self::PRECOMPILES.len() as u64; - U256(limbs) - }; - - // Precompile Context will be flattened to None if not calling the call_actor precompile - pub fn call_precompile( - system: &mut System, - precompile_addr: U256, - input: &[u8], - context: PrecompileContext, - ) -> PrecompileResult { - unsafe { Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](system, input, context) } - } - - #[inline] - pub fn is_precompile(addr: &U256) -> bool { - !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE - } -} - -// It is uncomfortable how much Eth pads everything... -fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { - let mut input: Cow<[u8]> = input.into(); - let input_len = input.len(); - if len > input_len { - input.to_mut().resize(len, 0); - } - input -} - -// --- Precompiles --- - -/// Read right padded BE encoded low u64 ID address from a u256 word. -/// Returns variant of [`BuiltinType`] encoded as a u256 word. -fn get_actor_type( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - const LAST_SYSTEM_ACTOR_ID: u64 = 32; - - let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); - let id = Parameter::::try_from(&id_bytes)?.0; - - if id < LAST_SYSTEM_ACTOR_ID { - // known to be system actors - Ok(NativeType::System.word_vec()) - } else { - // resolve type from code CID - let builtin_type = system - .rt - .get_actor_code_cid(&id) - .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); - - let builtin_type = match builtin_type { - Some(t) => match t { - Type::Account => NativeType::Account, - Type::System => NativeType::System, - Type::Embryo => NativeType::Embryo, - Type::EVM => NativeType::EVMContract, - Type::Miner => NativeType::StorageProvider, - // Others - Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, - // Singletons - Type::Market - | Type::Power - | Type::Init - | Type::Cron - | Type::Reward - | Type::VerifiedRegistry - | Type::DataCap - | Type::EAM => NativeType::System, - }, - None => NativeType::NonExistent, - }; - - Ok(builtin_type.word_vec()) - } -} - -/// Params: -/// -/// | Param | Value | -/// |------------------|---------------------------| -/// | randomness_type | U256 - low i32: `Chain`(0) OR `Beacon`(1) | -/// | personalization | U256 - low i64 | -/// | randomness_epoch | U256 - low i64 | -/// | entropy_length | U256 - low u32 | -/// | entropy | input\[32..] (right padded)| -/// -/// any bytes in between values are ignored -/// -/// Returns empty array if invalid randomness type -/// Errors if unable to fetch randomness -fn get_randomness( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut input_params = U256Reader::new(input); - - #[derive(num_derive::FromPrimitive)] - #[repr(i32)] - enum RandomnessType { - Chain = 0, - Beacon = 1, - } - - let randomness_type = RandomnessType::from_i32(input_params.next_param_padded::()?); - let personalization = input_params.next_param_padded::()?; - let rand_epoch = input_params.next_param_padded::()?; - let entropy_len = input_params.next_param_padded::()?; - - debug_assert_eq!(input_params.chunks_read(), 4); - - let entropy = read_right_pad(input_params.remaining_slice(), entropy_len as usize); - - let randomness = match randomness_type { - Some(RandomnessType::Chain) => system - .rt - .user_get_randomness_from_chain(personalization, rand_epoch, &entropy) - .map(|a| a.to_vec()), - Some(RandomnessType::Beacon) => system - .rt - .user_get_randomness_from_beacon(personalization, rand_epoch, &entropy) - .map(|a| a.to_vec()), - None => Ok(Vec::new()), - }; - - randomness.map_err(|_| PrecompileError::InvalidInput) -} - -/// Read BE encoded low u64 ID address from a u256 word -/// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found -fn lookup_address( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut id_bytes = U256Reader::new(input); - let id = id_bytes.next_param_padded::()?; - - let address = system.rt.lookup_address(id); - let ab = match address { - Some(a) => a.to_bytes(), - None => Vec::new(), - }; - Ok(ab) -} - -/// Reads a FIL encoded address -/// Resolves a FIL encoded address into an ID address -/// returns BE encoded u64 or empty array if nothing found -fn resolve_address( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut input_params = U256Reader::new(input); - - let len = input_params.next_param_padded::()? as usize; - let addr = match Address::from_bytes(&read_right_pad(input_params.remaining_slice(), len)) { - Ok(o) => o, - Err(_) => return Ok(Vec::new()), - }; - Ok(system.rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) -} - -/// Errors: -/// TODO should just give 0s? -/// - `IncorrectInputSize` if offset is larger than total input length -/// - `InvalidInput` if supplied address bytes isnt a filecoin address -/// -/// Returns: -/// -/// `[int256 exit_code, uint codec, uint offset, uint size, []bytes ]` -/// -/// for exit_code: -/// - negative values are system errors -/// - positive are user errors (from the called actor) -/// - 0 is success -pub fn call_actor( - system: &mut System, - input: &[u8], - ctx: PrecompileContext, -) -> PrecompileResult { - // ----- Input Parameters ------- - - let mut input_params = U256Reader::new(input); - - let method: u64 = input_params.next_param_padded()?; - let codec: u64 = input_params.next_param_padded()?; - // TODO only CBOR for now - if codec != fvm_ipld_encoding::DAG_CBOR { - return Err(PrecompileError::InvalidInput); - } - - let address_size = input_params.next_param_padded::()? as usize; - let send_data_size = input_params.next_param_padded::()? as usize; - - // ------ Begin Call ------- - - let result = { - // REMOVEME: closes https://github.com/filecoin-project/ref-fvm/issues/1018 - - let start = input_params.remaining_slice(); - let bytes = read_right_pad(start, send_data_size + address_size); - - let input_data = &bytes[..send_data_size]; - let address = &bytes[send_data_size..send_data_size + address_size]; - let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; - - system.send_with_gas( - &address, - method, - RawBytes::from(input_data.to_vec()), - TokenAmount::from(&ctx.value), - ctx.gas_limit, - ctx.is_static, - ) - }; - - // ------ 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(mut ae) => { - // TODO handle revert - // TODO https://github.com/filecoin-project/ref-fvm/issues/1020 - // put error number from call into revert - let exit_code = U256::from(ae.exit_code().value()); - - // no return only exit code - (exit_code, ae.take_data()) - } - Ok(ret) => (U256::zero(), ret), - }; - - const NUM_OUTPUT_PARAMS: u32 = 4; - - // codec of return data - // TODO hardcoded to CBOR for now - let codec = U256::from(fvm_ipld_encoding::DAG_CBOR); - let offset = U256::from(NUM_OUTPUT_PARAMS * 32); - let size = U256::from(data.len() as u32); - - let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS as usize * 32 + data.len()); - output.extend_from_slice(&exit_code.to_bytes()); - output.extend_from_slice(&codec.to_bytes()); - output.extend_from_slice(&offset.to_bytes()); - output.extend_from_slice(&size.to_bytes()); - // NOTE: - // we dont pad out to 32 bytes here, the idea being that users will already be in the "everythig is bytes" mode - // and will want re-pack align and whatever else by themselves - output.extend_from_slice(data.bytes()); - output - }; - - Ok(output) -} - -// ---------------- Normal EVM Precompiles ------------------ - /// recover a secp256k1 pubkey from a hash, recovery byte, and a signature -fn ec_recover( +pub(super) fn ec_recover( system: &mut System, input: &[u8], _: PrecompileContext, @@ -645,7 +79,7 @@ fn ec_recover( } /// hash with sha2-256 -fn sha256( +pub(super) fn sha256( system: &mut System, input: &[u8], _: PrecompileContext, @@ -654,7 +88,7 @@ fn sha256( } /// hash with ripemd160 -fn ripemd160( +pub(super) fn ripemd160( system: &mut System, input: &[u8], _: PrecompileContext, @@ -663,7 +97,7 @@ fn ripemd160( } /// data copy -fn identity( +pub(super) fn identity( _: &mut System, input: &[u8], _: PrecompileContext, @@ -673,7 +107,11 @@ fn identity( // https://eips.ethereum.org/EIPS/eip-198 /// modulus exponent a number -fn modexp(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { +pub(super) fn modexp( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let input = read_right_pad(input, 96); // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. @@ -726,7 +164,7 @@ fn modexp(_: &mut System, input: &[u8], _: PrecompileContext) - Ok(output) } -fn curve_to_vec(curve: G1) -> Vec { +pub(super) fn curve_to_vec(curve: G1) -> Vec { AffineG1::from_jacobian(curve) .map(|product| { let mut output = vec![0; 64]; @@ -738,7 +176,11 @@ fn curve_to_vec(curve: G1) -> Vec { } /// add 2 points together on an elliptic curve -fn ec_add(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { +pub(super) fn ec_add( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let mut input_params: PaddedChunks = PaddedChunks::new(input); let point1 = input_params.next_param_padded()?; let point2 = input_params.next_param_padded()?; @@ -747,7 +189,11 @@ fn ec_add(_: &mut System, input: &[u8], _: PrecompileContext) - } /// multiply a point on an elliptic curve by a scalar value -fn ec_mul(_: &mut System, input: &[u8], _: PrecompileContext) -> PrecompileResult { +pub(super) fn ec_mul( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { let input = read_right_pad(input, 96); let mut input_params: PaddedChunks = PaddedChunks::new(&input); let point = input_params.next_param_padded()?; @@ -761,7 +207,7 @@ fn ec_mul(_: &mut System, input: &[u8], _: PrecompileContext) - } /// pairs multple groups of twisted bn curves -fn ec_pairing( +pub(super) fn ec_pairing( _: &mut System, input: &[u8], _: PrecompileContext, @@ -823,7 +269,7 @@ fn ec_pairing( } /// https://eips.ethereum.org/EIPS/eip-152 -fn blake2f( +pub(super) fn blake2f( _: &mut System, input: &[u8], _: PrecompileContext, @@ -1022,7 +468,10 @@ mod tests { // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { - use super::{GroupError, MockRuntime}; + use substrate_bn::GroupError; + + use super::MockRuntime; + use crate::interpreter::{ precompiles::{ec_add, ec_mul, ec_pairing, PrecompileContext, PrecompileError}, System, diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs new file mode 100644 index 000000000..ffd8e647a --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -0,0 +1,246 @@ +use fil_actors_runtime::runtime::{builtins::Type, Runtime}; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::{address::Address, econ::TokenAmount}; +use num_traits::FromPrimitive; + +use crate::interpreter::{ + precompiles::{ + parameter::{read_right_pad, Parameter}, + NativeType, + }, + System, U256, +}; + +use super::{parameter::U256Reader, PrecompileContext, PrecompileError, PrecompileResult}; + +/// Read right padded BE encoded low u64 ID address from a u256 word. +/// Returns variant of [`BuiltinType`] encoded as a u256 word. +pub(super) fn get_actor_type( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + const LAST_SYSTEM_ACTOR_ID: u64 = 32; + + let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); + let id = Parameter::::try_from(&id_bytes)?.0; + + if id < LAST_SYSTEM_ACTOR_ID { + // known to be system actors + Ok(NativeType::System.word_vec()) + } else { + // resolve type from code CID + let builtin_type = system + .rt + .get_actor_code_cid(&id) + .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); + + let builtin_type = match builtin_type { + Some(t) => match t { + Type::Account => NativeType::Account, + Type::System => NativeType::System, + Type::Embryo => NativeType::Embryo, + Type::EVM => NativeType::EVMContract, + Type::Miner => NativeType::StorageProvider, + // Others + Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, + // Singletons + Type::Market + | Type::Power + | Type::Init + | Type::Cron + | Type::Reward + | Type::VerifiedRegistry + | Type::DataCap + | Type::EAM => NativeType::System, + }, + None => NativeType::NonExistent, + }; + + Ok(builtin_type.word_vec()) + } +} + +/// Params: +/// +/// | Param | Value | +/// |------------------|---------------------------| +/// | randomness_type | U256 - low i32: `Chain`(0) OR `Beacon`(1) | +/// | personalization | U256 - low i64 | +/// | randomness_epoch | U256 - low i64 | +/// | entropy_length | U256 - low u32 | +/// | entropy | input\[32..] (right padded)| +/// +/// any bytes in between values are ignored +/// +/// Returns empty array if invalid randomness type +/// Errors if unable to fetch randomness +pub(super) fn get_randomness( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut input_params = U256Reader::new(input); + + #[derive(num_derive::FromPrimitive)] + #[repr(i32)] + enum RandomnessType { + Chain = 0, + Beacon = 1, + } + + let randomness_type = RandomnessType::from_i32(input_params.next_param_padded::()?); + let personalization = input_params.next_param_padded::()?; + let rand_epoch = input_params.next_param_padded::()?; + let entropy_len = input_params.next_param_padded::()?; + + debug_assert_eq!(input_params.chunks_read(), 4); + + let entropy = read_right_pad(input_params.remaining_slice(), entropy_len as usize); + + let randomness = match randomness_type { + Some(RandomnessType::Chain) => system + .rt + .user_get_randomness_from_chain(personalization, rand_epoch, &entropy) + .map(|a| a.to_vec()), + Some(RandomnessType::Beacon) => system + .rt + .user_get_randomness_from_beacon(personalization, rand_epoch, &entropy) + .map(|a| a.to_vec()), + None => Ok(Vec::new()), + }; + + randomness.map_err(|_| PrecompileError::InvalidInput) +} + +/// Read BE encoded low u64 ID address from a u256 word +/// Looks up and returns the other address (encoded f2 or f4 addresses) of an ID address, returning empty array if not found +pub(super) fn lookup_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut id_bytes = U256Reader::new(input); + let id = id_bytes.next_param_padded::()?; + + let address = system.rt.lookup_address(id); + let ab = match address { + Some(a) => a.to_bytes(), + None => Vec::new(), + }; + Ok(ab) +} + +/// Reads a FIL encoded address +/// Resolves a FIL encoded address into an ID address +/// returns BE encoded u64 or empty array if nothing found +pub(super) fn resolve_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut input_params = U256Reader::new(input); + + let len = input_params.next_param_padded::()? as usize; + let addr = match Address::from_bytes(&read_right_pad(input_params.remaining_slice(), len)) { + Ok(o) => o, + Err(_) => return Ok(Vec::new()), + }; + Ok(system.rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) +} + +/// Errors: +/// TODO should just give 0s? +/// - `IncorrectInputSize` if offset is larger than total input length +/// - `InvalidInput` if supplied address bytes isnt a filecoin address +/// +/// Returns: +/// +/// `[int256 exit_code, uint codec, uint offset, uint size, []bytes ]` +/// +/// 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 { + // ----- Input Parameters ------- + + let mut input_params = U256Reader::new(input); + + let method: u64 = input_params.next_param_padded()?; + let codec: u64 = input_params.next_param_padded()?; + // TODO only CBOR for now + if codec != fvm_ipld_encoding::DAG_CBOR { + return Err(PrecompileError::InvalidInput); + } + + let address_size = input_params.next_param_padded::()? as usize; + let send_data_size = input_params.next_param_padded::()? as usize; + + // ------ Begin Call ------- + + let result = { + // REMOVEME: closes https://github.com/filecoin-project/ref-fvm/issues/1018 + + let start = input_params.remaining_slice(); + let bytes = read_right_pad(start, send_data_size + address_size); + + let input_data = &bytes[..send_data_size]; + let address = &bytes[send_data_size..send_data_size + address_size]; + let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; + + system.send_with_gas( + &address, + method, + RawBytes::from(input_data.to_vec()), + TokenAmount::from(&ctx.value), + ctx.gas_limit, + ctx.is_static, + ) + }; + + // ------ 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(mut ae) => { + // TODO handle revert + // TODO https://github.com/filecoin-project/ref-fvm/issues/1020 + // put error number from call into revert + let exit_code = U256::from(ae.exit_code().value()); + + // no return only exit code + (exit_code, ae.take_data()) + } + Ok(ret) => (U256::zero(), ret), + }; + + const NUM_OUTPUT_PARAMS: u32 = 4; + + // codec of return data + // TODO hardcoded to CBOR for now + let codec = U256::from(fvm_ipld_encoding::DAG_CBOR); + let offset = U256::from(NUM_OUTPUT_PARAMS * 32); + let size = U256::from(data.len() as u32); + + let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS as usize * 32 + data.len()); + output.extend_from_slice(&exit_code.to_bytes()); + output.extend_from_slice(&codec.to_bytes()); + output.extend_from_slice(&offset.to_bytes()); + output.extend_from_slice(&size.to_bytes()); + // NOTE: + // we dont pad out to 32 bytes here, the idea being that users will already be in the "everythig is bytes" mode + // and will want re-pack align and whatever else by themselves + output.extend_from_slice(data.bytes()); + 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..693c4a647 --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -0,0 +1,129 @@ +use std::marker::PhantomData; + +use fil_actors_runtime::runtime::Runtime; +use substrate_bn::{CurveError, GroupError}; + +use super::{StatusCode, System, U256}; + +mod evm; +mod fvm; +pub mod parameter; + +use evm::{blake2f, ec_add, ec_mul, ec_pairing, ec_recover, identity, modexp, ripemd160, sha256}; +use fvm::{call_actor, get_actor_type, get_randomness, lookup_address, resolve_address}; + +// really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy +type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; +pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec + +/// Generates a list of precompile smart contracts, index + 1 is the address. (another option is to make an enum) +const fn gen_precompiles() -> [PrecompileFn; 14] { + macro_rules! precompiles { + ($($precompile:ident,)*) => { + mod trampolines { + use fil_actors_runtime::runtime::Runtime; + use crate::System; + use super::{PrecompileContext, PrecompileResult}; + $( + #[inline(always)] + pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { + super::$precompile(&mut *s, inp, ctx) + } + )* + } + [ + $(trampolines::$precompile,)* + ] + } + } + + precompiles! { + ec_recover, // ecrecover 0x01 + sha256, // SHA2-256 0x02 + ripemd160, // ripemd160 0x03 + identity, // identity 0x04 + modexp, // modexp 0x05 + ec_add, // ecAdd 0x06 + ec_mul, // ecMul 0x07 + ec_pairing, // ecPairing 0x08 + blake2f, // blake2f 0x09 + // FIL precompiles + resolve_address, // lookup_address 0x0a + lookup_address, // resolve_address 0x0b + get_actor_type, // get actor type 0x0c + get_randomness, // rand 0x0d + call_actor, // call_actor 0x0e + } +} + +pub struct Precompiles(PhantomData); + +impl Precompiles { + const PRECOMPILES: [PrecompileFn; 14] = gen_precompiles(); + const MAX_PRECOMPILE: U256 = { + let mut limbs = [0u64; 4]; + limbs[0] = Self::PRECOMPILES.len() as u64; + U256(limbs) + }; + + // Precompile Context will be flattened to None if not calling the call_actor precompile + pub fn call_precompile( + system: &mut System, + precompile_addr: U256, + input: &[u8], + context: PrecompileContext, + ) -> PrecompileResult { + unsafe { Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](system, input, context) } + } + + #[inline] + pub fn is_precompile(addr: &U256) -> bool { + !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE + } +} + +#[derive(Debug)] +pub enum PrecompileError { + EcErr(CurveError), + EcGroupErr(GroupError), + InvalidInput, // TODO merge with below? + IncorrectInputSize, + OutOfGas, + CallActorError(StatusCode), +} + +impl From for StatusCode { + fn from(src: PrecompileError) -> Self { + match src { + PrecompileError::CallActorError(e) => e, + _ => StatusCode::PrecompileFailure, + } + } +} + +#[derive(Debug, PartialEq, Eq, Default)] +pub struct PrecompileContext { + pub is_static: bool, + pub gas_limit: Option, + pub value: U256, +} + +/// Native Type of a given contract +#[repr(u32)] +pub enum NativeType { + NonExistent = 0, + // user actors are flattened to "system" + /// System includes any singletons not otherwise defined. + System = 1, + Embryo = 2, + Account = 3, + StorageProvider = 4, + EVMContract = 5, + OtherTypes = 6, +} + +impl NativeType { + fn word_vec(self) -> Vec { + U256::from(self as u32).to_bytes().to_vec() + } +} diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/interpreter/precompiles/parameter.rs new file mode 100644 index 000000000..7c10ee654 --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/parameter.rs @@ -0,0 +1,215 @@ +use std::{borrow::Cow, slice::ChunksExact}; + +use substrate_bn::{AffineG1, FieldError, Fq, Group, GroupError, G1}; + +use crate::interpreter::U256; + +use super::PrecompileError; + +impl From for PrecompileError { + fn from(src: FieldError) -> Self { + PrecompileError::EcErr(src.into()) + } +} + +impl From for PrecompileError { + fn from(src: GroupError) -> Self { + PrecompileError::EcGroupErr(src) + } +} + +// It is uncomfortable how much Eth pads everything... +pub(super) fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { + let mut input: Cow<[u8]> = input.into(); + let input_len = input.len(); + if len > input_len { + input.to_mut().resize(len, 0); + } + input +} + +/// ensures top bits are zeroed +pub fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { + if src[..S] != [0u8; S] { + Err(PrecompileError::InvalidInput) + } else { + Ok(()) + } +} + +pub(super) struct Parameter(pub T); + +impl<'a> TryFrom<&'a [u8; 64]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 64]) -> Result { + let x = Fq::from_u256(U256::from_big_endian(&value[0..32]).into())?; + let y = Fq::from_u256(U256::from_big_endian(&value[32..64]).into())?; + + Ok(if x.is_zero() && y.is_zero() { + Parameter(G1::zero()) + } else { + Parameter(AffineG1::new(x, y)?.into()) + }) + } +} + +impl<'a> From<&'a [u8; 32]> for Parameter<[u8; 32]> { + fn from(value: &'a [u8; 32]) -> Self { + Self(*value) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<28>(value)?; + // Type ensures our remaining len == 4 + Ok(Self(u32::from_be_bytes(value[28..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<28>(value)?; + // Type ensures our remaining len == 4 + Ok(Self(i32::from_be_bytes(value[28..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<31>(value)?; + Ok(Self(value[31])) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<24>(value)?; + // Type ensures our remaining len == 8 + Ok(Self(u64::from_be_bytes(value[24..].try_into().unwrap()))) + } +} + +impl<'a> TryFrom<&'a [u8; 32]> for Parameter { + type Error = PrecompileError; + + fn try_from(value: &'a [u8; 32]) -> Result { + assert_zero_bytes::<24>(value)?; + // Type ensures our remaining len == 8 + Ok(Self(i64::from_be_bytes(value[24..].try_into().unwrap()))) + } +} + +impl<'a> From<&'a [u8; 32]> for Parameter { + fn from(value: &'a [u8; 32]) -> Self { + Self(U256::from_big_endian(value)) + } +} + +pub(super) type U256Reader<'a> = PaddedChunks<'a, u8, 32>; + +// will be nicer with https://github.com/rust-lang/rust/issues/74985 +/// Wrapper around `ChunksExact` that pads instead of overflowing. +/// Also provides a nice API interface for reading Parameters from input +pub(super) struct PaddedChunks<'a, T: Sized + Copy, const CHUNK_SIZE: usize> { + slice: &'a [T], + chunks: ChunksExact<'a, T>, + exhausted: bool, +} + +impl<'a, T: Sized + Copy, const CHUNK_SIZE: usize> PaddedChunks<'a, T, CHUNK_SIZE> { + pub(super) fn new(slice: &'a [T]) -> Self { + Self { slice, chunks: slice.chunks_exact(CHUNK_SIZE), exhausted: false } + } + + pub fn next(&mut self) -> Option<&[T; CHUNK_SIZE]> { + self.chunks.next().map(|s| s.try_into().unwrap()) + } + + pub fn next_padded(&mut self) -> [T; CHUNK_SIZE] + where + T: Default, + { + if self.chunks.len() > 0 { + self.next().copied().unwrap_or([T::default(); CHUNK_SIZE]) + } else if self.exhausted() { + [T::default(); CHUNK_SIZE] + } else { + self.exhausted = true; + let mut buf = [T::default(); CHUNK_SIZE]; + let remainder = self.chunks.remainder(); + buf[..remainder.len()].copy_from_slice(remainder); + buf + } + } + + pub fn exhausted(&self) -> bool { + self.exhausted + } + + pub fn remaining_len(&self) -> usize { + if self.exhausted { + 0 + } else { + self.chunks.len() * CHUNK_SIZE + self.chunks.remainder().len() + } + } + + pub fn chunks_read(&self) -> usize { + let total_chunks = self.slice.len() / CHUNK_SIZE; + let unread_chunks = self.chunks.len(); + total_chunks - unread_chunks + } + + // remaining unpadded slice of unread items + pub fn remaining_slice(&self) -> &[T] { + let start = self.slice.len() - self.remaining_len(); + &self.slice[start..] + } + + // // tries to read an unpadded and exact (aligned) parameter + #[allow(unused)] + pub fn next_param(&mut self) -> Result + where + Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, + { + Parameter::::try_from(self.next().ok_or(PrecompileError::IncorrectInputSize)?) + .map(|a| a.0) + } + + // tries to read a parameter with padding + pub fn next_param_padded(&mut self) -> Result + where + T: Default, + Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, + { + Parameter::::try_from(&self.next_padded()).map(|a| a.0) + } + + #[allow(unused)] + pub fn next_into_param_padded(&mut self) -> V + where + T: Default, + Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, + { + Parameter::::from(&self.next_padded()).0 + } + + // read a parameter with padding + pub fn next_into_param(&mut self) -> Result + where + T: Default, + Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, + { + self.next().map(|p| Parameter::::from(p).0).ok_or(PrecompileError::IncorrectInputSize) + } +} diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 23db2c15a..7289a168d 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -324,7 +324,7 @@ fn test_callactor_inner(exit_code: ExitCode) { impl CallActorReturn { pub fn read(src: &[u8]) -> Self { - use fil_actor_evm::interpreter::precompiles::assert_zero_bytes; + use fil_actor_evm::interpreter::precompiles::parameter::assert_zero_bytes; assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values"); let bytes = &src[..32]; From 3c417b8553634587110fbefe1937bfed4df20103 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 7 Dec 2022 11:54:34 -0500 Subject: [PATCH 170/339] Merge master and integration/builtin-api into next (#918) * Remove the market actor state mutation pattern (#734) Fixes https://github.com/filecoin-project/builtin-actors/issues/656 * Proof of concept exported API for Account actor (#797) * Export stable methods for public access (#807) * Export Datacap Actor methods * Export Init Actor methods * Export Market Actor methods * Export Miner Actor methods * Export Multisig Actor methods * Export Verifreg Actor methods * Address review * Restrict internal APIs of all actors (#809) * Exported API method for market actor escrow/locked balance (#812) * Power actor: Add exported getters for raw power (#810) * Power actor: Add exported getters for raw power * FRC-XXXX is FRC-0042 * Power actor: network_raw_power: Return this_epoch_raw_byte_power * Power actor: miner_raw_power: Return whether above consensus min power * Power actor: types: serialize one-element structs transparently * Address review * Miner actor: Add exported getters for info and monies (#811) * Miner actor: Add exported getters for info and monies * Tweak comment * Miner actor: Replace GetWorker and GetControls with IsControllingAddress * Miner actor: Add exported GetAvailableBalance * Miner actor: Add exported GetVestingFunds * Miner actor: Remove exported monies getters * Miner actor: types: serialize one-element structs transparently * Address review * Address review * Built-in market API for deal proposal metadata (#818) * Call exported authenticate method from PSD (#829) Co-authored-by: zenground0 * Drop CALLER_TYPES_SIGNABLE and signable caller validation (#821) * Market actor: Minor improvements to two exported getters (#826) * Market actor: GetDealTermExported: Return (start_epoch, duration) * Market actor: Export getter for deal total price * Exported API for market deal activation state (#819) * Paych actor: Drop account req, use AuthenticateMessage to verify sigs (#824) * Paych actor: Drop account req, use AuthenticateMessage to verify sigs * Address review * Address review * Account actor: Deprecate AuthenticateMessage (#856) * Market actor: Export PublishStorageDeals (#857) * Miner: Export several more methods (#863) * Miner: Export ChangeWorkerAddress * Miner: Export ChangePeerID * Miner: Export WithdrawBalance * Miner: Export ChangeMultiaddrs * Miner: Export ConfirmUpdateWorkerKey * Miner: Export RepayDebt * Miner: Export ChangeOwnerAddress * Miner: Add exported getters for PeerID & multiaddrs * Miner: Refactor: Rename ConfirmUpdateWorkerKey to ConfirmChangeWorkerAddress * Power actor: Export methods to CreateMiner and get miner counts (#868) * Power: Export CreateMiner * Power Actor: Export MinerCount and MinerConsensusCount * Update actors/power/src/lib.rs Co-authored-by: Alex <445306+anorth@users.noreply.github.com> Co-authored-by: Alex <445306+anorth@users.noreply.github.com> * Verifreg: Export AddVerifiedClient and GetClaims (#873) * Verifreg: Rename AddVerifierClientParams to AddVerifiedClientParams * Verifreg: Export AddVerifiedClient and GetClaims * Datacap actor: Modify exported methods (#909) * Datacap: Export Mint and Destroy * Datacap actor: Deprecate all internal methods * Datacap actor: Rename BalanceOf to Balance * Datacap actor: Add Granularity method * fix: comments on newly exported methods (#910) Co-authored-by: RK Co-authored-by: Alex <445306+anorth@users.noreply.github.com> Co-authored-by: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Co-authored-by: zenground0 --- actors/datacap/src/lib.rs | 77 +- actors/datacap/src/types.rs | 6 + actors/datacap/tests/datacap_actor_test.rs | 65 +- actors/datacap/tests/harness/mod.rs | 34 +- actors/init/src/lib.rs | 1 + actors/market/src/ext.rs | 2 +- actors/market/src/lib.rs | 460 +++----- actors/market/src/state.rs | 996 +++++++++++------- actors/miner/src/lib.rs | 6 +- actors/verifreg/src/ext.rs | 20 +- actors/verifreg/src/lib.rs | 10 +- actors/verifreg/tests/harness/mod.rs | 2 +- test_vm/src/util.rs | 16 +- test_vm/tests/datacap_tests.rs | 24 +- test_vm/tests/verifreg_remove_datacap_test.rs | 6 +- 15 files changed, 881 insertions(+), 844 deletions(-) diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index 129934508..8b56ab733 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -51,27 +51,29 @@ lazy_static! { #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - // Non-standard. - Mint = 2, - Destroy = 3, - // Static method numbers for token standard methods, for private use. - Name = 10, - Symbol = 11, - TotalSupply = 12, - BalanceOf = 13, - Transfer = 14, - TransferFrom = 15, - IncreaseAllowance = 16, - DecreaseAllowance = 17, - RevokeAllowance = 18, - Burn = 19, - BurnFrom = 20, - Allowance = 21, + // Deprecated in v10 + // Mint = 2, + // Destroy = 3, + // Name = 10, + // Symbol = 11, + // TotalSupply = 12, + // BalanceOf = 13, + // Transfer = 14, + // TransferFrom = 15, + // IncreaseAllowance = 16, + // DecreaseAllowance = 17, + // RevokeAllowance = 18, + // Burn = 19, + // BurnFrom = 20, + // Allowance = 21, // Method numbers derived from FRC-0042 standards + MintExported = frc42_dispatch::method_hash!("Mint"), + DestroyExported = frc42_dispatch::method_hash!("Destroy"), NameExported = frc42_dispatch::method_hash!("Name"), SymbolExported = frc42_dispatch::method_hash!("Symbol"), + GranularityExported = frc42_dispatch::method_hash!("GranularityExported"), TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), - BalanceOfExported = frc42_dispatch::method_hash!("BalanceOf"), + BalanceExported = frc42_dispatch::method_hash!("Balance"), TransferExported = frc42_dispatch::method_hash!("Transfer"), TransferFromExported = frc42_dispatch::method_hash!("TransferFrom"), IncreaseAllowanceExported = frc42_dispatch::method_hash!("IncreaseAllowance"), @@ -108,6 +110,11 @@ impl Actor { Ok("DCAP".to_string()) } + pub fn granularity(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + Ok(GranularityReturn { granularity: DATACAP_GRANULARITY }) + } + pub fn total_supply(rt: &mut impl Runtime, _: ()) -> Result { rt.validate_immediate_caller_accept_any()?; let mut st: State = rt.state()?; @@ -116,7 +123,7 @@ impl Actor { Ok(token.total_supply()) } - pub fn balance_of(rt: &mut impl Runtime, address: Address) -> Result { + pub fn balance(rt: &mut impl Runtime, address: Address) -> Result { // NOTE: mutability and method caller here are awkward for a read-only call rt.validate_immediate_caller_accept_any()?; let mut st: State = rt.state()?; @@ -477,59 +484,63 @@ impl ActorCode for Actor { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Mint) => { + Some(Method::MintExported) => { let ret = Self::mint(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "mint result") } - Some(Method::Destroy) => { + Some(Method::DestroyExported) => { let ret = Self::destroy(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "destroy result") } - Some(Method::Name) | Some(Method::NameExported) => { + Some(Method::NameExported) => { let ret = Self::name(rt)?; serialize(&ret, "name result") } - Some(Method::Symbol) | Some(Method::SymbolExported) => { + Some(Method::SymbolExported) => { let ret = Self::symbol(rt)?; serialize(&ret, "symbol result") } - Some(Method::TotalSupply) | Some(Method::TotalSupplyExported) => { + Some(Method::GranularityExported) => { + let ret = Self::granularity(rt)?; + serialize(&ret, "granularity result") + } + Some(Method::TotalSupplyExported) => { let ret = Self::total_supply(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "total_supply result") } - Some(Method::BalanceOf) | Some(Method::BalanceOfExported) => { - let ret = Self::balance_of(rt, cbor::deserialize_params(params)?)?; + Some(Method::BalanceExported) => { + let ret = Self::balance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "balance_of result") } - Some(Method::Transfer) | Some(Method::TransferExported) => { + Some(Method::TransferExported) => { let ret = Self::transfer(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer result") } - Some(Method::TransferFrom) | Some(Method::TransferFromExported) => { + Some(Method::TransferFromExported) => { let ret = Self::transfer_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer_from result") } - Some(Method::IncreaseAllowance) | Some(Method::IncreaseAllowanceExported) => { + Some(Method::IncreaseAllowanceExported) => { let ret = Self::increase_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "increase_allowance result") } - Some(Method::DecreaseAllowance) | Some(Method::DecreaseAllowanceExported) => { + Some(Method::DecreaseAllowanceExported) => { let ret = Self::decrease_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "decrease_allowance result") } - Some(Method::RevokeAllowance) | Some(Method::RevokeAllowanceExported) => { + Some(Method::RevokeAllowanceExported) => { Self::revoke_allowance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Burn) | Some(Method::BurnExported) => { + Some(Method::BurnExported) => { let ret = Self::burn(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn result") } - Some(Method::BurnFrom) | Some(Method::BurnFromExported) => { + Some(Method::BurnFromExported) => { let ret = Self::burn_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn_from result") } - Some(Method::Allowance) | Some(Method::AllowanceExported) => { + Some(Method::AllowanceExported) => { let ret = Self::allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "allowance result") } diff --git a/actors/datacap/src/types.rs b/actors/datacap/src/types.rs index 4e9ded1a2..de4e87b0b 100644 --- a/actors/datacap/src/types.rs +++ b/actors/datacap/src/types.rs @@ -22,3 +22,9 @@ pub struct DestroyParams { } impl Cbor for DestroyParams {} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GranularityReturn { + pub granularity: u64, +} diff --git a/actors/datacap/tests/datacap_actor_test.rs b/actors/datacap/tests/datacap_actor_test.rs index 2c90367b0..c672e825a 100644 --- a/actors/datacap/tests/datacap_actor_test.rs +++ b/actors/datacap/tests/datacap_actor_test.rs @@ -16,7 +16,10 @@ lazy_static! { mod construction { use crate::*; + use fil_actor_datacap::{Actor, GranularityReturn, Method, DATACAP_GRANULARITY}; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; + use fvm_ipld_encoding::RawBytes; + use fvm_shared::MethodNum; #[test] fn construct_with_verified() { @@ -24,6 +27,15 @@ mod construction { let h = Harness { governor: VERIFIED_REGISTRY_ACTOR_ADDR }; h.construct_and_verify(&mut rt, &h.governor); h.check_state(&rt); + + rt.expect_validate_caller_any(); + let ret: GranularityReturn = rt + .call::(Method::GranularityExported as MethodNum, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + assert_eq!(ret.granularity, DATACAP_GRANULARITY) } } @@ -34,9 +46,7 @@ mod mint { use fil_actor_datacap::{Actor, Method, MintParams, INFINITE_ALLOWANCE}; use fil_actors_runtime::cbor::serialize; - use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, MARKET_ACTOR_CODE_ID, - }; + use fil_actors_runtime::test_utils::{expect_abort_contains_message, MARKET_ACTOR_CODE_ID}; use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use std::ops::Sub; @@ -53,32 +63,17 @@ mod mint { assert_eq!(amt, ret.supply); assert_eq!(amt, ret.balance); assert_eq!(amt, h.get_supply(&rt)); - assert_eq!(amt, h.get_balance(&rt, &*ALICE)); + assert_eq!(amt, h.get_balance(&mut rt, &*ALICE)); let ret = h.mint(&mut rt, &*BOB, &amt, vec![]).unwrap(); assert_eq!(&amt * 2, ret.supply); assert_eq!(amt, ret.balance); assert_eq!(&amt * 2, h.get_supply(&rt)); - assert_eq!(amt, h.get_balance(&rt, &*BOB)); + assert_eq!(amt, h.get_balance(&mut rt, &*BOB)); h.check_state(&rt); } - #[test] - fn requires_builtin_caller() { - let (mut rt, h) = make_harness(); - let amt = TokenAmount::from_whole(1); - let params = MintParams { to: *ALICE, amount: amt, operators: vec![] }; - - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), - ); - h.check_state(&rt); - } - #[test] fn requires_verifreg_caller() { let (mut rt, h) = make_harness(); @@ -90,7 +85,10 @@ mod mint { expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "caller address", - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), + rt.call::( + Method::MintExported as MethodNum, + &serialize(¶ms, "params").unwrap(), + ), ); h.check_state(&rt); } @@ -206,11 +204,8 @@ mod transfer { mod destroy { use crate::{make_harness, ALICE, BOB}; use fil_actor_datacap::DestroyParams; - use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, - }; + use fil_actors_runtime::test_utils::{expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID}; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; - use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; @@ -218,21 +213,6 @@ mod destroy { use fil_actors_runtime::cbor::serialize; use fvm_shared::error::ExitCode; - #[test] - fn requires_builtin_caller() { - let (mut rt, h) = make_harness(); - let amt = TokenAmount::from_whole(1); - let params = DestroyParams { owner: *ALICE, amount: amt }; - - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), - ); - h.check_state(&rt); - } - #[test] fn only_governor_allowed() { let (mut rt, h) = make_harness(); @@ -248,7 +228,10 @@ mod destroy { expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "caller address", - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), + rt.call::( + Method::DestroyExported as MethodNum, + &serialize(¶ms, "params").unwrap(), + ), ); // Destroying from 0 allowance having governor works diff --git a/actors/datacap/tests/harness/mod.rs b/actors/datacap/tests/harness/mod.rs index ee3e7e506..8f8f35b7f 100644 --- a/actors/datacap/tests/harness/mod.rs +++ b/actors/datacap/tests/harness/mod.rs @@ -93,8 +93,10 @@ impl Harness { let params = MintParams { to: *to, amount: amount.clone(), operators }; rt.set_caller(*VERIFREG_ACTOR_CODE_ID, VERIFIED_REGISTRY_ACTOR_ADDR); - let ret = - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::MintExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -111,8 +113,10 @@ impl Harness { let params = DestroyParams { owner: *owner, amount: amount.clone() }; rt.set_caller(*VERIFREG_ACTOR_CODE_ID, VERIFIED_REGISTRY_ACTOR_ADDR); - let ret = - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::DestroyExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -155,8 +159,10 @@ impl Harness { ); let params = TransferParams { to: *to, amount: amount.clone(), operator_data }; - let ret = - rt.call::(Method::Transfer as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::TransferExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -202,7 +208,7 @@ impl Harness { let params = TransferFromParams { to: *to, from: *from, amount: amount.clone(), operator_data }; let ret = rt.call::( - Method::TransferFrom as MethodNum, + Method::TransferFromExported as MethodNum, &serialize(¶ms, "params")?, )?; @@ -216,8 +222,18 @@ impl Harness { } // Reads a balance from state directly. - pub fn get_balance(&self, rt: &MockRuntime, address: &Address) -> TokenAmount { - rt.get_state::().token.get_balance(rt.store(), address.id().unwrap()).unwrap() + pub fn get_balance(&self, rt: &mut MockRuntime, address: &Address) -> TokenAmount { + rt.expect_validate_caller_any(); + let ret = rt + .call::( + Method::BalanceExported as MethodNum, + &serialize(&address, "params").unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + ret } // Reads allowance from state directly diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index bb2a7ef37..15ffc8d4a 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -36,6 +36,7 @@ pub enum Method { InstallCode = 4, // Method numbers derived from FRC-0042 standards ExecExported = frc42_dispatch::method_hash!("Exec"), + // TODO: Export new methods if appropriate } /// Init actor diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index 63d62b1d2..da9099ef2 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -80,7 +80,7 @@ pub mod verifreg { } pub mod datacap { - pub const TRANSFER_FROM_METHOD: u64 = 15; + pub const TRANSFER_FROM_METHOD: u64 = frc42_dispatch::method_hash!("TransferFrom"); } pub mod reward { diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index a050d3439..f3afaa882 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -99,9 +99,7 @@ impl Actor { pub fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; - let st = State::new(rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create market state") - })?; + let st = State::new(rt.store())?; rt.create(&st)?; Ok(()) } @@ -123,23 +121,7 @@ impl Actor { let (nominal, _, _) = escrow_address(rt, &provider_or_client)?; rt.transaction(|st: &mut State, rt| { - let mut msm = st.mutator(rt.store()); - msm.with_escrow_table(Permission::Write) - .with_locked_table(Permission::Write) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; - - msm.escrow_table.as_mut().unwrap().add(&nominal, &msg_value).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to add balance to escrow table", - ) - })?; - - msm.commit_state()?; - + st.add_balance_to_escrow_table(rt.store(), &nominal, &msg_value)?; Ok(()) })?; @@ -162,34 +144,7 @@ impl Actor { rt.validate_immediate_caller_is(&approved)?; let amount_extracted = rt.transaction(|st: &mut State, rt| { - let mut msm = st.mutator(rt.store()); - msm.with_escrow_table(Permission::Write) - .with_locked_table(Permission::Write) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; - - // The withdrawable amount might be slightly less than nominal - // depending on whether or not all relevant entries have been processed - // by cron - let min_balance = msm.locked_table.as_ref().unwrap().get(&nominal).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance") - })?; - - let ex = msm - .escrow_table - .as_mut() - .unwrap() - .subtract_with_minimum(&nominal, ¶ms.amount, &min_balance) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to subtract from escrow table", - ) - })?; - - msm.commit_state()?; + let ex = st.withdraw_balance_from_escrow_table(rt.store(), &nominal, ¶ms.amount)?; Ok(ex) })?; @@ -290,14 +245,7 @@ impl Actor { let mut valid_input_bf = BitField::default(); let curr_epoch = rt.curr_epoch(); - let mut state: State = rt.state::()?; - let store = rt.store(); - let mut msm = state.mutator(store); - msm.with_pending_proposals(Permission::ReadOnly) - .with_escrow_table(Permission::ReadOnly) - .with_locked_table(Permission::ReadOnly) - .build() - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load msm"))?; + let state: State = rt.state()?; for (di, mut deal) in params.deals.into_iter().enumerate() { // drop malformed deals @@ -332,12 +280,7 @@ impl Actor { client_lockup += deal.proposal.client_balance_requirement(); let client_balance_ok = - msm.balance_covered(Address::new_id(client_id), &client_lockup).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to check client balance coverage", - ) - })?; + state.balance_covered(rt.store(), Address::new_id(client_id), &client_lockup)?; if !client_balance_ok { info!("invalid deal: {}: insufficient client funds to cover proposal cost", di); @@ -346,14 +289,11 @@ impl Actor { let mut provider_lockup = total_provider_lockup.clone(); provider_lockup += &deal.proposal.provider_collateral; - let provider_balance_ok = msm - .balance_covered(Address::new_id(provider_id), &provider_lockup) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to check provider balance coverage", - ) - })?; + let provider_balance_ok = state.balance_covered( + rt.store(), + Address::new_id(provider_id), + &provider_lockup, + )?; if !provider_balance_ok { info!("invalid deal: {}: insufficient provider funds to cover proposal cost", di); @@ -371,13 +311,8 @@ impl Actor { // check proposalCids for duplication within message batch // check state PendingProposals for duplication across messages - let duplicate_in_state = - msm.pending_deals.as_ref().unwrap().has(&pcid.to_bytes()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to check for existence of deal proposal", - ) - })?; + let duplicate_in_state = state.has_pending_deal(rt.store(), pcid)?; + let duplicate_in_message = proposal_cid_lookup.contains(&pcid); if duplicate_in_state || duplicate_in_message { info!("invalid deal {}: cannot publish duplicate deal proposal", di); @@ -456,63 +391,47 @@ impl Actor { let mut new_deal_ids = Vec::with_capacity(valid_deals.len()); rt.transaction(|st: &mut State, rt| { - let mut msm = st.mutator(rt.store()); - msm.with_pending_proposals(Permission::Write) - .with_deal_proposals(Permission::Write) - .with_deals_by_epoch(Permission::Write) - .with_escrow_table(Permission::Write) - .with_locked_table(Permission::Write) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; + let mut pending_deals: Vec = vec![]; + let mut deal_proposals: Vec<(DealID, DealProposal)> = vec![]; + let mut deals_by_epoch: Vec<(ChainEpoch, DealID)> = vec![]; + let mut pending_deal_allocation_ids: Vec<(BytesKey, AllocationID)> = vec![]; + // All storage dealProposals will be added in an atomic transaction; this operation will be unrolled if any of them fails. // This should only fail on programmer error because all expected invalid conditions should be filtered in the first set of checks. for valid_deal in valid_deals.iter() { - msm.lock_client_and_provider_balances(&valid_deal.proposal)?; + st.lock_client_and_provider_balances(rt.store(), &valid_deal.proposal)?; // Store the proposal CID in pending deals set. - msm.pending_deals - .as_mut() - .unwrap() - .put(valid_deal.cid.to_bytes().into()) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set pending deal")?; + pending_deals.push(valid_deal.cid); + // Allocate a deal ID and store the proposal in the proposals AMT. - let deal_id = msm.generate_storage_deal_id(); - msm.deal_proposals - .as_mut() - .unwrap() - .set(deal_id, valid_deal.proposal.clone()) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal")?; + let deal_id = st.generate_storage_deal_id(); + deal_proposals.push((deal_id, valid_deal.proposal.clone())); + // Store verified allocation (if any) in the pending allocation IDs map. // It will be removed when the deal is activated or expires. if valid_deal.allocation != NO_ALLOCATION_ID { - msm.pending_deal_allocation_ids - .as_mut() - .unwrap() - .set(deal_id_key(deal_id), valid_deal.allocation) - .context_code( - ExitCode::USR_ILLEGAL_STATE, - "failed to set deal allocation", - )?; + pending_deal_allocation_ids.push((deal_id_key(deal_id), valid_deal.allocation)); } // Randomize the first epoch for when the deal will be processed so an attacker isn't able to // schedule too many deals for the same tick. - let process_epoch = - gen_rand_next_epoch(rt.policy(), valid_deal.proposal.start_epoch, deal_id); - - msm.deals_by_epoch.as_mut().unwrap().put(process_epoch, deal_id).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to set deal ops by epoch", - ) - })?; + deals_by_epoch.push(( + gen_rand_next_epoch(rt.policy(), valid_deal.proposal.start_epoch, deal_id), + deal_id, + )); new_deal_ids.push(deal_id); } - msm.commit_state()?; + st.put_pending_deals(rt.store(), &pending_deals)?; + + st.put_deal_proposals(rt.store(), &deal_proposals)?; + + st.put_pending_deal_allocation_ids(rt.store(), &pending_deal_allocation_ids)?; + + st.put_deals_by_epoch(rt.store(), &deals_by_epoch)?; + Ok(()) })?; @@ -530,9 +449,7 @@ impl Actor { let curr_epoch = rt.curr_epoch(); let st: State = rt.state()?; - let proposals = DealArray::load(&st.proposals, rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals") - })?; + let proposals = st.get_proposal_array(rt.store())?; let mut sectors_data = Vec::with_capacity(params.sectors.len()); for sector in params.sectors.iter() { @@ -570,12 +487,10 @@ impl Actor { let miner_addr = rt.message().caller(); let curr_epoch = rt.curr_epoch(); - let deal_spaces = { - let st: State = rt.state()?; - let proposals = DealArray::load(&st.proposals, rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals") - })?; + let st: State = rt.state()?; + let proposals = st.get_proposal_array(rt.store())?; + let deal_spaces = { validate_and_return_deal_space( &proposals, ¶ms.deal_ids, @@ -590,26 +505,13 @@ impl Actor { // Update deal states let mut verified_infos = Vec::new(); rt.transaction(|st: &mut State, rt| { - let mut msm = st.mutator(rt.store()); - msm.with_deal_states(Permission::Write) - .with_pending_proposals(Permission::Write) - .with_deal_proposals(Permission::ReadOnly) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; + let mut deal_states: Vec<(DealID, DealState)> = vec![]; for deal_id in params.deal_ids { // This construction could be replaced with a single "update deal state" // state method, possibly batched over all deal ids at once. - let s = msm - .deal_states - .as_ref() - .unwrap() - .get(deal_id) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to check state for deal ({})", deal_id) - })?; + let s = st.find_deal_state(rt.store(), deal_id)?; + if s.is_some() { return Err(actor_error!( illegal_argument, @@ -618,28 +520,15 @@ impl Actor { )); } - let proposal = msm - .deal_proposals - .as_ref() - .unwrap() - .get(deal_id) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to load deal proposal {}", deal_id) - })? - .ok_or_else(|| actor_error!(not_found, "no such deal proposal {}", deal_id))?; + let proposal = st + .find_proposal(rt.store(), deal_id)? + .ok_or_else(|| actor_error!(not_found, "no such deal_id: {}", deal_id))?; - let propc = rt_deal_cid(rt, proposal)?; + let propc = rt_deal_cid(rt, &proposal)?; // Confirm the deal is in the pending proposals queue. // It will be removed from this queue later, during cron. - let has = msm - .pending_deals - .as_ref() - .unwrap() - .has(&propc.to_bytes()) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to get pending proposal ({})", propc) - })?; + let has = st.has_pending_deal(rt.store(), propc)?; if !has { return Err(actor_error!( @@ -650,16 +539,11 @@ impl Actor { } // Extract and remove any verified allocation ID for the pending deal. - let allocation = msm - .pending_deal_allocation_ids - .as_mut() - .unwrap() - .delete(&deal_id_key(deal_id)) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to remove allocation id for deal {}", deal_id) - })? + let allocation = st + .remove_pending_deal_allocation_id(rt.store(), &deal_id_key(deal_id))? .unwrap_or((BytesKey(vec![]), NO_ALLOCATION_ID)) .1; + if allocation != NO_ALLOCATION_ID { verified_infos.push(VerifiedDealInfo { client: proposal.client.id().unwrap(), @@ -668,24 +552,20 @@ impl Actor { size: proposal.piece_size, }) } - msm.deal_states - .as_mut() - .unwrap() - .set( - deal_id, - DealState { - sector_start_epoch: curr_epoch, - last_updated_epoch: EPOCH_UNDEFINED, - slash_epoch: EPOCH_UNDEFINED, - verified_claim: allocation, - }, - ) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to set deal state {}", deal_id) - })?; + + deal_states.push(( + deal_id, + DealState { + sector_start_epoch: curr_epoch, + last_updated_epoch: EPOCH_UNDEFINED, + slash_epoch: EPOCH_UNDEFINED, + verified_claim: allocation, + }, + )); } - msm.commit_state()?; + st.put_deal_states(rt.store(), &deal_states)?; + Ok(()) })?; @@ -703,18 +583,11 @@ impl Actor { let miner_addr = rt.message().caller(); rt.transaction(|st: &mut State, rt| { - let mut msm = st.mutator(rt.store()); - msm.with_deal_states(Permission::Write) - .with_deal_proposals(Permission::ReadOnly) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; + let mut deal_states: Vec<(DealID, DealState)> = vec![]; for id in params.deal_ids { - let deal = msm.deal_proposals.as_ref().unwrap().get(id).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get deal proposal") - })?; + let deal = st.find_proposal(rt.store(), id)?; + // The deal may have expired and been deleted before the sector is terminated. // Nothing to do, but continue execution for the other deals. if deal.is_none() { @@ -739,14 +612,8 @@ impl Actor { continue; } - let mut state: DealState = *msm - .deal_states - .as_ref() - .unwrap() - .get(id) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get deal state") - })? + let mut state: DealState = st + .find_deal_state(rt.store(), id)? // A deal with a proposal but no state is not activated, but then it should not be // part of a sector that is terminating. .ok_or_else(|| actor_error!(illegal_argument, "no state for deal {}", id))?; @@ -761,15 +628,10 @@ impl Actor { // and slashing of provider collateral happens in cron_tick. state.slash_epoch = params.epoch; - msm.deal_states.as_mut().unwrap().set(id, state).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to set deal state ({})", id), - ) - })?; + deal_states.push((id, state)); } - msm.commit_state()?; + st.put_deal_states(rt.store(), &deal_states)?; Ok(()) })?; Ok(()) @@ -782,10 +644,8 @@ impl Actor { rt.validate_immediate_caller_type(std::iter::once(&Type::Miner))?; let st: State = rt.state()?; + let proposals = st.get_proposal_array(rt.store())?; - let proposals = DealArray::load(&st.proposals, rt.store()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals") - })?; let mut commds = Vec::with_capacity(params.inputs.len()); for comm_input in params.inputs.iter() { commds.push(compute_data_commitment( @@ -808,57 +668,19 @@ impl Actor { rt.transaction(|st: &mut State, rt| { let last_cron = st.last_cron; let mut updates_needed: BTreeMap> = BTreeMap::new(); - let mut msm = st.mutator(rt.store()); - msm.with_deal_states(Permission::Write) - .with_locked_table(Permission::Write) - .with_escrow_table(Permission::Write) - .with_deals_by_epoch(Permission::Write) - .with_deal_proposals(Permission::Write) - .with_pending_proposals(Permission::Write) - .build() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load state") - })?; + let mut rm_cron_id: Vec = vec![]; for i in (last_cron + 1)..=rt.curr_epoch() { - // TODO specs-actors modifies msm as it's iterated through, which is memory unsafe - // for now the deal ids are being collected and then iterated on, which could - // cause a potential inconsistency in exit code returned if a deal_id fails - // to be pulled from storage where it wouldn't be triggered otherwise. - // Workaround a better solution (seperating msm or fixing go impl) - let mut deal_ids = Vec::new(); - msm.deals_by_epoch - .as_ref() - .unwrap() - .for_each(i, |deal_id| { - deal_ids.push(deal_id); - Ok(()) - }) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal state")?; + let deal_ids = st.get_deals_for_epoch(rt.store(), i)?; for deal_id in deal_ids { - let deal = msm - .deal_proposals - .as_ref() - .unwrap() - .get(deal_id) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to get deal_id ({})", deal_id) - })? - .ok_or_else(|| { - actor_error!(not_found, "proposal doesn't exist ({})", deal_id) - })? - .clone(); + let deal = st.find_proposal(rt.store(), deal_id)?.ok_or_else(|| { + actor_error!(not_found, "proposal doesn't exist ({})", deal_id) + })?; let dcid = rt_deal_cid(rt, &deal)?; - let state = msm - .deal_states - .as_ref() - .unwrap() - .get(deal_id) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get deal state")? - .cloned(); + let state = st.find_deal_state(rt.store(), deal_id)?; // deal has been published but not activated yet -> terminate it // as it has timed out @@ -873,20 +695,14 @@ impl Actor { )); } - let slashed = msm.process_deal_init_timed_out(&deal)?; + let slashed = st.process_deal_init_timed_out(rt.store(), &deal)?; if !slashed.is_zero() { amount_slashed += slashed; } // Delete the proposal (but not state, which doesn't exist). - let deleted = msm - .deal_proposals - .as_mut() - .unwrap() - .delete(deal_id) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to delete deal proposal {}", deal_id) - })?; + let deleted = st.remove_proposal(rt.store(), deal_id)?; + if deleted.is_none() { return Err(actor_error!( illegal_state, @@ -896,57 +712,39 @@ impl Actor { ) )); } + // Delete pending deal CID - msm.pending_deals - .as_mut() - .unwrap() - .delete(&dcid.to_bytes()) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to delete pending proposal {}", deal_id) - })? - .ok_or_else(|| { - actor_error!( - illegal_state, - "failed to delete pending proposal: does not exist" - ) - })?; + st.remove_pending_deal(rt.store(), dcid)?.ok_or_else(|| { + actor_error!( + illegal_state, + "failed to delete pending deals: does not exist" + ) + })?; + // Delete pending deal allocation id (if present). - msm.pending_deal_allocation_ids - .as_mut() - .unwrap() - .delete(&deal_id_key(deal_id)) - .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!( - "failed to delete pending proposal allocation id for {}", - deal_id - ) - })?; + st.remove_pending_deal_allocation_id(rt.store(), &deal_id_key(deal_id))?; continue; } let mut state = state.unwrap(); if state.last_updated_epoch == EPOCH_UNDEFINED { - msm.pending_deals - .as_mut() - .unwrap() - .delete(&dcid.to_bytes()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to delete pending proposal {}", dcid), - ) - })? - .ok_or_else(|| { - actor_error!( - illegal_state, - "failed to delete pending proposal: does not exist" - ) - })?; + st.remove_pending_deal(rt.store(), dcid)?.ok_or_else(|| { + actor_error!( + illegal_state, + "failed to delete pending proposal: does not exist" + ) + })?; } - let (slash_amount, next_epoch, remove_deal) = - msm.update_pending_deal_state(rt.policy(), &state, &deal, curr_epoch)?; + let (slash_amount, next_epoch, remove_deal) = st.put_pending_deal_state( + rt.store(), + rt.policy(), + &state, + &deal, + curr_epoch, + )?; + if slash_amount.is_negative() { return Err(actor_error!( illegal_state, @@ -971,13 +769,8 @@ impl Actor { amount_slashed += slash_amount; // Delete proposal and state simultaneously. - let deleted = - msm.deal_states.as_mut().unwrap().delete(deal_id).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to delete deal state", - ) - })?; + let deleted = st.remove_deal_state(rt.store(), deal_id)?; + if deleted.is_none() { return Err(actor_error!( illegal_state, @@ -985,13 +778,8 @@ impl Actor { )); } - let deleted = - msm.deal_proposals.as_mut().unwrap().delete(deal_id).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to delete deal proposal", - ) - })?; + let deleted = st.remove_proposal(rt.store(), deal_id)?; + if deleted.is_none() { return Err(actor_error!( illegal_state, @@ -1016,12 +804,7 @@ impl Actor { } state.last_updated_epoch = curr_epoch; - msm.deal_states.as_mut().unwrap().set(deal_id, state).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to set deal state", - ) - })?; + st.put_deal_states(rt.store(), &[(deal_id, state)])?; if let Some(ev) = updates_needed.get_mut(&next_epoch) { ev.push(deal_id); @@ -1030,27 +813,16 @@ impl Actor { } } } - msm.deals_by_epoch.as_mut().unwrap().remove_all(i).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to delete deal ops for epoch {}", i), - ) - })?; + rm_cron_id.push(i); } + st.remove_deals_by_epoch(rt.store(), &rm_cron_id)?; + // updates_needed is already sorted by epoch. - for (epoch, deals) in updates_needed { - msm.deals_by_epoch.as_mut().unwrap().put_many(epoch, &deals).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to reinsert deal IDs for epoch {}", epoch), - ) - })?; - } + st.put_batch_deals_by_epoch(rt.store(), &updates_needed)?; - msm.st.last_cron = rt.curr_epoch(); + st.last_cron = rt.curr_epoch(); - msm.commit_state()?; Ok(()) })?; @@ -1112,7 +884,7 @@ impl Actor { Ok(GetDealTermReturn { start: found.start_epoch, duration: found.duration() }) } - /// Returns the per-epoch price of a deal proposal. + /// Returns the total price that will be paid from the client to the provider for this deal. fn get_deal_total_price( rt: &mut impl Runtime, params: GetDealTotalPriceParams, @@ -1209,6 +981,7 @@ fn compute_data_commitment( deal_ids: &[DealID], ) -> Result { let mut pieces = Vec::with_capacity(deal_ids.len()); + for deal_id in deal_ids { let deal = proposals .get(*deal_id) @@ -1219,6 +992,7 @@ fn compute_data_commitment( ) })? .ok_or_else(|| actor_error!(not_found, "proposal doesn't exist ({})", deal_id))?; + pieces.push(PieceInfo { cid: deal.piece_cid, size: deal.piece_size }); } rt.compute_unsealed_sector_cid(sector_type, &pieces).map_err(|e| { @@ -1237,6 +1011,7 @@ pub fn validate_and_return_deal_space( let mut seen_deal_ids = BTreeSet::new(); let mut deal_space = BigInt::zero(); let mut verified_deal_space = BigInt::zero(); + for deal_id in deal_ids { if !seen_deal_ids.insert(deal_id) { return Err(actor_error!( @@ -1245,6 +1020,7 @@ pub fn validate_and_return_deal_space( deal_id )); } + let proposal = proposals .get(*deal_id) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal")? diff --git a/actors/market/src/state.rs b/actors/market/src/state.rs index bf3fea656..55262f449 100644 --- a/actors/market/src/state.rs +++ b/actors/market/src/state.rs @@ -3,16 +3,16 @@ use crate::balance_table::BalanceTable; use crate::ext::verifreg::AllocationID; -use anyhow::anyhow; use cid::Cid; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::{ - actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorDowncast, ActorError, Array, - AsActorError, Map, Set, SetMultimap, + actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, Array, AsActorError, + Set, SetMultimap, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; +use fvm_ipld_hamt::BytesKey; use fvm_shared::address::Address; use fvm_shared::clock::{ChainEpoch, EPOCH_UNDEFINED}; use fvm_shared::deal::DealID; @@ -20,11 +20,18 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::HAMT_BIT_WIDTH; use num_traits::Zero; +use std::collections::BTreeMap; use super::policy::*; use super::types::*; use super::{DealProposal, DealState}; +pub enum Reason { + ClientCollateral, + ClientStorageFee, + ProviderCollateral, +} + /// Market actor state #[derive(Clone, Default, Serialize_tuple, Deserialize_tuple, Debug)] pub struct State { @@ -69,29 +76,43 @@ pub struct State { pub pending_deal_allocation_ids: Cid, // HAMT[DealID]AllocationID } +impl Cbor for State {} + impl State { - pub fn new(store: &BS) -> anyhow::Result { + pub fn new(store: &BS) -> Result { let empty_proposals_array = Array::<(), BS>::new_with_bit_width(store, PROPOSALS_AMT_BITWIDTH) .flush() - .map_err(|e| anyhow!("Failed to create empty proposals array: {}", e))?; + .context_code( + ExitCode::USR_ILLEGAL_STATE, + "Failed to create empty proposals array", + )?; + let empty_states_array = Array::<(), BS>::new_with_bit_width(store, STATES_AMT_BITWIDTH) .flush() - .map_err(|e| anyhow!("Failed to create empty states array: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "Failed to create empty states array")?; + + let empty_pending_proposals_map = + make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH).flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "Failed to create empty pending proposals map state", + )?; + + let empty_balance_table = BalanceTable::new(store).root().context_code( + ExitCode::USR_ILLEGAL_STATE, + "Failed to create empty balance table map", + )?; - let empty_pending_proposals_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) - .flush() - .map_err(|e| anyhow!("Failed to create empty pending proposals map state: {}", e))?; - let empty_balance_table = BalanceTable::new(store) - .root() - .map_err(|e| anyhow!("Failed to create empty balance table map: {}", e))?; let empty_deal_ops_hamt = SetMultimap::new(store) .root() - .map_err(|e| anyhow!("Failed to create empty multiset: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "Failed to create empty multiset")?; + let empty_pending_deal_allocation_map = - make_empty_map::<_, AllocationID>(store, HAMT_BIT_WIDTH).flush().map_err(|e| { - anyhow!("Failed to create empty pending deal allocation map: {}", e) - })?; + make_empty_map::<_, AllocationID>(store, HAMT_BIT_WIDTH).flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "Failed to create empty pending deal allocation map", + )?; + Ok(Self { proposals: empty_proposals_array, states: empty_states_array, @@ -109,308 +130,458 @@ impl State { }) } - pub fn total_locked(&self) -> TokenAmount { + pub fn get_total_locked(&self) -> TokenAmount { &self.total_client_locked_collateral + &self.total_provider_locked_collateral + &self.total_client_storage_fee } - pub fn get_proposal( + pub fn find_deal_state( &self, store: &BS, - id: DealID, - ) -> Result { - let found = self - .find_proposal(store, id)? - .with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?; - Ok(found) - } + deal_id: DealID, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let states = DealMetaArray::load(&self.states, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal state array")?; - pub fn find_proposal( - &self, - store: &BS, - id: DealID, - ) -> Result, ActorError> { - let proposals = DealArray::load(&self.proposals, store) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals")?; - let maybe = proposals.get(id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to load deal proposal {}", id) + let found = states.get(deal_id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("no such deal state for {}", deal_id) })?; - Ok(maybe.cloned()) + + Ok(found.cloned()) } - pub fn find_deal_state( - &self, + pub fn put_deal_states( + &mut self, store: &BS, - id: DealID, - ) -> Result, ActorError> { - let states = DealMetaArray::load(&self.states, store) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal states")?; - let found = states.get(id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { - format!("failed to load deal state {}", id) + new_deal_states: &[(DealID, DealState)], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut states = DealMetaArray::load(&self.states, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposal array")?; + + new_deal_states.iter().try_for_each(|(id, deal_state)| -> Result<(), ActorError> { + states + .set(*id, *deal_state) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal state")?; + Ok(()) })?; - Ok(found.cloned()) + + self.states = states + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal states")?; + + Ok(()) } - pub(super) fn mutator<'bs, BS: Blockstore>( + pub fn remove_deal_state( &mut self, - store: &'bs BS, - ) -> MarketStateMutation<'bs, '_, BS> { - MarketStateMutation::new(self, store) - } -} + store: &BS, + deal_id: DealID, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let mut states = DealMetaArray::load(&self.states, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposal array")?; + + let rval_deal_state = states + .delete(deal_id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to delete deal state")?; + + self.states = states + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal states")?; -fn deal_get_payment_remaining( - deal: &DealProposal, - mut slash_epoch: ChainEpoch, -) -> Result { - if slash_epoch > deal.end_epoch { - return Err(actor_error!( - illegal_state, - "deal slash epoch {} after end epoch {}", - slash_epoch, - deal.end_epoch - )); + Ok(rval_deal_state) } - // Payments are always for start -> end epoch irrespective of when the deal is slashed. - slash_epoch = std::cmp::max(slash_epoch, deal.start_epoch); + pub fn get_proposal_array<'a, BS>(&'a self, store: &'a BS) -> Result, ActorError> + where + BS: Blockstore, + { + let deal_proposals = DealArray::load(&self.proposals, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposal array")?; - let duration_remaining = deal.end_epoch - slash_epoch; - if duration_remaining < 0 { - return Err(actor_error!( - illegal_state, - "deal remaining duration negative: {}", - duration_remaining - )); + Ok(deal_proposals) } - Ok(&deal.storage_price_per_epoch * duration_remaining as u64) -} + pub fn get_proposal( + &self, + store: &BS, + id: DealID, + ) -> Result { + let found = self + .find_proposal(store, id)? + .with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?; + Ok(found) + } + pub fn find_proposal( + &self, + store: &BS, + deal_id: DealID, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let deal_proposals = self.get_proposal_array(store)?; + + let proposal = + deal_proposals.get(deal_id).with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load deal proposal {}", deal_id) + })?; -impl Cbor for State {} + Ok(proposal.cloned()) + } -#[derive(Debug, PartialEq, Eq)] -pub(super) enum Permission { - Invalid, - ReadOnly, - Write, -} + pub fn remove_proposal( + &mut self, + store: &BS, + deal_id: DealID, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let mut deal_proposals = DealArray::load(&self.proposals, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposal array")?; + + let proposal = deal_proposals + .delete(deal_id) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("no such deal proposal {}", deal_id) + })?; -pub(super) enum Reason { - ClientCollateral, - ClientStorageFee, - ProviderCollateral, -} + self.proposals = deal_proposals + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal proposals")?; -pub(super) struct MarketStateMutation<'bs, 's, BS> { - pub(super) st: &'s mut State, - pub(super) store: &'bs BS, + Ok(proposal) + } - pub(super) proposal_permit: Permission, - pub(super) deal_proposals: Option>, + pub fn put_deal_proposals( + &mut self, + store: &BS, + new_deal_proposals: &[(DealID, DealProposal)], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut deal_proposals = DealArray::load(&self.proposals, store) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposal array")?; + + new_deal_proposals.iter().try_for_each(|(id, proposal)| -> Result<(), ActorError> { + deal_proposals + .set(*id, proposal.clone()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal proposal")?; + Ok(()) + })?; - pub(super) state_permit: Permission, - pub(super) deal_states: Option>, + self.proposals = deal_proposals + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal proposals")?; - pub(super) escrow_permit: Permission, - pub(super) escrow_table: Option>, + Ok(()) + } - pub(super) pending_permit: Permission, - pub(super) pending_deals: Option>, - pub(super) pending_deal_allocation_ids: Option>, + pub fn put_pending_deal_allocation_ids( + &mut self, + store: &BS, + new_pending_deal_allocation_ids: &[(BytesKey, AllocationID)], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut pending_deal_allocation_ids = make_map_with_root_and_bitwidth( + &self.pending_deal_allocation_ids, + store, + HAMT_BIT_WIDTH, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending deal allocation id's")?; - pub(super) dpe_permit: Permission, - pub(super) deals_by_epoch: Option>, + new_pending_deal_allocation_ids.iter().try_for_each( + |(deal_id, allocation_id)| -> Result<(), ActorError> { + pending_deal_allocation_ids.set(deal_id.clone(), *allocation_id).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to set pending deal allocation id", + )?; + Ok(()) + }, + )?; - pub(super) locked_permit: Permission, - pub(super) locked_table: Option>, - pub(super) total_client_locked_collateral: Option, - pub(super) total_provider_locked_collateral: Option, - pub(super) total_client_storage_fee: Option, + self.pending_deal_allocation_ids = pending_deal_allocation_ids.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush pending deal allocation id", + )?; - pub(super) next_deal_id: DealID, -} + Ok(()) + } -impl<'bs, 's, BS> MarketStateMutation<'bs, 's, BS> -where - BS: Blockstore, -{ - pub(super) fn new(st: &'s mut State, store: &'bs BS) -> Self { - Self { - next_deal_id: st.next_id, - st, + pub fn remove_pending_deal_allocation_id( + &mut self, + store: &BS, + deal_id_key: &BytesKey, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let mut pending_deal_allocation_ids = make_map_with_root_and_bitwidth( + &self.pending_deal_allocation_ids, store, - proposal_permit: Permission::Invalid, - deal_proposals: None, - state_permit: Permission::Invalid, - deal_states: None, - escrow_permit: Permission::Invalid, - escrow_table: None, - pending_permit: Permission::Invalid, - pending_deals: None, - pending_deal_allocation_ids: None, - dpe_permit: Permission::Invalid, - deals_by_epoch: None, - locked_permit: Permission::Invalid, - locked_table: None, - total_client_locked_collateral: None, - total_provider_locked_collateral: None, - total_client_storage_fee: None, - } - } + HAMT_BIT_WIDTH, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending deal allocation id's")?; + + let rval_allocation_id = pending_deal_allocation_ids + .delete(deal_id_key) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("no such deal proposal {:#?}", deal_id_key) + })?; - pub(super) fn build(&mut self) -> anyhow::Result<&mut Self> { - if self.proposal_permit != Permission::Invalid { - self.deal_proposals = Some(DealArray::load(&self.st.proposals, self.store)?); - } + self.pending_deal_allocation_ids = pending_deal_allocation_ids + .flush() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal proposals")?; - if self.state_permit != Permission::Invalid { - self.deal_states = Some(DealMetaArray::load(&self.st.states, self.store)?); - } + Ok(rval_allocation_id) + } - if self.locked_permit != Permission::Invalid { - self.locked_table = Some(BalanceTable::from_root(self.store, &self.st.locked_table)?); - self.total_client_locked_collateral = - Some(self.st.total_client_locked_collateral.clone()); - self.total_client_storage_fee = Some(self.st.total_client_storage_fee.clone()); - self.total_provider_locked_collateral = - Some(self.st.total_provider_locked_collateral.clone()); - } + pub fn put_deals_by_epoch( + &mut self, + store: &BS, + new_deals_by_epoch: &[(ChainEpoch, DealID)], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut deals_by_epoch = SetMultimap::from_root(store, &self.deal_ops_by_epoch) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deals by epoch")?; + + new_deals_by_epoch.iter().try_for_each(|(epoch, id)| -> Result<(), ActorError> { + deals_by_epoch + .put(*epoch, *id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal")?; + Ok(()) + })?; - if self.escrow_permit != Permission::Invalid { - self.escrow_table = Some(BalanceTable::from_root(self.store, &self.st.escrow_table)?); - } + self.deal_ops_by_epoch = deals_by_epoch + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deals by epoch")?; - if self.pending_permit != Permission::Invalid { - self.pending_deals = Some(Set::from_root(self.store, &self.st.pending_proposals)?); - self.pending_deal_allocation_ids = Some(make_map_with_root_and_bitwidth( - &self.st.pending_deal_allocation_ids, - self.store, - HAMT_BIT_WIDTH, - )?); - } + Ok(()) + } - if self.dpe_permit != Permission::Invalid { - self.deals_by_epoch = - Some(SetMultimap::from_root(self.store, &self.st.deal_ops_by_epoch)?); - } + pub fn put_batch_deals_by_epoch( + &mut self, + store: &BS, + new_deals_by_epoch: &BTreeMap>, + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut deals_by_epoch = SetMultimap::from_root(store, &self.deal_ops_by_epoch) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deals by epoch")?; + + new_deals_by_epoch.iter().try_for_each(|(epoch, deals)| -> Result<(), ActorError> { + deals_by_epoch + .put_many(*epoch, deals) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to reinsert deal IDs for epoch {}", epoch) + })?; + Ok(()) + })?; - self.next_deal_id = self.st.next_id; + self.deal_ops_by_epoch = deals_by_epoch + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deals by epoch")?; - Ok(self) + Ok(()) } - pub(super) fn with_deal_proposals(&mut self, permit: Permission) -> &mut Self { - self.proposal_permit = permit; - self + pub fn get_deals_for_epoch( + &self, + store: &BS, + key: ChainEpoch, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let mut deal_ids = Vec::new(); + + let deals_by_epoch = SetMultimap::from_root(store, &self.deal_ops_by_epoch) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deals by epoch")?; + + deals_by_epoch + .for_each(key, |deal_id| { + deal_ids.push(deal_id); + Ok(()) + }) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal state")?; + + Ok(deal_ids) } - pub(super) fn with_deal_states(&mut self, permit: Permission) -> &mut Self { - self.state_permit = permit; - self - } + pub fn remove_deals_by_epoch( + &mut self, + store: &BS, + epochs_to_remove: &[ChainEpoch], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut deals_by_epoch = SetMultimap::from_root(store, &self.deal_ops_by_epoch) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deals by epoch")?; + + epochs_to_remove.iter().try_for_each(|epoch| -> Result<(), ActorError> { + deals_by_epoch + .remove_all(*epoch) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to delete deal ops for epoch {}", epoch) + })?; + Ok(()) + })?; - pub(super) fn with_escrow_table(&mut self, permit: Permission) -> &mut Self { - self.escrow_permit = permit; - self - } + self.deal_ops_by_epoch = deals_by_epoch + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deals by epoch")?; - pub(super) fn with_locked_table(&mut self, permit: Permission) -> &mut Self { - self.locked_permit = permit; - self + Ok(()) } - pub(super) fn with_pending_proposals(&mut self, permit: Permission) -> &mut Self { - self.pending_permit = permit; - self - } + pub fn add_balance_to_escrow_table( + &mut self, + store: &BS, + addr: &Address, + amount: &TokenAmount, + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + + escrow_table + .add(addr, amount) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to add escrow table")?; + + self.escrow_table = escrow_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; - pub(super) fn with_deals_by_epoch(&mut self, permit: Permission) -> &mut Self { - self.dpe_permit = permit; - self + Ok(()) } - pub(super) fn commit_state(&mut self) -> Result<(), ActorError> { - if self.proposal_permit == Permission::Write { - if let Some(s) = &mut self.deal_proposals { - self.st.proposals = s - .flush() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal proposals")?; - } - } + pub fn withdraw_balance_from_escrow_table( + &mut self, + store: &BS, + addr: &Address, + amount: &TokenAmount, + ) -> Result + where + BS: Blockstore, + { + let mut escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; - if self.state_permit == Permission::Write { - if let Some(s) = &mut self.deal_states { - self.st.states = s - .flush() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush deal states")?; - } - } + let locked_table = BalanceTable::from_root(store, &self.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; - if self.locked_permit == Permission::Write { - if let Some(s) = &mut self.locked_table { - self.st.locked_table = s - .root() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush locked table")?; - } - if let Some(s) = &mut self.total_client_locked_collateral { - self.st.total_client_locked_collateral = s.clone(); - } - if let Some(s) = &mut self.total_provider_locked_collateral { - self.st.total_provider_locked_collateral = s.clone(); - } - if let Some(s) = &mut self.total_client_storage_fee { - self.st.total_client_storage_fee = s.clone(); - } - } + let min_balance = locked_table + .get(addr) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance")?; - if self.escrow_permit == Permission::Write { - if let Some(s) = &mut self.escrow_table { - self.st.escrow_table = s - .root() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; - } - } + let ex = escrow_table + .subtract_with_minimum(addr, amount, &min_balance) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to subtract from escrow table")?; - if self.pending_permit == Permission::Write { - if let Some(s) = &mut self.pending_deals { - self.st.pending_proposals = s - .root() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; - } - if let Some(s) = &mut self.pending_deal_allocation_ids { - self.st.pending_deal_allocation_ids = s.flush().context_code( - ExitCode::USR_ILLEGAL_STATE, - "failed to flush pending deal allocation ids", - )?; - } - } + self.escrow_table = escrow_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; - if self.dpe_permit == Permission::Write { - if let Some(s) = &mut self.deals_by_epoch { - self.st.deal_ops_by_epoch = s - .root() - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; - } - } + Ok(ex) + } + + pub fn has_pending_deal(&self, store: &BS, key: Cid) -> Result + where + BS: Blockstore, + { + let pending_deals = Set::from_root(store, &self.pending_proposals) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get pending deals")?; - self.st.next_id = self.next_deal_id; + let rval = pending_deals + .has(&key.to_bytes()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get pending deals")?; + + Ok(rval) + } + + pub fn put_pending_deals( + &mut self, + store: &BS, + new_pending_deals: &[Cid], + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + let mut pending_deals = Set::from_root(store, &self.pending_proposals) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending deals")?; + + new_pending_deals.iter().try_for_each(|key: &Cid| -> Result<(), ActorError> { + pending_deals + .put(key.to_bytes().into()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set deal")?; + Ok(()) + })?; + + self.pending_proposals = pending_deals + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush pending deals")?; Ok(()) } + pub fn remove_pending_deal( + &mut self, + store: &BS, + pending_deal_key: Cid, + ) -> Result, ActorError> + where + BS: Blockstore, + { + let mut pending_deals = Set::from_root(store, &self.pending_proposals) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load pending deals")?; + + let rval_pending_deal = pending_deals + .delete(&pending_deal_key.to_bytes()) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to delete pending proposal {}", pending_deal_key) + })?; + + self.pending_proposals = pending_deals + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush pending deals")?; + + Ok(rval_pending_deal) + } + //////////////////////////////////////////////////////////////////////////////// // Deal state operations //////////////////////////////////////////////////////////////////////////////// #[allow(clippy::too_many_arguments)] - pub(super) fn update_pending_deal_state( + pub fn put_pending_deal_state( &mut self, + store: &BS, policy: &Policy, state: &DealState, deal: &DealProposal, epoch: ChainEpoch, - ) -> Result<(TokenAmount, ChainEpoch, bool), ActorError> { + ) -> Result<(TokenAmount, ChainEpoch, bool), ActorError> + where + BS: Blockstore, + { let ever_updated = state.last_updated_epoch != EPOCH_UNDEFINED; let ever_slashed = state.slash_epoch != EPOCH_UNDEFINED; @@ -460,7 +631,7 @@ where let total_payment = &deal.storage_price_per_epoch * num_epochs_elapsed; if total_payment.is_positive() { - self.transfer_balance(&deal.client, &deal.provider, &total_payment)?; + self.transfer_balance(store, &deal.client, &deal.provider, &total_payment)?; } if ever_slashed { @@ -468,33 +639,31 @@ where let payment_remaining = deal_get_payment_remaining(deal, state.slash_epoch)?; // Unlock remaining storage fee - self.unlock_balance(&deal.client, &payment_remaining, Reason::ClientStorageFee) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to unlock remaining client storage fee", - ) - })?; + self.unlock_balance(store, &deal.client, &payment_remaining, Reason::ClientStorageFee) + .context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to unlock remaining client storage fee", + )?; // Unlock client collateral - self.unlock_balance(&deal.client, &deal.client_collateral, Reason::ClientCollateral) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to unlock client collateral", - ) - })?; + self.unlock_balance( + store, + &deal.client, + &deal.client_collateral, + Reason::ClientCollateral, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to unlock client collateral")?; // slash provider collateral let slashed = deal.provider_collateral.clone(); - self.slash_balance(&deal.provider, &slashed, Reason::ProviderCollateral) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "slashing balance"))?; + self.slash_balance(store, &deal.provider, &slashed, Reason::ProviderCollateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "slashing balance")?; return Ok((slashed, EPOCH_UNDEFINED, true)); } if epoch >= deal.end_epoch { - self.process_deal_expired(deal, state)?; + self.process_deal_expired(store, deal, state)?; return Ok((TokenAmount::zero(), EPOCH_UNDEFINED, true)); } @@ -509,111 +678,125 @@ where /// Deal start deadline elapsed without appearing in a proven sector. /// Slash a portion of provider's collateral, and unlock remaining collaterals /// for both provider and client. - pub(super) fn process_deal_init_timed_out( + pub fn process_deal_init_timed_out( &mut self, + store: &BS, deal: &DealProposal, - ) -> Result { - self.unlock_balance(&deal.client, &deal.total_storage_fee(), Reason::ClientStorageFee) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failure unlocking client storage fee", - ) - })?; + ) -> Result + where + BS: Blockstore, + { + self.unlock_balance( + store, + &deal.client, + &deal.total_storage_fee(), + Reason::ClientStorageFee, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failure unlocking client storage fee")?; - self.unlock_balance(&deal.client, &deal.client_collateral, Reason::ClientCollateral) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failure unlocking client collateral", - ) - })?; + self.unlock_balance(store, &deal.client, &deal.client_collateral, Reason::ClientCollateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failure unlocking client collateral")?; let amount_slashed = collateral_penalty_for_deal_activation_missed(deal.provider_collateral.clone()); let amount_remaining = deal.provider_balance_requirement() - &amount_slashed; - self.slash_balance(&deal.provider, &amount_slashed, Reason::ProviderCollateral).map_err( - |e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to slash balance"), - )?; + self.slash_balance(store, &deal.provider, &amount_slashed, Reason::ProviderCollateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to slash balance")?; - self.unlock_balance(&deal.provider, &amount_remaining, Reason::ProviderCollateral) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to unlock deal provider balance", - ) - })?; + self.unlock_balance(store, &deal.provider, &amount_remaining, Reason::ProviderCollateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to unlock deal provider balance")?; Ok(amount_slashed) } /// Normal expiration. Unlock collaterals for both miner and client. - fn process_deal_expired( + fn process_deal_expired( &mut self, + store: &BS, deal: &DealProposal, state: &DealState, - ) -> Result<(), ActorError> { + ) -> Result<(), ActorError> + where + BS: Blockstore, + { if state.sector_start_epoch == EPOCH_UNDEFINED { return Err(actor_error!(illegal_state, "start sector epoch undefined")); } - self.unlock_balance(&deal.provider, &deal.provider_collateral, Reason::ProviderCollateral) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed unlocking deal provider balance", - ) - })?; + self.unlock_balance( + store, + &deal.provider, + &deal.provider_collateral, + Reason::ProviderCollateral, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed unlocking deal provider balance")?; - self.unlock_balance(&deal.client, &deal.client_collateral, Reason::ClientCollateral) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed unlocking deal client balance", - ) - })?; + self.unlock_balance(store, &deal.client, &deal.client_collateral, Reason::ClientCollateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed unlocking deal client balance")?; Ok(()) } - pub(super) fn generate_storage_deal_id(&mut self) -> DealID { - let ret = self.next_deal_id; - self.next_deal_id += 1; + pub fn generate_storage_deal_id(&mut self) -> DealID { + let ret = self.next_id; + self.next_id += 1; ret } // Return true when the funds in escrow for the input address can cover an additional lockup of amountToLock - pub(super) fn balance_covered( + pub fn balance_covered( &self, + store: &BS, addr: Address, amount_to_lock: &TokenAmount, - ) -> anyhow::Result { - let prev_locked = self.locked_table.as_ref().unwrap().get(&addr).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance") - })?; - let escrow_balance = self.escrow_table.as_ref().unwrap().get(&addr).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance") - })?; + ) -> Result + where + BS: Blockstore, + { + let escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + + let locked_table = BalanceTable::from_root(store, &self.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; + + let escrow_balance = escrow_table + .get(&addr) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance")?; + + let prev_locked = locked_table + .get(&addr) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance")?; + Ok((prev_locked + amount_to_lock) <= escrow_balance) } - pub(super) fn maybe_lock_balance( + fn maybe_lock_balance( &mut self, + store: &BS, addr: &Address, amount: &TokenAmount, - ) -> Result<(), ActorError> { + ) -> Result<(), ActorError> + where + BS: Blockstore, + { if amount.is_negative() { return Err(actor_error!(illegal_state, "cannot lock negative amount {}", amount)); } - let prev_locked = self.locked_table.as_ref().unwrap().get(addr).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance") - })?; + let escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; - let escrow_balance = self.escrow_table.as_ref().unwrap().get(addr).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance") - })?; + let mut locked_table = BalanceTable::from_root(store, &self.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; + + let prev_locked = locked_table + .get(addr) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance")?; + + let escrow_balance = escrow_table + .get(addr) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance")?; if &prev_locked + amount > escrow_balance { return Err(actor_error!(insufficient_funds; @@ -622,103 +805,172 @@ where addr, escrow_balance, prev_locked, amount)); } - self.locked_table.as_mut().unwrap().add(addr, amount).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to add locked balance") - })?; + locked_table + .add(addr, amount) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to add locked balance")?; + + self.locked_table = locked_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush locked table")?; + Ok(()) } - pub(super) fn lock_client_and_provider_balances( + pub fn lock_client_and_provider_balances( &mut self, + store: &BS, proposal: &DealProposal, - ) -> Result<(), ActorError> { - self.maybe_lock_balance(&proposal.client, &proposal.client_balance_requirement()) - .map_err(|e| e.wrap("failed to lock client funds"))?; + ) -> Result<(), ActorError> + where + BS: Blockstore, + { + self.maybe_lock_balance(store, &proposal.client, &proposal.client_balance_requirement()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lock client funds")?; - self.maybe_lock_balance(&proposal.provider, &proposal.provider_collateral) - .map_err(|e| e.wrap("failed to lock provider funds"))?; + self.maybe_lock_balance(store, &proposal.provider, &proposal.provider_collateral) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lock provider funds")?; + + self.total_client_locked_collateral += &proposal.client_collateral; + + self.total_client_storage_fee += proposal.total_storage_fee(); + + self.total_provider_locked_collateral += &proposal.provider_collateral; - if let Some(v) = self.total_client_locked_collateral.as_mut() { - *v += &proposal.client_collateral; - } - if let Some(v) = self.total_client_storage_fee.as_mut() { - *v += proposal.total_storage_fee(); - } - if let Some(v) = self.total_provider_locked_collateral.as_mut() { - *v += &proposal.provider_collateral; - } Ok(()) } - fn unlock_balance( + fn unlock_balance( &mut self, + store: &BS, addr: &Address, amount: &TokenAmount, lock_reason: Reason, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> + where + BS: Blockstore, + { if amount.is_negative() { - return Err(actor_error!(illegal_state, "unlock negative amount: {}", amount).into()); + return Err(actor_error!(illegal_state, "unlock negative amount: {}", amount)); } - self.locked_table.as_mut().unwrap().must_subtract(addr, amount)?; + + let mut locked_table = BalanceTable::from_root(store, &self.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; + + locked_table + .must_subtract(addr, amount) + .context_code(ExitCode::USR_ILLEGAL_STATE, "subtract from locked table failed")?; match lock_reason { - Reason::ClientCollateral => self.total_client_locked_collateral.as_mut().map(|v| { - *v -= amount; - }), - Reason::ClientStorageFee => self.total_client_storage_fee.as_mut().map(|v| { - *v -= amount; - }), - Reason::ProviderCollateral => self.total_provider_locked_collateral.as_mut().map(|v| { - *v -= amount; - }), + Reason::ClientCollateral => { + self.total_client_locked_collateral -= amount; + } + Reason::ClientStorageFee => { + self.total_client_storage_fee -= amount; + } + Reason::ProviderCollateral => { + self.total_provider_locked_collateral -= amount; + } }; + self.locked_table = locked_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush locked table")?; + Ok(()) } /// move funds from locked in client to available in provider - fn transfer_balance( + fn transfer_balance( &mut self, + store: &BS, from_addr: &Address, to_addr: &Address, amount: &TokenAmount, - ) -> Result<(), ActorError> { + ) -> Result<(), ActorError> + where + BS: Blockstore, + { if amount.is_negative() { return Err(actor_error!(illegal_state, "transfer negative amount: {}", amount)); } + let mut escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + // Subtract from locked and escrow tables - self.escrow_table - .as_mut() - .unwrap() + escrow_table .must_subtract(from_addr, amount) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "subtract from escrow"))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "subtract from escrow")?; - self.unlock_balance(from_addr, amount, Reason::ClientStorageFee) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "subtract from locked"))?; + self.unlock_balance(store, from_addr, amount, Reason::ClientStorageFee) + .context_code(ExitCode::USR_ILLEGAL_STATE, "subtract from locked")?; // Add subtracted amount to the recipient - self.escrow_table - .as_mut() - .unwrap() + escrow_table .add(to_addr, amount) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "add to escrow"))?; + .context_code(ExitCode::USR_ILLEGAL_STATE, "add to escrow")?; + + self.escrow_table = escrow_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; Ok(()) } - fn slash_balance( + fn slash_balance( &mut self, + store: &BS, addr: &Address, amount: &TokenAmount, lock_reason: Reason, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> + where + BS: Blockstore, + { if amount.is_negative() { - return Err(actor_error!(illegal_state, "negative amount to slash: {}", amount).into()); + return Err(actor_error!(illegal_state, "negative amount to slash: {}", amount)); } + let mut escrow_table = BalanceTable::from_root(store, &self.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + // Subtract from locked and escrow tables - self.escrow_table.as_mut().unwrap().must_subtract(addr, amount)?; - self.unlock_balance(addr, amount, lock_reason) + escrow_table + .must_subtract(addr, amount) + .context_code(ExitCode::USR_ILLEGAL_STATE, "subtract from escrow failed")?; + + self.escrow_table = escrow_table + .root() + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush escrow table")?; + + self.unlock_balance(store, addr, amount, lock_reason) + } +} + +fn deal_get_payment_remaining( + deal: &DealProposal, + mut slash_epoch: ChainEpoch, +) -> Result { + if slash_epoch > deal.end_epoch { + return Err(actor_error!( + illegal_state, + "deal slash epoch {} after end epoch {}", + slash_epoch, + deal.end_epoch + )); } + + // Payments are always for start -> end epoch irrespective of when the deal is slashed. + slash_epoch = std::cmp::max(slash_epoch, deal.start_epoch); + + let duration_remaining = deal.end_epoch - slash_epoch; + if duration_remaining < 0 { + return Err(actor_error!( + illegal_state, + "deal remaining duration negative: {}", + duration_remaining + )); + } + + Ok(&deal.storage_price_per_epoch * duration_remaining as u64) } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 48cef69ce..eb3a3fe45 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -239,7 +239,7 @@ impl Actor { }) } - /// Returns the owner address + /// Returns the owner address. fn get_owner(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -270,7 +270,7 @@ impl Actor { Ok(IsControllingAddressReturn { is_controlling }) } - /// Returns the miner's sector size + /// Returns the miner's sector size. fn get_sector_size(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -422,6 +422,7 @@ impl Actor { }) } + /// Returns the Peer ID for this miner. fn get_peer_id(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -450,6 +451,7 @@ impl Actor { Ok(()) } + /// Returns the multiaddresses set for this miner. fn get_multiaddresses(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; diff --git a/actors/verifreg/src/ext.rs b/actors/verifreg/src/ext.rs index d70d74c46..1d9aa1425 100644 --- a/actors/verifreg/src/ext.rs +++ b/actors/verifreg/src/ext.rs @@ -8,21 +8,11 @@ pub mod datacap { #[repr(u64)] pub enum Method { - // Non-standard. - Mint = 2, - Destroy = 3, - // Static method numbers for token standard methods, for private use. - // Name = 10, - // Symbol = 11, - // TotalSupply = 12, - BalanceOf = 13, - Transfer = 14, - // TransferFrom = 15, - // IncreaseAllowance = 16, - // DecreaseAllowance = 17, - // RevokeAllowance = 18, - Burn = 19, - // BurnFrom = 20, + Mint = frc42_dispatch::method_hash!("Mint"), + Destroy = frc42_dispatch::method_hash!("Destroy"), + Balance = frc42_dispatch::method_hash!("Balance"), + Transfer = frc42_dispatch::method_hash!("Transfer"), + Burn = frc42_dispatch::method_hash!("Burn"), } #[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index ded09f416..fa45d4b21 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -114,7 +114,7 @@ impl Actor { } // Disallow existing clients as verifiers. - let token_balance = balance_of(rt, &verifier)?; + let token_balance = balance(rt, &verifier)?; if token_balance.is_positive() { return Err(actor_error!( illegal_argument, @@ -288,7 +288,7 @@ impl Actor { })?; // Burn the client's data cap tokens. - let balance = balance_of(rt, &client).context("failed to fetch balance")?; + let balance = balance(rt, &client).context("failed to fetch balance")?; let burnt = std::cmp::min(balance, params.data_cap_amount_to_remove); destroy(rt, &client, &burnt) .context(format!("failed to destroy {} from allowance for {}", &burnt, &client))?; @@ -719,13 +719,13 @@ fn is_verifier(rt: &impl Runtime, st: &State, address: Address) -> Result Result { +// Invokes Balance on the data cap token actor, and converts the result to whole units of data cap. +fn balance(rt: &mut impl Runtime, owner: &Address) -> Result { let params = serialize(owner, "owner address")?; let ret = rt .send( &DATACAP_TOKEN_ACTOR_ADDR, - ext::datacap::Method::BalanceOf as u64, + ext::datacap::Method::Balance as u64, params, TokenAmount::zero(), ) diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 2fe45ff82..0ed78c9e4 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -102,7 +102,7 @@ impl Harness { // Expect checking the verifier's token balance. rt.expect_send( DATACAP_TOKEN_ACTOR_ADDR, - ext::datacap::Method::BalanceOf as MethodNum, + ext::datacap::Method::Balance as MethodNum, RawBytes::serialize(&verifier_resolved).unwrap(), TokenAmount::zero(), serialize(&BigIntSer(&(cap * TOKEN_PRECISION)), "").unwrap(), diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 7ba03842d..5eaa7ce79 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -854,7 +854,7 @@ pub fn verifreg_add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) params: Some(serialize(&add_verifier_params, "verifreg add verifier params").unwrap()), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::BalanceOf as u64, + method: DataCapMethod::BalanceExported as u64, params: Some(serialize(&verifier, "balance of params").unwrap()), code: Some(ExitCode::OK), ..Default::default() @@ -882,7 +882,7 @@ pub fn verifreg_add_client(v: &VM, verifier: Address, client: Address, allowance method: VerifregMethod::AddVerifiedClient as u64, subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Mint as u64, + method: DataCapMethod::MintExported as u64, params: Some( serialize( &MintParams { @@ -949,7 +949,7 @@ pub fn verifreg_remove_expired_allocations( method: VerifregMethod::RemoveExpiredAllocations as u64, subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Transfer as u64, + method: DataCapMethod::TransferExported as u64, code: Some(ExitCode::OK), params: Some( serialize( @@ -975,7 +975,7 @@ pub fn datacap_get_balance(v: &VM, address: Address) -> TokenAmount { address, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::BalanceOf as u64, + DataCapMethod::BalanceExported as u64, address, ); deserialize(&ret, "balance of return value").unwrap() @@ -1006,13 +1006,13 @@ pub fn datacap_extend_claim( client, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Transfer as u64, + DataCapMethod::TransferExported as u64, transfer_params, ); ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Transfer as u64, + method: DataCapMethod::TransferExported as u64, subinvocs: Some(vec![ExpectInvocation { to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::UniversalReceiverHook as u64, @@ -1040,7 +1040,7 @@ pub fn datacap_extend_claim( ), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Burn as u64, + method: DataCapMethod::BurnExported as u64, code: Some(ExitCode::OK), params: Some( serialize(&BurnParams { amount: token_amount }, "burn params").unwrap(), @@ -1153,7 +1153,7 @@ pub fn market_publish_deal( }; expect_publish_invocs.push(ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::TransferFrom as u64, + method: DataCapMethod::TransferFromExported as u64, params: Some( RawBytes::serialize(&TransferFromParams { from: deal_client, diff --git a/test_vm/tests/datacap_tests.rs b/test_vm/tests/datacap_tests.rs index e0361a264..83eea93d3 100644 --- a/test_vm/tests/datacap_tests.rs +++ b/test_vm/tests/datacap_tests.rs @@ -48,7 +48,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Mint as u64, + DataCapMethod::MintExported as u64, mint_params.clone(), ExitCode::USR_FORBIDDEN, ); @@ -59,7 +59,7 @@ fn datacap_transfer_scenario() { VERIFIED_REGISTRY_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Mint as u64, + DataCapMethod::MintExported as u64, mint_params, ); @@ -70,7 +70,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Allowance as u64, + DataCapMethod::AllowanceExported as u64, GetAllowanceParams { owner: client, operator }, ); @@ -116,7 +116,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_piece_too_small, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -130,7 +130,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_mismatched_datacap, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -149,7 +149,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_bad_term, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -162,7 +162,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(¶ms_bad_receiver), ExitCode::USR_FORBIDDEN, // ExitCode(19) because non-operator has insufficient allowance ); @@ -173,7 +173,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(&transfer_from_params), ExitCode::USR_INSUFFICIENT_FUNDS, // ExitCode(19) because non-operator has insufficient allowance ); @@ -183,7 +183,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(&transfer_from_params), ); @@ -193,7 +193,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, transfer_from_params, ExitCode::USR_INSUFFICIENT_FUNDS, ); @@ -212,7 +212,7 @@ fn call_name_symbol() { sender, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Name as u64, + DataCapMethod::NameExported as u64, RawBytes::default(), ) .deserialize() @@ -224,7 +224,7 @@ fn call_name_symbol() { sender, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Symbol as u64, + DataCapMethod::SymbolExported as u64, RawBytes::default(), ) .deserialize() diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 24194ed43..98012fd35 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -68,7 +68,7 @@ fn remove_datacap_simple_successful_path() { params: Some(serialize(&add_verified_client_params, "add verifier params").unwrap()), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Mint as u64, + method: DataCapMethod::MintExported as u64, params: Some(serialize(&mint_params, "mint params").unwrap()), subinvocs: None, ..Default::default() @@ -334,7 +334,7 @@ fn expect_remove_datacap(params: &RemoveDataCapParams) -> ExpectInvocation { subinvocs: Some(vec![ ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::BalanceOf as u64, + method: DataCapMethod::BalanceExported as u64, params: Some( serialize(¶ms.verified_client_to_remove, "balance_of params").unwrap(), ), @@ -344,7 +344,7 @@ fn expect_remove_datacap(params: &RemoveDataCapParams) -> ExpectInvocation { }, ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Destroy as u64, + method: DataCapMethod::DestroyExported as u64, params: Some( serialize( &DestroyParams { From 0fc355633f90330003b1c81c766dffe6ccbb4e54 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Wed, 7 Dec 2022 20:31:46 +0000 Subject: [PATCH 171/339] EVM-859: Optimize storage footprint (#919) * EVM-899: Skip write when no change (#709) * EVM-899: Don't even call the HAMT store if there was no change. * EVM-899: Measurement updates with flush fix. * EVM-890: No hashing by HAMT (#730) * EVM-890: Use the identity hasher in the EVM * EVM-890: Update the measurements. * EVM-890: Fix newline in bash script. * EVM-890: Fix charts missing series. * EVM-890: Add key wrapper and use big endian order in hash. * EVM-890: Update measurements with big endian key order. * EVM-890: Remove leftover let binding. * EVM-890: Derive serde * EVM-890: Fix jq check. * EVM-891: HAMT link extension measurements (#739) * EVM-891: Use extensions. * EVM-891: Update measurements * EVM-891: Point at the PR branch. * EVM-891: Update measurements after single char fields * EVM-890: Fix jq check. * EVM-892: Keep root small (#740) * EVM-892: Keep the first 2 levels free of data. * EVM-892: Update patch branch * EVM-892: Reduce bit width to 5 (#741) * EVM-1085: Use Kamt (#855) * EVM-1085: Use Kamt * EVM-1085: Update measurements * EVM-1085: Use the kamt branch on CI. * EVM-1085: Remove use_extensions option. --- Cargo.lock | 196 ++++++--- Cargo.toml | 38 +- actors/evm/Cargo.toml | 2 +- actors/evm/Makefile | 24 +- actors/evm/src/interpreter/system.rs | 48 ++- actors/evm/src/interpreter/uints.rs | 14 - actors/evm/src/state.rs | 2 +- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 15633 -> 15619 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 22640 -> 25461 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 14691 -> 13369 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 18437 -> 14255 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 17092 -> 15071 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 14527 -> 14022 bytes .../measurements/mapping_add_n1.jsonline | 398 ++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 16081 -> 19902 bytes .../measurements/mapping_add_n100.jsonline | 398 ++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 23485 -> 20965 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 23561 -> 22313 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 18077 -> 17467 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 19729 -> 18833 bytes .../tests/measurements/storage-footprint.sh | 2 +- 30 files changed, 1828 insertions(+), 1734 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29bef758e..bf2a29b38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,7 +592,21 @@ checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", - "multihash", + "multihash 0.16.3", + "serde", + "serde_bytes", + "unsigned-varint", +] + +[[package]] +name = "cid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b68e3193982cd54187d71afdb2a271ad4cf8af157858e9cb911b91321de143" +dependencies = [ + "core2", + "multibase", + "multihash 0.17.0", "serde", "serde_bytes", "unsigned-varint", @@ -872,9 +886,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "data-encoding-macro" @@ -898,9 +912,9 @@ dependencies = [ [[package]] name = "der" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", "zeroize", @@ -1419,7 +1433,7 @@ checksum = "6d0550a13a20decf920aeeb630a648b13174af5acf6b513895996ff1cd09393d" dependencies = [ "anyhow", "async-std", - "cid", + "cid 0.8.6", "clap", "futures", "fvm_ipld_blockstore", @@ -1448,7 +1462,7 @@ dependencies = [ name = "fil_actor_datacap" version = "10.0.0-alpha.1" dependencies = [ - "cid", + "cid 0.8.6", "fil_actors_runtime", "frc42_dispatch", "frc46_token", @@ -1469,7 +1483,7 @@ name = "fil_actor_eam" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actor_evm", "fil_actors_runtime", "fvm_ipld_blockstore", @@ -1477,7 +1491,7 @@ dependencies = [ "fvm_shared", "hex-literal", "log", - "multihash", + "multihash 0.16.3", "num-derive", "num-traits", "rlp", @@ -1500,7 +1514,7 @@ dependencies = [ "anyhow", "arrayvec", "bytes", - "cid", + "cid 0.8.6", "derive_more", "ethers", "etk-asm", @@ -1508,14 +1522,14 @@ dependencies = [ "fixed-hash", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "fvm_ipld_hamt", + "fvm_ipld_kamt", "fvm_shared", "hex", "hex-literal", "impl-serde", "lazy_static", "log", - "multihash", + "multihash 0.16.3", "near-blake2", "num-derive", "num-traits", @@ -1534,7 +1548,7 @@ name = "fil_actor_init" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actors_runtime", "frc42_dispatch", "fvm_ipld_blockstore", @@ -1552,7 +1566,7 @@ name = "fil_actor_market" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actor_power", "fil_actor_reward", "fil_actor_verifreg", @@ -1569,7 +1583,7 @@ dependencies = [ "itertools", "libipld-core 0.13.1", "log", - "multihash", + "multihash 0.16.3", "num-derive", "num-traits", "regex", @@ -1582,7 +1596,7 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "byteorder", - "cid", + "cid 0.8.6", "fil_actor_account", "fil_actor_market", "fil_actor_power", @@ -1598,7 +1612,7 @@ dependencies = [ "itertools", "lazy_static", "log", - "multihash", + "multihash 0.16.3", "num-derive", "num-traits", "rand", @@ -1611,7 +1625,7 @@ name = "fil_actor_multisig" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actors_runtime", "frc42_dispatch", "fvm_ipld_blockstore", @@ -1631,7 +1645,7 @@ name = "fil_actor_paych" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "derive_builder", "fil_actors_runtime", "frc42_dispatch", @@ -1649,7 +1663,7 @@ name = "fil_actor_power" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actor_reward", "fil_actors_runtime", "frc42_dispatch", @@ -1687,7 +1701,7 @@ name = "fil_actor_system" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -1702,7 +1716,7 @@ name = "fil_actor_verifreg" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fil_actors_runtime", "frc42_dispatch", "frc46_token", @@ -1724,7 +1738,7 @@ dependencies = [ "anyhow", "blake2b_simd", "byteorder", - "cid", + "cid 0.8.6", "derive_builder", "fvm_ipld_amt", "fvm_ipld_blockstore", @@ -1737,7 +1751,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "multihash", + "multihash 0.16.3", "num-derive", "num-traits", "paste", @@ -1754,7 +1768,7 @@ dependencies = [ name = "fil_builtin_actors_bundle" version = "10.0.0-alpha.1" dependencies = [ - "cid", + "cid 0.8.6", "clap", "fil_actor_account", "fil_actor_bundler", @@ -1781,7 +1795,7 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "bimap", - "cid", + "cid 0.8.6", "fil_actor_account", "fil_actor_cron", "fil_actor_datacap", @@ -1881,7 +1895,7 @@ version = "1.1.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_amt", @@ -2030,7 +2044,7 @@ version = "0.1.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2047,7 +2061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21efabb8ae696f1cfd90b176a3600176010bd6ad3fb9919b16a24b43ba866b3d" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fvm_ipld_blockstore", "fvm_ipld_encoding", "itertools", @@ -2075,8 +2089,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" dependencies = [ "anyhow", - "cid", - "multihash", + "cid 0.8.6", + "multihash 0.16.3", ] [[package]] @@ -2085,7 +2099,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c60423568393a284de6d7c342cd664690611f27d223eb78629fa568ddd4e7951" dependencies = [ - "cid", + "cid 0.8.6", "futures", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2101,9 +2115,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf531c59e7c9a4c9044a34d1b44c9d544fab1eb0ba50aaa18121fe698d4fec35" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "fvm_ipld_blockstore", - "multihash", + "multihash 0.16.3", "serde", "serde_ipld_dagcbor", "serde_repr", @@ -2119,12 +2133,32 @@ checksum = "0c942494dde990aeac314311bde34c787be99cab7d0836397a75556cbaa2c3e7" dependencies = [ "anyhow", "byteorder", - "cid", + "cid 0.8.6", "forest_hash_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding", "libipld-core 0.14.0", - "multihash", + "multihash 0.16.3", + "once_cell", + "serde", + "sha2 0.10.6", + "thiserror", +] + +[[package]] +name = "fvm_ipld_kamt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72011dfb8938afdd752563bbf43fb900494c9c220dda8d3029cf675f2b6cb8b8" +dependencies = [ + "anyhow", + "byteorder", + "cid 0.8.6", + "forest_hash_utils", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "libipld-core 0.15.0", + "multihash 0.16.3", "once_cell", "serde", "sha2 0.10.6", @@ -2137,7 +2171,7 @@ version = "3.0.0-alpha.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "820720405923ef64f9ec5b1c7bb523629763d888f34904c8ff3bdd69de652195" dependencies = [ - "cid", + "cid 0.8.6", "fvm_ipld_encoding", "fvm_shared", "lazy_static", @@ -2156,14 +2190,14 @@ dependencies = [ "bitflags", "blake2b_simd", "byteorder", - "cid", + "cid 0.8.6", "data-encoding", "data-encoding-macro", "fvm_ipld_blockstore", "fvm_ipld_encoding", "lazy_static", "log", - "multihash", + "multihash 0.16.3", "num-bigint", "num-derive", "num-integer", @@ -2557,9 +2591,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "libipld-core" @@ -2568,10 +2602,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdd758764f9680a818af33c31db733eb7c45224715d8816b9dcf0548c75f7c5" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "core2", "multibase", - "multihash", + "multihash 0.16.3", "serde", "thiserror", ] @@ -2583,10 +2617,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" dependencies = [ "anyhow", - "cid", + "cid 0.8.6", "core2", "multibase", - "multihash", + "multihash 0.16.3", + "serde", + "thiserror", +] + +[[package]] +name = "libipld-core" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a704ba3b25dee9e7a2361fae2c7c19defae2a92e69ae96ffb203996705cd7c" +dependencies = [ + "anyhow", + "cid 0.9.0", + "core2", + "multibase", + "multihash 0.17.0", "serde", "thiserror", ] @@ -2712,6 +2761,19 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "multihash-derive", + "serde", + "serde-big-array", + "unsigned-varint", +] + [[package]] name = "multihash-derive" version = "0.8.1" @@ -2971,9 +3033,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a" +checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" dependencies = [ "thiserror", "ucd-trie", @@ -2981,9 +3043,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "423c2ba011d6e27b02b482a3707c773d19aec65cc024637aec44e19652e66f63" +checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344" dependencies = [ "pest", "pest_generator", @@ -2991,9 +3053,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e64e6c2c85031c02fdbd9e5c72845445ca0a724d419aa0bc068ac620c9935c1" +checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c" dependencies = [ "pest", "pest_meta", @@ -3004,9 +3066,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57959b91f0a133f89a68be874a5c88ed689c19cd729ecdb5d762ebf16c64d662" +checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20" dependencies = [ "once_cell", "pest", @@ -3526,9 +3588,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" dependencies = [ "serde_derive", ] @@ -3563,9 +3625,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" dependencies = [ "proc-macro2", "quote", @@ -3579,7 +3641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1e23de7a4a18dff77ab9531f279a882500b8cf3549fde044d4e10481b411f1e" dependencies = [ "cbor4ii", - "cid", + "cid 0.8.6", "scopeguard", "serde", ] @@ -3824,9 +3886,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", @@ -3889,7 +3951,7 @@ dependencies = [ "anyhow", "bimap", "blake2b_simd", - "cid", + "cid 0.8.6", "ethers", "fil_actor_account", "fil_actor_cron", @@ -3918,7 +3980,7 @@ dependencies = [ "integer-encoding", "lazy_static", "log", - "multihash", + "multihash 0.16.3", "num-derive", "num-traits", "rand", @@ -3981,9 +4043,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ "autocfg", "bytes", @@ -3993,7 +4055,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2", - "winapi", + "windows-sys", ] [[package]] @@ -4086,9 +4148,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" diff --git a/Cargo.toml b/Cargo.toml index e1b557cd9..0a9caf00d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,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 = [] @@ -49,25 +49,21 @@ testing-fake-proofs = [] m2-native = [] [workspace] -members = [ - "actors/*", - "state", - "runtime", - "test_vm", -] +members = ["actors/*", "state", "runtime", "test_vm"] [patch.crates-io] frc42_dispatch = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" } frc46_token = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" } fvm_actor_utils = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" } -#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm" } -#fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm" } +# fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_kamt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_amt = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_bitfield = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_encoding = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } +# fvm_ipld_blockstore = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } ## 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. @@ -76,22 +72,24 @@ fvm_actor_utils = { git = "https://github.com/filecoin-project/filecoin-actor-ut #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) +# 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"} -# fvm_ipld_blockstore = { path = "../ref-fvm/ipld/blockstore"} +# fvm_ipld_bitfield = { path = "../ref-fvm/ipld/bitfield" } +# fvm_ipld_encoding = { path = "../ref-fvm/ipld/encoding" } +# fvm_ipld_blockstore = { path = "../ref-fvm/ipld/blockstore" } [profile.wasm] inherits = "release" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 80290cdf2..16d2e9ab0 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.13", default-features = false } -fvm_ipld_hamt = "0.6.1" +fvm_ipld_kamt = { version = "0.1.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" num-traits = "0.2.14" diff --git a/actors/evm/Makefile b/actors/evm/Makefile index b1d4d69ca..3a5b6dad3 100644 --- a/actors/evm/Makefile +++ b/actors/evm/Makefile @@ -7,12 +7,10 @@ MEASUREMENTS_JSON = $(shell find $(MEASUREMENTS_DIR) -type f -name "*.jsonline") MEASUREMENTS_PNG = $(MEASUREMENTS_JSON:.jsonline=.png) .PHONY: all -all: test-contracts - -# Run storage footprint tests. -measure-storage-footprint: - cargo test --test storage_footprint - +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/ @@ -27,12 +25,22 @@ $(TEST_CONTRACTS_DIR)/%.hex: $(TEST_CONTRACTS_DIR)/%.sol | solc 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 | jq gnuplot +$(MEASUREMENTS_DIR)/%.png: \ + $(MEASUREMENTS_DIR)/%.jsonline \ + $(MEASUREMENTS_DIR)/storage-footprint.plt \ + $(MEASUREMENTS_DIR)/storage-footprint.sh \ + | jq gnuplot cd $(MEASUREMENTS_DIR) && ./storage-footprint.sh $* @@ -54,7 +62,7 @@ gnuplot: .PHONY: jq jq: - @if [ -z "$(shell which gnuplot)" ]; then \ + @if [ -z "$(shell which jq)" ]; then \ echo "Please install jq. See https://stedolan.github.io/jq/"; \ exit 1; \ fi diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index c8f722309..26e47e47f 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -1,8 +1,11 @@ #![allow(dead_code)] +use std::borrow::Cow; + use fil_actors_runtime::{actor_error, runtime::EMPTY_ARR_CID, AsActorError, EAM_ACTOR_ID}; use fvm_ipld_blockstore::Block; use fvm_ipld_encoding::{CborStore, RawBytes}; +use fvm_ipld_kamt::HashedKey; use fvm_shared::{ address::{Address, Payload}, econ::TokenAmount, @@ -20,9 +23,46 @@ use { cid::Cid, fil_actors_runtime::{runtime::Runtime, ActorError}, fvm_ipld_blockstore::Blockstore, - fvm_ipld_hamt::Hamt, + fvm_ipld_kamt::{AsHashedKey, Config as KamtConfig, Kamt}, }; +lazy_static::lazy_static! { + // 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. + // 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. + static ref KAMT_CONFIG: KamtConfig = KamtConfig { + min_data_depth: 2, + bit_width: 5, + max_array_width: 3 + }; +} + +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; @@ -35,7 +75,7 @@ pub struct System<'r, RT: Runtime> { /// The current bytecode. This is usually only "none" when the actor is first constructed. bytecode: Option, /// The contract's EVM storage slots. - slots: Hamt, + slots: StateKamt, /// The contracts "nonce" (incremented when creating new actors). nonce: u64, /// The last saved state root. None if the current state hasn't been saved yet. @@ -58,7 +98,7 @@ impl<'r, RT: Runtime> System<'r, RT> { let store = rt.store().clone(); Ok(Self { rt, - slots: Hamt::<_, U256, U256>::new(store), + slots: StateKamt::new_with_config(store, KAMT_CONFIG.clone()), nonce: 1, saved_state_root: None, bytecode: None, @@ -81,7 +121,7 @@ impl<'r, RT: Runtime> System<'r, RT> { Ok(Self { rt, - slots: Hamt::<_, U256, U256>::load(&state.contract_state, store) + 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), diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 77c75efa0..f87aba173 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -147,16 +147,6 @@ impl<'de> Deserialize<'de> for U256 { } } -macro_rules! impl_hamt_hash { - ($type:ident) => { - impl fvm_ipld_hamt::Hash for $type { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } - } - }; -} - fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { let v = v.as_ref(); &v[v.iter().take_while(|&&b| b == 0).count()..] @@ -182,10 +172,6 @@ macro_rules! impl_rlp_codec_uint { }; } -// Hamt support -impl_hamt_hash!(U256); -impl_hamt_hash!(U512); - // RLP Support impl_rlp_codec_uint!(U256, 32); impl_rlp_codec_uint!(U512, 64); diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 05adad380..fa9358c06 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -16,7 +16,7 @@ pub struct State { /// The EVM contract state dictionary. /// All eth contract state is a map of U256 -> U256 values. /// - /// HAMT + /// KAMT pub contract_state: Cid, /// The EVM nonce used to track how many times CREATE or CREATE2 have been called. diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index c3d42fa00..4357decd7 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2248,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2157,"get_count":3,"put_bytes":153,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2161,"get_count":3,"put_bytes":157,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2165,"get_count":3,"put_bytes":161,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2169,"get_count":3,"put_bytes":165,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2173,"get_count":3,"put_bytes":169,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2177,"get_count":3,"put_bytes":174,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2182,"get_count":3,"put_bytes":178,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2186,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":234,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2242,"get_count":3,"put_bytes":238,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":242,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2250,"get_count":3,"put_bytes":246,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2254,"get_count":3,"put_bytes":250,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2258,"get_count":3,"put_bytes":255,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":2263,"get_count":3,"put_bytes":259,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":2267,"get_count":3,"put_bytes":297,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":2305,"get_count":3,"put_bytes":301,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2309,"get_count":3,"put_bytes":305,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2313,"get_count":3,"put_bytes":309,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":2317,"get_count":3,"put_bytes":313,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2321,"get_count":3,"put_bytes":317,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2325,"get_count":3,"put_bytes":322,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2330,"get_count":3,"put_bytes":326,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":2334,"get_count":3,"put_bytes":364,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":2372,"get_count":3,"put_bytes":368,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":2376,"get_count":3,"put_bytes":372,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":2380,"get_count":3,"put_bytes":376,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":2384,"get_count":3,"put_bytes":380,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":2388,"get_count":3,"put_bytes":384,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":2392,"get_count":3,"put_bytes":389,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":2397,"get_count":3,"put_bytes":393,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":2401,"get_count":3,"put_bytes":430,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":2438,"get_count":3,"put_bytes":434,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":2442,"get_count":3,"put_bytes":438,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":2446,"get_count":3,"put_bytes":442,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":2450,"get_count":3,"put_bytes":446,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":2454,"get_count":3,"put_bytes":450,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":2458,"get_count":3,"put_bytes":455,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":2463,"get_count":3,"put_bytes":459,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":2467,"get_count":3,"put_bytes":497,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":2505,"get_count":3,"put_bytes":501,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":2509,"get_count":3,"put_bytes":505,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":2513,"get_count":3,"put_bytes":509,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":2517,"get_count":3,"put_bytes":513,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":2521,"get_count":3,"put_bytes":517,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":2525,"get_count":3,"put_bytes":522,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":2530,"get_count":3,"put_bytes":526,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":2534,"get_count":3,"put_bytes":565,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":2573,"get_count":3,"put_bytes":569,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":2577,"get_count":3,"put_bytes":573,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":2581,"get_count":3,"put_bytes":577,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":2585,"get_count":3,"put_bytes":581,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":2589,"get_count":3,"put_bytes":585,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":2593,"get_count":3,"put_bytes":590,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":2598,"get_count":3,"put_bytes":594,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":2602,"get_count":3,"put_bytes":632,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":2640,"get_count":3,"put_bytes":636,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":2644,"get_count":3,"put_bytes":640,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":2648,"get_count":3,"put_bytes":644,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":2652,"get_count":3,"put_bytes":648,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":2656,"get_count":3,"put_bytes":652,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":2660,"get_count":3,"put_bytes":657,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":2665,"get_count":3,"put_bytes":661,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":2669,"get_count":3,"put_bytes":699,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":2707,"get_count":3,"put_bytes":703,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":2711,"get_count":3,"put_bytes":707,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":2715,"get_count":3,"put_bytes":711,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":2719,"get_count":3,"put_bytes":715,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":2723,"get_count":3,"put_bytes":719,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":2727,"get_count":3,"put_bytes":724,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":2732,"get_count":3,"put_bytes":728,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":2736,"get_count":3,"put_bytes":766,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":2774,"get_count":3,"put_bytes":770,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":2778,"get_count":3,"put_bytes":774,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":2782,"get_count":3,"put_bytes":778,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":2786,"get_count":3,"put_bytes":782,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":2790,"get_count":3,"put_bytes":786,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":2794,"get_count":3,"put_bytes":791,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":2799,"get_count":3,"put_bytes":795,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":2803,"get_count":3,"put_bytes":833,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":2841,"get_count":3,"put_bytes":837,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":2845,"get_count":3,"put_bytes":841,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":2849,"get_count":3,"put_bytes":845,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":2853,"get_count":3,"put_bytes":849,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":2857,"get_count":3,"put_bytes":853,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":2861,"get_count":3,"put_bytes":858,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":2866,"get_count":3,"put_bytes":862,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":2870,"get_count":3,"put_bytes":900,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":2908,"get_count":3,"put_bytes":904,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":2912,"get_count":3,"put_bytes":908,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":2916,"get_count":3,"put_bytes":912,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":2920,"get_count":3,"put_bytes":916,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":2924,"get_count":3,"put_bytes":920,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":2928,"get_count":3,"put_bytes":925,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":2933,"get_count":3,"put_bytes":929,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":2937,"get_count":3,"put_bytes":967,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":2975,"get_count":3,"put_bytes":971,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":2979,"get_count":3,"put_bytes":975,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":2983,"get_count":3,"put_bytes":979,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":2987,"get_count":3,"put_bytes":1023,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":3031,"get_count":3,"put_bytes":1027,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3035,"get_count":3,"put_bytes":1031,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1035,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":3043,"get_count":3,"put_bytes":1039,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":3047,"get_count":3,"put_bytes":1043,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":3051,"get_count":3,"put_bytes":1048,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":3056,"get_count":3,"put_bytes":1052,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":3060,"get_count":3,"put_bytes":1090,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":3098,"get_count":3,"put_bytes":1094,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":3102,"get_count":3,"put_bytes":1098,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":3106,"get_count":3,"put_bytes":1102,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":3110,"get_count":3,"put_bytes":1106,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":3114,"get_count":3,"put_bytes":1110,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":3118,"get_count":3,"put_bytes":1115,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":3123,"get_count":3,"put_bytes":1119,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1156,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":3164,"get_count":3,"put_bytes":1160,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":3168,"get_count":3,"put_bytes":1164,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":3172,"get_count":3,"put_bytes":1168,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":3176,"get_count":3,"put_bytes":1172,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":3180,"get_count":3,"put_bytes":1176,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":3184,"get_count":3,"put_bytes":1181,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":3189,"get_count":3,"put_bytes":1185,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":3193,"get_count":3,"put_bytes":1223,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":3231,"get_count":3,"put_bytes":1227,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":3235,"get_count":3,"put_bytes":1231,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":3239,"get_count":3,"put_bytes":1235,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":3243,"get_count":3,"put_bytes":1239,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":3247,"get_count":3,"put_bytes":1243,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":3251,"get_count":3,"put_bytes":1248,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":3256,"get_count":3,"put_bytes":1252,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1290,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":3298,"get_count":3,"put_bytes":1294,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":3302,"get_count":3,"put_bytes":1298,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":3306,"get_count":3,"put_bytes":1302,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":3310,"get_count":3,"put_bytes":1306,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":3314,"get_count":3,"put_bytes":1310,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":3318,"get_count":3,"put_bytes":1315,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":3323,"get_count":3,"put_bytes":1319,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":3327,"get_count":3,"put_bytes":1357,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":3365,"get_count":3,"put_bytes":1361,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":3369,"get_count":3,"put_bytes":1365,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":3373,"get_count":3,"put_bytes":1369,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":3377,"get_count":3,"put_bytes":1373,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":3381,"get_count":3,"put_bytes":1377,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":3385,"get_count":3,"put_bytes":1382,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":3390,"get_count":3,"put_bytes":1386,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":3394,"get_count":3,"put_bytes":1426,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":3434,"get_count":3,"put_bytes":1430,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":3438,"get_count":3,"put_bytes":1434,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":3442,"get_count":3,"put_bytes":1438,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":3446,"get_count":3,"put_bytes":1442,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":3450,"get_count":3,"put_bytes":1446,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":3454,"get_count":3,"put_bytes":1451,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":3459,"get_count":3,"put_bytes":1455,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":3463,"get_count":3,"put_bytes":1493,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":3501,"get_count":3,"put_bytes":1497,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":3505,"get_count":3,"put_bytes":1501,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":3509,"get_count":3,"put_bytes":1505,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":3513,"get_count":3,"put_bytes":1509,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":3517,"get_count":3,"put_bytes":1513,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":3521,"get_count":3,"put_bytes":1518,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":3526,"get_count":3,"put_bytes":1522,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1560,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":3568,"get_count":3,"put_bytes":1564,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":3572,"get_count":3,"put_bytes":1568,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":3576,"get_count":3,"put_bytes":1572,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":3580,"get_count":3,"put_bytes":1576,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":3584,"get_count":3,"put_bytes":1580,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":3588,"get_count":3,"put_bytes":1585,"put_count":2}} -{"i":71,"series":2,"stats":{"get_bytes":3593,"get_count":3,"put_bytes":1589,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":3597,"get_count":3,"put_bytes":1627,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":3635,"get_count":3,"put_bytes":1631,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":3639,"get_count":3,"put_bytes":1635,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":3643,"get_count":3,"put_bytes":1639,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":3647,"get_count":3,"put_bytes":1643,"put_count":2}} -{"i":77,"series":2,"stats":{"get_bytes":3651,"get_count":3,"put_bytes":1647,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":3655,"get_count":3,"put_bytes":1652,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":3660,"get_count":3,"put_bytes":1656,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":3664,"get_count":3,"put_bytes":1695,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":3703,"get_count":3,"put_bytes":1699,"put_count":2}} -{"i":82,"series":2,"stats":{"get_bytes":3707,"get_count":3,"put_bytes":1703,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":3711,"get_count":3,"put_bytes":1707,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":3715,"get_count":3,"put_bytes":1711,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":3719,"get_count":3,"put_bytes":1715,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":3723,"get_count":3,"put_bytes":1720,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":3728,"get_count":3,"put_bytes":1724,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":3732,"get_count":3,"put_bytes":1761,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":3769,"get_count":3,"put_bytes":1765,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":3773,"get_count":3,"put_bytes":1769,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":3777,"get_count":3,"put_bytes":1773,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":3781,"get_count":3,"put_bytes":1777,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":3785,"get_count":3,"put_bytes":1781,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":3789,"get_count":3,"put_bytes":1786,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":3794,"get_count":3,"put_bytes":1790,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":3798,"get_count":3,"put_bytes":1828,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":3836,"get_count":3,"put_bytes":1832,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1836,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":3844,"get_count":3,"put_bytes":1840,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2515,"put_count":9}} +{"i":1,"series":1,"stats":{"get_bytes":2424,"get_count":7,"put_bytes":420,"put_count":6}} +{"i":2,"series":1,"stats":{"get_bytes":2428,"get_count":7,"put_bytes":424,"put_count":6}} +{"i":3,"series":1,"stats":{"get_bytes":2432,"get_count":7,"put_bytes":428,"put_count":6}} +{"i":4,"series":1,"stats":{"get_bytes":2436,"get_count":7,"put_bytes":432,"put_count":6}} +{"i":5,"series":1,"stats":{"get_bytes":2440,"get_count":7,"put_bytes":436,"put_count":6}} +{"i":6,"series":1,"stats":{"get_bytes":2444,"get_count":7,"put_bytes":441,"put_count":6}} +{"i":7,"series":1,"stats":{"get_bytes":2449,"get_count":7,"put_bytes":445,"put_count":6}} +{"i":8,"series":1,"stats":{"get_bytes":2453,"get_count":7,"put_bytes":482,"put_count":6}} +{"i":9,"series":1,"stats":{"get_bytes":2490,"get_count":7,"put_bytes":486,"put_count":6}} +{"i":10,"series":1,"stats":{"get_bytes":2494,"get_count":7,"put_bytes":490,"put_count":6}} +{"i":11,"series":1,"stats":{"get_bytes":2498,"get_count":7,"put_bytes":494,"put_count":6}} +{"i":12,"series":1,"stats":{"get_bytes":2502,"get_count":7,"put_bytes":498,"put_count":6}} +{"i":13,"series":1,"stats":{"get_bytes":2506,"get_count":7,"put_bytes":502,"put_count":6}} +{"i":14,"series":1,"stats":{"get_bytes":2510,"get_count":7,"put_bytes":507,"put_count":6}} +{"i":15,"series":1,"stats":{"get_bytes":2515,"get_count":7,"put_bytes":511,"put_count":6}} +{"i":16,"series":1,"stats":{"get_bytes":2519,"get_count":7,"put_bytes":548,"put_count":6}} +{"i":17,"series":1,"stats":{"get_bytes":2556,"get_count":7,"put_bytes":552,"put_count":6}} +{"i":18,"series":1,"stats":{"get_bytes":2560,"get_count":7,"put_bytes":556,"put_count":6}} +{"i":19,"series":1,"stats":{"get_bytes":2564,"get_count":7,"put_bytes":560,"put_count":6}} +{"i":20,"series":1,"stats":{"get_bytes":2568,"get_count":7,"put_bytes":564,"put_count":6}} +{"i":21,"series":1,"stats":{"get_bytes":2572,"get_count":7,"put_bytes":568,"put_count":6}} +{"i":22,"series":1,"stats":{"get_bytes":2576,"get_count":7,"put_bytes":573,"put_count":6}} +{"i":23,"series":1,"stats":{"get_bytes":2581,"get_count":7,"put_bytes":577,"put_count":6}} +{"i":24,"series":1,"stats":{"get_bytes":2585,"get_count":7,"put_bytes":746,"put_count":7}} +{"i":25,"series":1,"stats":{"get_bytes":2754,"get_count":8,"put_bytes":750,"put_count":7}} +{"i":26,"series":1,"stats":{"get_bytes":2758,"get_count":8,"put_bytes":754,"put_count":7}} +{"i":27,"series":1,"stats":{"get_bytes":2762,"get_count":8,"put_bytes":758,"put_count":7}} +{"i":28,"series":1,"stats":{"get_bytes":2766,"get_count":8,"put_bytes":762,"put_count":7}} +{"i":29,"series":1,"stats":{"get_bytes":2770,"get_count":8,"put_bytes":766,"put_count":7}} +{"i":30,"series":1,"stats":{"get_bytes":2774,"get_count":8,"put_bytes":771,"put_count":7}} +{"i":31,"series":1,"stats":{"get_bytes":2779,"get_count":8,"put_bytes":775,"put_count":7}} +{"i":32,"series":1,"stats":{"get_bytes":2783,"get_count":8,"put_bytes":816,"put_count":7}} +{"i":33,"series":1,"stats":{"get_bytes":2824,"get_count":8,"put_bytes":820,"put_count":7}} +{"i":34,"series":1,"stats":{"get_bytes":2828,"get_count":8,"put_bytes":824,"put_count":7}} +{"i":35,"series":1,"stats":{"get_bytes":2832,"get_count":8,"put_bytes":828,"put_count":7}} +{"i":36,"series":1,"stats":{"get_bytes":2836,"get_count":8,"put_bytes":832,"put_count":7}} +{"i":37,"series":1,"stats":{"get_bytes":2840,"get_count":8,"put_bytes":836,"put_count":7}} +{"i":38,"series":1,"stats":{"get_bytes":2844,"get_count":8,"put_bytes":841,"put_count":7}} +{"i":39,"series":1,"stats":{"get_bytes":2849,"get_count":8,"put_bytes":845,"put_count":7}} +{"i":40,"series":1,"stats":{"get_bytes":2853,"get_count":8,"put_bytes":882,"put_count":7}} +{"i":41,"series":1,"stats":{"get_bytes":2890,"get_count":8,"put_bytes":886,"put_count":7}} +{"i":42,"series":1,"stats":{"get_bytes":2894,"get_count":8,"put_bytes":890,"put_count":7}} +{"i":43,"series":1,"stats":{"get_bytes":2898,"get_count":8,"put_bytes":894,"put_count":7}} +{"i":44,"series":1,"stats":{"get_bytes":2902,"get_count":8,"put_bytes":898,"put_count":7}} +{"i":45,"series":1,"stats":{"get_bytes":2906,"get_count":8,"put_bytes":902,"put_count":7}} +{"i":46,"series":1,"stats":{"get_bytes":2910,"get_count":8,"put_bytes":907,"put_count":7}} +{"i":47,"series":1,"stats":{"get_bytes":2915,"get_count":8,"put_bytes":911,"put_count":7}} +{"i":48,"series":1,"stats":{"get_bytes":2919,"get_count":8,"put_bytes":952,"put_count":7}} +{"i":49,"series":1,"stats":{"get_bytes":2960,"get_count":8,"put_bytes":956,"put_count":7}} +{"i":50,"series":1,"stats":{"get_bytes":2964,"get_count":8,"put_bytes":960,"put_count":7}} +{"i":51,"series":1,"stats":{"get_bytes":2968,"get_count":8,"put_bytes":964,"put_count":7}} +{"i":52,"series":1,"stats":{"get_bytes":2972,"get_count":8,"put_bytes":968,"put_count":7}} +{"i":53,"series":1,"stats":{"get_bytes":2976,"get_count":8,"put_bytes":972,"put_count":7}} +{"i":54,"series":1,"stats":{"get_bytes":2980,"get_count":8,"put_bytes":977,"put_count":7}} +{"i":55,"series":1,"stats":{"get_bytes":2985,"get_count":8,"put_bytes":981,"put_count":7}} +{"i":56,"series":1,"stats":{"get_bytes":2989,"get_count":8,"put_bytes":1018,"put_count":7}} +{"i":57,"series":1,"stats":{"get_bytes":3026,"get_count":8,"put_bytes":1022,"put_count":7}} +{"i":58,"series":1,"stats":{"get_bytes":3030,"get_count":8,"put_bytes":1026,"put_count":7}} +{"i":59,"series":1,"stats":{"get_bytes":3034,"get_count":8,"put_bytes":1030,"put_count":7}} +{"i":60,"series":1,"stats":{"get_bytes":3038,"get_count":8,"put_bytes":1034,"put_count":7}} +{"i":61,"series":1,"stats":{"get_bytes":3042,"get_count":8,"put_bytes":1038,"put_count":7}} +{"i":62,"series":1,"stats":{"get_bytes":3046,"get_count":8,"put_bytes":1043,"put_count":7}} +{"i":63,"series":1,"stats":{"get_bytes":3051,"get_count":8,"put_bytes":1047,"put_count":7}} +{"i":64,"series":1,"stats":{"get_bytes":3055,"get_count":8,"put_bytes":1088,"put_count":7}} +{"i":65,"series":1,"stats":{"get_bytes":3096,"get_count":8,"put_bytes":1092,"put_count":7}} +{"i":66,"series":1,"stats":{"get_bytes":3100,"get_count":8,"put_bytes":1096,"put_count":7}} +{"i":67,"series":1,"stats":{"get_bytes":3104,"get_count":8,"put_bytes":1100,"put_count":7}} +{"i":68,"series":1,"stats":{"get_bytes":3108,"get_count":8,"put_bytes":1104,"put_count":7}} +{"i":69,"series":1,"stats":{"get_bytes":3112,"get_count":8,"put_bytes":1108,"put_count":7}} +{"i":70,"series":1,"stats":{"get_bytes":3116,"get_count":8,"put_bytes":1113,"put_count":7}} +{"i":71,"series":1,"stats":{"get_bytes":3121,"get_count":8,"put_bytes":1117,"put_count":7}} +{"i":72,"series":1,"stats":{"get_bytes":3125,"get_count":8,"put_bytes":1154,"put_count":7}} +{"i":73,"series":1,"stats":{"get_bytes":3162,"get_count":8,"put_bytes":1158,"put_count":7}} +{"i":74,"series":1,"stats":{"get_bytes":3166,"get_count":8,"put_bytes":1162,"put_count":7}} +{"i":75,"series":1,"stats":{"get_bytes":3170,"get_count":8,"put_bytes":1166,"put_count":7}} +{"i":76,"series":1,"stats":{"get_bytes":3174,"get_count":8,"put_bytes":1170,"put_count":7}} +{"i":77,"series":1,"stats":{"get_bytes":3178,"get_count":8,"put_bytes":1174,"put_count":7}} +{"i":78,"series":1,"stats":{"get_bytes":3182,"get_count":8,"put_bytes":1179,"put_count":7}} +{"i":79,"series":1,"stats":{"get_bytes":3187,"get_count":8,"put_bytes":1183,"put_count":7}} +{"i":80,"series":1,"stats":{"get_bytes":3191,"get_count":8,"put_bytes":605,"put_count":7}} +{"i":81,"series":1,"stats":{"get_bytes":2613,"get_count":8,"put_bytes":609,"put_count":7}} +{"i":82,"series":1,"stats":{"get_bytes":2617,"get_count":8,"put_bytes":613,"put_count":7}} +{"i":83,"series":1,"stats":{"get_bytes":2621,"get_count":8,"put_bytes":617,"put_count":7}} +{"i":84,"series":1,"stats":{"get_bytes":2625,"get_count":8,"put_bytes":621,"put_count":7}} +{"i":85,"series":1,"stats":{"get_bytes":2629,"get_count":8,"put_bytes":625,"put_count":7}} +{"i":86,"series":1,"stats":{"get_bytes":2633,"get_count":8,"put_bytes":630,"put_count":7}} +{"i":87,"series":1,"stats":{"get_bytes":2638,"get_count":8,"put_bytes":634,"put_count":7}} +{"i":88,"series":1,"stats":{"get_bytes":2642,"get_count":8,"put_bytes":671,"put_count":7}} +{"i":89,"series":1,"stats":{"get_bytes":2679,"get_count":8,"put_bytes":675,"put_count":7}} +{"i":90,"series":1,"stats":{"get_bytes":2683,"get_count":8,"put_bytes":679,"put_count":7}} +{"i":91,"series":1,"stats":{"get_bytes":2687,"get_count":8,"put_bytes":683,"put_count":7}} +{"i":92,"series":1,"stats":{"get_bytes":2691,"get_count":8,"put_bytes":687,"put_count":7}} +{"i":93,"series":1,"stats":{"get_bytes":2695,"get_count":8,"put_bytes":691,"put_count":7}} +{"i":94,"series":1,"stats":{"get_bytes":2699,"get_count":8,"put_bytes":696,"put_count":7}} +{"i":95,"series":1,"stats":{"get_bytes":2704,"get_count":8,"put_bytes":700,"put_count":7}} +{"i":96,"series":1,"stats":{"get_bytes":2708,"get_count":8,"put_bytes":737,"put_count":7}} +{"i":97,"series":1,"stats":{"get_bytes":2745,"get_count":8,"put_bytes":741,"put_count":7}} +{"i":98,"series":1,"stats":{"get_bytes":2749,"get_count":8,"put_bytes":745,"put_count":7}} +{"i":99,"series":1,"stats":{"get_bytes":2753,"get_count":8,"put_bytes":749,"put_count":7}} +{"i":0,"series":2,"stats":{"get_bytes":2311,"get_count":5,"put_bytes":486,"put_count":6}} +{"i":1,"series":2,"stats":{"get_bytes":2494,"get_count":7,"put_bytes":490,"put_count":6}} +{"i":2,"series":2,"stats":{"get_bytes":2498,"get_count":7,"put_bytes":494,"put_count":6}} +{"i":3,"series":2,"stats":{"get_bytes":2502,"get_count":7,"put_bytes":498,"put_count":6}} +{"i":4,"series":2,"stats":{"get_bytes":2506,"get_count":7,"put_bytes":502,"put_count":6}} +{"i":5,"series":2,"stats":{"get_bytes":2510,"get_count":7,"put_bytes":506,"put_count":6}} +{"i":6,"series":2,"stats":{"get_bytes":2514,"get_count":7,"put_bytes":511,"put_count":6}} +{"i":7,"series":2,"stats":{"get_bytes":2519,"get_count":7,"put_bytes":515,"put_count":6}} +{"i":8,"series":2,"stats":{"get_bytes":2523,"get_count":7,"put_bytes":552,"put_count":6}} +{"i":9,"series":2,"stats":{"get_bytes":2560,"get_count":7,"put_bytes":556,"put_count":6}} +{"i":10,"series":2,"stats":{"get_bytes":2564,"get_count":7,"put_bytes":560,"put_count":6}} +{"i":11,"series":2,"stats":{"get_bytes":2568,"get_count":7,"put_bytes":564,"put_count":6}} +{"i":12,"series":2,"stats":{"get_bytes":2572,"get_count":7,"put_bytes":568,"put_count":6}} +{"i":13,"series":2,"stats":{"get_bytes":2576,"get_count":7,"put_bytes":572,"put_count":6}} +{"i":14,"series":2,"stats":{"get_bytes":2580,"get_count":7,"put_bytes":577,"put_count":6}} +{"i":15,"series":2,"stats":{"get_bytes":2585,"get_count":7,"put_bytes":581,"put_count":6}} +{"i":16,"series":2,"stats":{"get_bytes":2589,"get_count":7,"put_bytes":618,"put_count":6}} +{"i":17,"series":2,"stats":{"get_bytes":2626,"get_count":7,"put_bytes":622,"put_count":6}} +{"i":18,"series":2,"stats":{"get_bytes":2630,"get_count":7,"put_bytes":626,"put_count":6}} +{"i":19,"series":2,"stats":{"get_bytes":2634,"get_count":7,"put_bytes":630,"put_count":6}} +{"i":20,"series":2,"stats":{"get_bytes":2638,"get_count":7,"put_bytes":634,"put_count":6}} +{"i":21,"series":2,"stats":{"get_bytes":2642,"get_count":7,"put_bytes":638,"put_count":6}} +{"i":22,"series":2,"stats":{"get_bytes":2646,"get_count":7,"put_bytes":643,"put_count":6}} +{"i":23,"series":2,"stats":{"get_bytes":2651,"get_count":7,"put_bytes":647,"put_count":6}} +{"i":24,"series":2,"stats":{"get_bytes":2655,"get_count":7,"put_bytes":816,"put_count":7}} +{"i":25,"series":2,"stats":{"get_bytes":2824,"get_count":8,"put_bytes":820,"put_count":7}} +{"i":26,"series":2,"stats":{"get_bytes":2828,"get_count":8,"put_bytes":824,"put_count":7}} +{"i":27,"series":2,"stats":{"get_bytes":2832,"get_count":8,"put_bytes":828,"put_count":7}} +{"i":28,"series":2,"stats":{"get_bytes":2836,"get_count":8,"put_bytes":832,"put_count":7}} +{"i":29,"series":2,"stats":{"get_bytes":2840,"get_count":8,"put_bytes":836,"put_count":7}} +{"i":30,"series":2,"stats":{"get_bytes":2844,"get_count":8,"put_bytes":841,"put_count":7}} +{"i":31,"series":2,"stats":{"get_bytes":2849,"get_count":8,"put_bytes":845,"put_count":7}} +{"i":32,"series":2,"stats":{"get_bytes":2853,"get_count":8,"put_bytes":886,"put_count":7}} +{"i":33,"series":2,"stats":{"get_bytes":2894,"get_count":8,"put_bytes":890,"put_count":7}} +{"i":34,"series":2,"stats":{"get_bytes":2898,"get_count":8,"put_bytes":894,"put_count":7}} +{"i":35,"series":2,"stats":{"get_bytes":2902,"get_count":8,"put_bytes":898,"put_count":7}} +{"i":36,"series":2,"stats":{"get_bytes":2906,"get_count":8,"put_bytes":902,"put_count":7}} +{"i":37,"series":2,"stats":{"get_bytes":2910,"get_count":8,"put_bytes":906,"put_count":7}} +{"i":38,"series":2,"stats":{"get_bytes":2914,"get_count":8,"put_bytes":911,"put_count":7}} +{"i":39,"series":2,"stats":{"get_bytes":2919,"get_count":8,"put_bytes":915,"put_count":7}} +{"i":40,"series":2,"stats":{"get_bytes":2923,"get_count":8,"put_bytes":952,"put_count":7}} +{"i":41,"series":2,"stats":{"get_bytes":2960,"get_count":8,"put_bytes":956,"put_count":7}} +{"i":42,"series":2,"stats":{"get_bytes":2964,"get_count":8,"put_bytes":960,"put_count":7}} +{"i":43,"series":2,"stats":{"get_bytes":2968,"get_count":8,"put_bytes":964,"put_count":7}} +{"i":44,"series":2,"stats":{"get_bytes":2972,"get_count":8,"put_bytes":968,"put_count":7}} +{"i":45,"series":2,"stats":{"get_bytes":2976,"get_count":8,"put_bytes":972,"put_count":7}} +{"i":46,"series":2,"stats":{"get_bytes":2980,"get_count":8,"put_bytes":977,"put_count":7}} +{"i":47,"series":2,"stats":{"get_bytes":2985,"get_count":8,"put_bytes":981,"put_count":7}} +{"i":48,"series":2,"stats":{"get_bytes":2989,"get_count":8,"put_bytes":1022,"put_count":7}} +{"i":49,"series":2,"stats":{"get_bytes":3030,"get_count":8,"put_bytes":1026,"put_count":7}} +{"i":50,"series":2,"stats":{"get_bytes":3034,"get_count":8,"put_bytes":1030,"put_count":7}} +{"i":51,"series":2,"stats":{"get_bytes":3038,"get_count":8,"put_bytes":1034,"put_count":7}} +{"i":52,"series":2,"stats":{"get_bytes":3042,"get_count":8,"put_bytes":1038,"put_count":7}} +{"i":53,"series":2,"stats":{"get_bytes":3046,"get_count":8,"put_bytes":1042,"put_count":7}} +{"i":54,"series":2,"stats":{"get_bytes":3050,"get_count":8,"put_bytes":1047,"put_count":7}} +{"i":55,"series":2,"stats":{"get_bytes":3055,"get_count":8,"put_bytes":1051,"put_count":7}} +{"i":56,"series":2,"stats":{"get_bytes":3059,"get_count":8,"put_bytes":1088,"put_count":7}} +{"i":57,"series":2,"stats":{"get_bytes":3096,"get_count":8,"put_bytes":1092,"put_count":7}} +{"i":58,"series":2,"stats":{"get_bytes":3100,"get_count":8,"put_bytes":1096,"put_count":7}} +{"i":59,"series":2,"stats":{"get_bytes":3104,"get_count":8,"put_bytes":1100,"put_count":7}} +{"i":60,"series":2,"stats":{"get_bytes":3108,"get_count":8,"put_bytes":1104,"put_count":7}} +{"i":61,"series":2,"stats":{"get_bytes":3112,"get_count":8,"put_bytes":1108,"put_count":7}} +{"i":62,"series":2,"stats":{"get_bytes":3116,"get_count":8,"put_bytes":1113,"put_count":7}} +{"i":63,"series":2,"stats":{"get_bytes":3121,"get_count":8,"put_bytes":1117,"put_count":7}} +{"i":64,"series":2,"stats":{"get_bytes":3125,"get_count":8,"put_bytes":1158,"put_count":7}} +{"i":65,"series":2,"stats":{"get_bytes":3166,"get_count":8,"put_bytes":1162,"put_count":7}} +{"i":66,"series":2,"stats":{"get_bytes":3170,"get_count":8,"put_bytes":1166,"put_count":7}} +{"i":67,"series":2,"stats":{"get_bytes":3174,"get_count":8,"put_bytes":1170,"put_count":7}} +{"i":68,"series":2,"stats":{"get_bytes":3178,"get_count":8,"put_bytes":1174,"put_count":7}} +{"i":69,"series":2,"stats":{"get_bytes":3182,"get_count":8,"put_bytes":1178,"put_count":7}} +{"i":70,"series":2,"stats":{"get_bytes":3186,"get_count":8,"put_bytes":1183,"put_count":7}} +{"i":71,"series":2,"stats":{"get_bytes":3191,"get_count":8,"put_bytes":1187,"put_count":7}} +{"i":72,"series":2,"stats":{"get_bytes":3195,"get_count":8,"put_bytes":1224,"put_count":7}} +{"i":73,"series":2,"stats":{"get_bytes":3232,"get_count":8,"put_bytes":1228,"put_count":7}} +{"i":74,"series":2,"stats":{"get_bytes":3236,"get_count":8,"put_bytes":1232,"put_count":7}} +{"i":75,"series":2,"stats":{"get_bytes":3240,"get_count":8,"put_bytes":1236,"put_count":7}} +{"i":76,"series":2,"stats":{"get_bytes":3244,"get_count":8,"put_bytes":1240,"put_count":7}} +{"i":77,"series":2,"stats":{"get_bytes":3248,"get_count":8,"put_bytes":1244,"put_count":7}} +{"i":78,"series":2,"stats":{"get_bytes":3252,"get_count":8,"put_bytes":1249,"put_count":7}} +{"i":79,"series":2,"stats":{"get_bytes":3257,"get_count":8,"put_bytes":1253,"put_count":7}} +{"i":80,"series":2,"stats":{"get_bytes":3261,"get_count":8,"put_bytes":1294,"put_count":7}} +{"i":81,"series":2,"stats":{"get_bytes":3302,"get_count":8,"put_bytes":1298,"put_count":7}} +{"i":82,"series":2,"stats":{"get_bytes":3306,"get_count":8,"put_bytes":1302,"put_count":7}} +{"i":83,"series":2,"stats":{"get_bytes":3310,"get_count":8,"put_bytes":1306,"put_count":7}} +{"i":84,"series":2,"stats":{"get_bytes":3314,"get_count":8,"put_bytes":1310,"put_count":7}} +{"i":85,"series":2,"stats":{"get_bytes":3318,"get_count":8,"put_bytes":1314,"put_count":7}} +{"i":86,"series":2,"stats":{"get_bytes":3322,"get_count":8,"put_bytes":1319,"put_count":7}} +{"i":87,"series":2,"stats":{"get_bytes":3327,"get_count":8,"put_bytes":1323,"put_count":7}} +{"i":88,"series":2,"stats":{"get_bytes":3331,"get_count":8,"put_bytes":1360,"put_count":7}} +{"i":89,"series":2,"stats":{"get_bytes":3368,"get_count":8,"put_bytes":1364,"put_count":7}} +{"i":90,"series":2,"stats":{"get_bytes":3372,"get_count":8,"put_bytes":1368,"put_count":7}} +{"i":91,"series":2,"stats":{"get_bytes":3376,"get_count":8,"put_bytes":1372,"put_count":7}} +{"i":92,"series":2,"stats":{"get_bytes":3380,"get_count":8,"put_bytes":1376,"put_count":7}} +{"i":93,"series":2,"stats":{"get_bytes":3384,"get_count":8,"put_bytes":1380,"put_count":7}} +{"i":94,"series":2,"stats":{"get_bytes":3388,"get_count":8,"put_bytes":1385,"put_count":7}} +{"i":95,"series":2,"stats":{"get_bytes":3393,"get_count":8,"put_bytes":1389,"put_count":7}} +{"i":96,"series":2,"stats":{"get_bytes":3397,"get_count":8,"put_bytes":1430,"put_count":7}} +{"i":97,"series":2,"stats":{"get_bytes":3438,"get_count":8,"put_bytes":1434,"put_count":7}} +{"i":98,"series":2,"stats":{"get_bytes":3442,"get_count":8,"put_bytes":1438,"put_count":7}} +{"i":99,"series":2,"stats":{"get_bytes":3446,"get_count":8,"put_bytes":1442,"put_count":7}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index f1a71469b5a0ab72d9be108259650f8224d042c1..49d1432368931129ee8d40fea9194ac4ef73c286 100644 GIT binary patch literal 15619 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Μ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 VH^NbISmkY-rmC(=g);cf{{oF$sty1E diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 78ca7a2e0..840cad44c 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3078,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2987,"get_count":3,"put_bytes":1802,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":3810,"get_count":3,"put_bytes":2658,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":4666,"get_count":3,"put_bytes":3475,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":5483,"get_count":3,"put_bytes":4328,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":6336,"get_count":3,"put_bytes":5149,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":7157,"get_count":3,"put_bytes":5998,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":8006,"get_count":3,"put_bytes":6817,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":8825,"get_count":3,"put_bytes":7666,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":9674,"get_count":3,"put_bytes":8628,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":10046,"get_count":3,"put_bytes":8885,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":10893,"get_count":3,"put_bytes":9701,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":11709,"get_count":3,"put_bytes":10549,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":12557,"get_count":3,"put_bytes":11427,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":13150,"get_count":3,"put_bytes":11989,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":13997,"get_count":3,"put_bytes":12804,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":14812,"get_count":3,"put_bytes":13731,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":15440,"get_count":3,"put_bytes":14476,"put_count":5}} -{"i":18,"series":1,"stats":{"get_bytes":15585,"get_count":3,"put_bytes":14423,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":16431,"get_count":3,"put_bytes":15315,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":17324,"get_count":4,"put_bytes":16162,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":17800,"get_count":3,"put_bytes":16671,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":18389,"get_count":3,"put_bytes":17227,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":19533,"get_count":4,"put_bytes":18336,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":19979,"get_count":3,"put_bytes":18888,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":20603,"get_count":3,"put_bytes":19477,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":21194,"get_count":3,"put_bytes":20029,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":22327,"get_count":4,"put_bytes":21298,"put_count":5}} -{"i":28,"series":1,"stats":{"get_bytes":22339,"get_count":3,"put_bytes":21248,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":23555,"get_count":5,"put_bytes":22582,"put_count":7}} -{"i":30,"series":1,"stats":{"get_bytes":23263,"get_count":4,"put_bytes":22334,"put_count":6}} -{"i":31,"series":1,"stats":{"get_bytes":23378,"get_count":4,"put_bytes":22553,"put_count":8}} -{"i":32,"series":1,"stats":{"get_bytes":23090,"get_count":4,"put_bytes":21997,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":23938,"get_count":5,"put_bytes":22870,"put_count":6}} -{"i":34,"series":1,"stats":{"get_bytes":24180,"get_count":5,"put_bytes":23102,"put_count":5}} -{"i":35,"series":1,"stats":{"get_bytes":24671,"get_count":5,"put_bytes":23634,"put_count":6}} -{"i":36,"series":1,"stats":{"get_bytes":24896,"get_count":5,"put_bytes":23807,"put_count":5}} -{"i":37,"series":1,"stats":{"get_bytes":25817,"get_count":6,"put_bytes":24857,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":25971,"get_count":6,"put_bytes":24943,"put_count":7}} -{"i":39,"series":1,"stats":{"get_bytes":25304,"get_count":4,"put_bytes":24274,"put_count":5}} -{"i":40,"series":1,"stats":{"get_bytes":26709,"get_count":7,"put_bytes":25623,"put_count":7}} -{"i":41,"series":1,"stats":{"get_bytes":26254,"get_count":5,"put_bytes":25291,"put_count":7}} -{"i":42,"series":1,"stats":{"get_bytes":26413,"get_count":5,"put_bytes":25474,"put_count":7}} -{"i":43,"series":1,"stats":{"get_bytes":26743,"get_count":6,"put_bytes":25748,"put_count":8}} -{"i":44,"series":1,"stats":{"get_bytes":27104,"get_count":6,"put_bytes":26314,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":27521,"get_count":9,"put_bytes":26329,"put_count":8}} -{"i":46,"series":1,"stats":{"get_bytes":26616,"get_count":5,"put_bytes":25531,"put_count":5}} -{"i":47,"series":1,"stats":{"get_bytes":27299,"get_count":5,"put_bytes":26337,"put_count":7}} -{"i":48,"series":1,"stats":{"get_bytes":27613,"get_count":6,"put_bytes":26751,"put_count":9}} -{"i":49,"series":1,"stats":{"get_bytes":27793,"get_count":7,"put_bytes":26655,"put_count":7}} -{"i":50,"series":1,"stats":{"get_bytes":28336,"get_count":7,"put_bytes":27247,"put_count":7}} -{"i":51,"series":1,"stats":{"get_bytes":29297,"get_count":9,"put_bytes":28266,"put_count":10}} -{"i":52,"series":1,"stats":{"get_bytes":27964,"get_count":5,"put_bytes":27173,"put_count":9}} -{"i":53,"series":1,"stats":{"get_bytes":28648,"get_count":8,"put_bytes":27781,"put_count":11}} -{"i":54,"series":1,"stats":{"get_bytes":28050,"get_count":7,"put_bytes":27050,"put_count":8}} -{"i":55,"series":1,"stats":{"get_bytes":28030,"get_count":7,"put_bytes":27073,"put_count":9}} -{"i":56,"series":1,"stats":{"get_bytes":28586,"get_count":9,"put_bytes":27502,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":29215,"get_count":9,"put_bytes":28184,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":28920,"get_count":8,"put_bytes":28079,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":28057,"get_count":7,"put_bytes":26950,"put_count":7}} -{"i":60,"series":1,"stats":{"get_bytes":29731,"get_count":9,"put_bytes":28578,"put_count":8}} -{"i":61,"series":1,"stats":{"get_bytes":29318,"get_count":8,"put_bytes":28298,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":30382,"get_count":10,"put_bytes":29459,"put_count":12}} -{"i":63,"series":1,"stats":{"get_bytes":29847,"get_count":9,"put_bytes":28892,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":29570,"get_count":8,"put_bytes":28407,"put_count":7}} -{"i":65,"series":1,"stats":{"get_bytes":29785,"get_count":7,"put_bytes":29034,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":29093,"get_count":9,"put_bytes":28007,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":30609,"get_count":11,"put_bytes":29574,"put_count":12}} -{"i":68,"series":1,"stats":{"get_bytes":30016,"get_count":10,"put_bytes":29011,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":29685,"get_count":9,"put_bytes":28721,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":29921,"get_count":9,"put_bytes":29078,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":29713,"get_count":10,"put_bytes":28598,"put_count":10}} -{"i":72,"series":1,"stats":{"get_bytes":30516,"get_count":12,"put_bytes":29584,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":30085,"get_count":12,"put_bytes":29053,"put_count":13}} -{"i":74,"series":1,"stats":{"get_bytes":28862,"get_count":8,"put_bytes":28135,"put_count":13}} -{"i":75,"series":1,"stats":{"get_bytes":29861,"get_count":13,"put_bytes":28752,"put_count":13}} -{"i":76,"series":1,"stats":{"get_bytes":28179,"get_count":9,"put_bytes":27354,"put_count":12}} -{"i":77,"series":1,"stats":{"get_bytes":29704,"get_count":14,"put_bytes":28616,"put_count":14}} -{"i":78,"series":1,"stats":{"get_bytes":28773,"get_count":12,"put_bytes":27841,"put_count":14}} -{"i":79,"series":1,"stats":{"get_bytes":29082,"get_count":13,"put_bytes":28035,"put_count":14}} -{"i":80,"series":1,"stats":{"get_bytes":28081,"get_count":10,"put_bytes":27065,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":28099,"get_count":11,"put_bytes":27150,"put_count":13}} -{"i":82,"series":1,"stats":{"get_bytes":27933,"get_count":11,"put_bytes":26777,"put_count":10}} -{"i":83,"series":1,"stats":{"get_bytes":29068,"get_count":12,"put_bytes":28028,"put_count":13}} -{"i":84,"series":1,"stats":{"get_bytes":29270,"get_count":14,"put_bytes":28363,"put_count":16}} -{"i":85,"series":1,"stats":{"get_bytes":27636,"get_count":11,"put_bytes":26600,"put_count":12}} -{"i":86,"series":1,"stats":{"get_bytes":28466,"get_count":12,"put_bytes":27459,"put_count":13}} -{"i":87,"series":1,"stats":{"get_bytes":27673,"get_count":13,"put_bytes":26719,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":30019,"get_count":17,"put_bytes":28876,"put_count":16}} -{"i":89,"series":1,"stats":{"get_bytes":27272,"get_count":11,"put_bytes":26318,"put_count":13}} -{"i":90,"series":1,"stats":{"get_bytes":27576,"get_count":13,"put_bytes":26658,"put_count":15}} -{"i":91,"series":1,"stats":{"get_bytes":26654,"get_count":12,"put_bytes":25693,"put_count":14}} -{"i":92,"series":1,"stats":{"get_bytes":26244,"get_count":11,"put_bytes":25399,"put_count":14}} -{"i":93,"series":1,"stats":{"get_bytes":25588,"get_count":11,"put_bytes":24700,"put_count":14}} -{"i":94,"series":1,"stats":{"get_bytes":26255,"get_count":14,"put_bytes":25256,"put_count":15}} -{"i":95,"series":1,"stats":{"get_bytes":26365,"get_count":14,"put_bytes":25256,"put_count":14}} -{"i":96,"series":1,"stats":{"get_bytes":26582,"get_count":15,"put_bytes":25440,"put_count":14}} -{"i":97,"series":1,"stats":{"get_bytes":25952,"get_count":13,"put_bytes":24849,"put_count":13}} -{"i":98,"series":1,"stats":{"get_bytes":25340,"get_count":12,"put_bytes":24336,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":25400,"get_count":12,"put_bytes":24437,"put_count":14}} -{"i":0,"series":2,"stats":{"get_bytes":24567,"get_count":11,"put_bytes":23564,"put_count":12}} -{"i":1,"series":2,"stats":{"get_bytes":26538,"get_count":16,"put_bytes":25427,"put_count":16}} -{"i":2,"series":2,"stats":{"get_bytes":25530,"get_count":14,"put_bytes":24459,"put_count":14}} -{"i":3,"series":2,"stats":{"get_bytes":27062,"get_count":15,"put_bytes":25953,"put_count":15}} -{"i":4,"series":2,"stats":{"get_bytes":25571,"get_count":13,"put_bytes":24501,"put_count":13}} -{"i":5,"series":2,"stats":{"get_bytes":23770,"get_count":10,"put_bytes":22740,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":25452,"get_count":13,"put_bytes":24378,"put_count":13}} -{"i":7,"series":2,"stats":{"get_bytes":25512,"get_count":14,"put_bytes":24392,"put_count":14}} -{"i":8,"series":2,"stats":{"get_bytes":24742,"get_count":12,"put_bytes":23668,"put_count":12}} -{"i":9,"series":2,"stats":{"get_bytes":26261,"get_count":14,"put_bytes":25074,"put_count":13}} -{"i":10,"series":2,"stats":{"get_bytes":26297,"get_count":14,"put_bytes":25286,"put_count":15}} -{"i":11,"series":2,"stats":{"get_bytes":24466,"get_count":12,"put_bytes":23436,"put_count":13}} -{"i":12,"series":2,"stats":{"get_bytes":25465,"get_count":14,"put_bytes":24453,"put_count":15}} -{"i":13,"series":2,"stats":{"get_bytes":24018,"get_count":11,"put_bytes":22990,"put_count":12}} -{"i":14,"series":2,"stats":{"get_bytes":26909,"get_count":16,"put_bytes":25838,"put_count":16}} -{"i":15,"series":2,"stats":{"get_bytes":25504,"get_count":13,"put_bytes":24403,"put_count":13}} -{"i":16,"series":2,"stats":{"get_bytes":26138,"get_count":15,"put_bytes":24993,"put_count":14}} -{"i":17,"series":2,"stats":{"get_bytes":26410,"get_count":16,"put_bytes":25321,"put_count":16}} -{"i":18,"series":2,"stats":{"get_bytes":25207,"get_count":14,"put_bytes":24211,"put_count":15}} -{"i":19,"series":2,"stats":{"get_bytes":24476,"get_count":14,"put_bytes":23352,"put_count":14}} -{"i":20,"series":2,"stats":{"get_bytes":26632,"get_count":16,"put_bytes":25477,"put_count":15}} -{"i":21,"series":2,"stats":{"get_bytes":25786,"get_count":16,"put_bytes":24680,"put_count":16}} -{"i":22,"series":2,"stats":{"get_bytes":25090,"get_count":12,"put_bytes":24234,"put_count":15}} -{"i":23,"series":2,"stats":{"get_bytes":23529,"get_count":13,"put_bytes":22421,"put_count":13}} -{"i":24,"series":2,"stats":{"get_bytes":24824,"get_count":17,"put_bytes":23685,"put_count":16}} -{"i":25,"series":2,"stats":{"get_bytes":25012,"get_count":16,"put_bytes":23911,"put_count":16}} -{"i":26,"series":2,"stats":{"get_bytes":23668,"get_count":12,"put_bytes":22740,"put_count":14}} -{"i":27,"series":2,"stats":{"get_bytes":24194,"get_count":15,"put_bytes":23106,"put_count":15}} -{"i":28,"series":2,"stats":{"get_bytes":24674,"get_count":15,"put_bytes":23527,"put_count":14}} -{"i":29,"series":2,"stats":{"get_bytes":23989,"get_count":15,"put_bytes":22949,"put_count":16}} -{"i":30,"series":2,"stats":{"get_bytes":24558,"get_count":15,"put_bytes":23482,"put_count":15}} -{"i":31,"series":2,"stats":{"get_bytes":23109,"get_count":13,"put_bytes":22154,"put_count":15}} -{"i":32,"series":2,"stats":{"get_bytes":23134,"get_count":14,"put_bytes":22129,"put_count":15}} -{"i":33,"series":2,"stats":{"get_bytes":22847,"get_count":15,"put_bytes":21738,"put_count":15}} -{"i":34,"series":2,"stats":{"get_bytes":23609,"get_count":15,"put_bytes":22535,"put_count":15}} -{"i":35,"series":2,"stats":{"get_bytes":24581,"get_count":16,"put_bytes":23470,"put_count":16}} -{"i":36,"series":2,"stats":{"get_bytes":23103,"get_count":15,"put_bytes":22096,"put_count":16}} -{"i":37,"series":2,"stats":{"get_bytes":23393,"get_count":16,"put_bytes":22228,"put_count":15}} -{"i":38,"series":2,"stats":{"get_bytes":23134,"get_count":16,"put_bytes":22052,"put_count":16}} -{"i":39,"series":2,"stats":{"get_bytes":24151,"get_count":16,"put_bytes":23052,"put_count":16}} -{"i":40,"series":2,"stats":{"get_bytes":21612,"get_count":14,"put_bytes":20694,"put_count":16}} -{"i":41,"series":2,"stats":{"get_bytes":20186,"get_count":11,"put_bytes":19220,"put_count":13}} -{"i":42,"series":2,"stats":{"get_bytes":21986,"get_count":17,"put_bytes":20840,"put_count":16}} -{"i":43,"series":2,"stats":{"get_bytes":21626,"get_count":14,"put_bytes":20588,"put_count":15}} -{"i":44,"series":2,"stats":{"get_bytes":21285,"get_count":15,"put_bytes":20289,"put_count":16}} -{"i":45,"series":2,"stats":{"get_bytes":22814,"get_count":17,"put_bytes":21627,"put_count":16}} -{"i":46,"series":2,"stats":{"get_bytes":20881,"get_count":16,"put_bytes":19805,"put_count":16}} -{"i":47,"series":2,"stats":{"get_bytes":20388,"get_count":17,"put_bytes":19211,"put_count":16}} -{"i":48,"series":2,"stats":{"get_bytes":22832,"get_count":17,"put_bytes":21681,"put_count":16}} -{"i":49,"series":2,"stats":{"get_bytes":22388,"get_count":16,"put_bytes":21201,"put_count":15}} -{"i":50,"series":2,"stats":{"get_bytes":22525,"get_count":17,"put_bytes":21379,"put_count":16}} -{"i":51,"series":2,"stats":{"get_bytes":22375,"get_count":16,"put_bytes":21196,"put_count":15}} -{"i":52,"series":2,"stats":{"get_bytes":22501,"get_count":17,"put_bytes":21349,"put_count":16}} -{"i":53,"series":2,"stats":{"get_bytes":22512,"get_count":17,"put_bytes":21334,"put_count":16}} -{"i":54,"series":2,"stats":{"get_bytes":22436,"get_count":16,"put_bytes":21297,"put_count":15}} -{"i":55,"series":2,"stats":{"get_bytes":22586,"get_count":17,"put_bytes":21399,"put_count":16}} -{"i":56,"series":2,"stats":{"get_bytes":22228,"get_count":15,"put_bytes":21158,"put_count":15}} -{"i":57,"series":2,"stats":{"get_bytes":21336,"get_count":14,"put_bytes":20227,"put_count":14}} -{"i":58,"series":2,"stats":{"get_bytes":22131,"get_count":15,"put_bytes":21112,"put_count":16}} -{"i":59,"series":2,"stats":{"get_bytes":21749,"get_count":16,"put_bytes":20561,"put_count":15}} -{"i":60,"series":2,"stats":{"get_bytes":22439,"get_count":17,"put_bytes":21303,"put_count":16}} -{"i":61,"series":2,"stats":{"get_bytes":22757,"get_count":17,"put_bytes":21574,"put_count":16}} -{"i":62,"series":2,"stats":{"get_bytes":22301,"get_count":16,"put_bytes":21149,"put_count":15}} -{"i":63,"series":2,"stats":{"get_bytes":22402,"get_count":17,"put_bytes":21217,"put_count":16}} -{"i":64,"series":2,"stats":{"get_bytes":21213,"get_count":16,"put_bytes":20142,"put_count":16}} -{"i":65,"series":2,"stats":{"get_bytes":22162,"get_count":17,"put_bytes":20977,"put_count":16}} -{"i":66,"series":2,"stats":{"get_bytes":22283,"get_count":16,"put_bytes":21210,"put_count":16}} -{"i":67,"series":2,"stats":{"get_bytes":20485,"get_count":16,"put_bytes":19301,"put_count":15}} -{"i":68,"series":2,"stats":{"get_bytes":22289,"get_count":17,"put_bytes":21142,"put_count":16}} -{"i":69,"series":2,"stats":{"get_bytes":22779,"get_count":16,"put_bytes":21670,"put_count":16}} -{"i":70,"series":2,"stats":{"get_bytes":21800,"get_count":17,"put_bytes":20650,"put_count":16}} -{"i":71,"series":2,"stats":{"get_bytes":22659,"get_count":17,"put_bytes":21471,"put_count":16}} -{"i":72,"series":2,"stats":{"get_bytes":21823,"get_count":16,"put_bytes":20768,"put_count":16}} -{"i":73,"series":2,"stats":{"get_bytes":22286,"get_count":17,"put_bytes":21109,"put_count":16}} -{"i":74,"series":2,"stats":{"get_bytes":21193,"get_count":16,"put_bytes":20122,"put_count":16}} -{"i":75,"series":2,"stats":{"get_bytes":21506,"get_count":16,"put_bytes":20317,"put_count":15}} -{"i":76,"series":2,"stats":{"get_bytes":21895,"get_count":17,"put_bytes":20743,"put_count":16}} -{"i":77,"series":2,"stats":{"get_bytes":21845,"get_count":16,"put_bytes":20659,"put_count":15}} -{"i":78,"series":2,"stats":{"get_bytes":21527,"get_count":16,"put_bytes":20382,"put_count":15}} -{"i":79,"series":2,"stats":{"get_bytes":22471,"get_count":17,"put_bytes":21297,"put_count":16}} -{"i":80,"series":2,"stats":{"get_bytes":21700,"get_count":17,"put_bytes":20553,"put_count":16}} -{"i":81,"series":2,"stats":{"get_bytes":20343,"get_count":16,"put_bytes":19163,"put_count":15}} -{"i":82,"series":2,"stats":{"get_bytes":21817,"get_count":17,"put_bytes":20669,"put_count":16}} -{"i":83,"series":2,"stats":{"get_bytes":22620,"get_count":17,"put_bytes":21437,"put_count":16}} -{"i":84,"series":2,"stats":{"get_bytes":20110,"get_count":14,"put_bytes":19035,"put_count":14}} -{"i":85,"series":2,"stats":{"get_bytes":22970,"get_count":17,"put_bytes":21790,"put_count":16}} -{"i":86,"series":2,"stats":{"get_bytes":23108,"get_count":16,"put_bytes":21975,"put_count":15}} -{"i":87,"series":2,"stats":{"get_bytes":23321,"get_count":17,"put_bytes":22141,"put_count":16}} -{"i":88,"series":2,"stats":{"get_bytes":22543,"get_count":17,"put_bytes":21393,"put_count":16}} -{"i":89,"series":2,"stats":{"get_bytes":23930,"get_count":17,"put_bytes":22750,"put_count":16}} -{"i":90,"series":2,"stats":{"get_bytes":23580,"get_count":16,"put_bytes":22426,"put_count":15}} -{"i":91,"series":2,"stats":{"get_bytes":23656,"get_count":17,"put_bytes":22468,"put_count":16}} -{"i":92,"series":2,"stats":{"get_bytes":22874,"get_count":15,"put_bytes":21801,"put_count":15}} -{"i":93,"series":2,"stats":{"get_bytes":22733,"get_count":16,"put_bytes":21557,"put_count":15}} -{"i":94,"series":2,"stats":{"get_bytes":22522,"get_count":15,"put_bytes":21436,"put_count":15}} -{"i":95,"series":2,"stats":{"get_bytes":22862,"get_count":16,"put_bytes":21674,"put_count":15}} -{"i":96,"series":2,"stats":{"get_bytes":20476,"get_count":15,"put_bytes":19333,"put_count":14}} -{"i":97,"series":2,"stats":{"get_bytes":22827,"get_count":17,"put_bytes":21645,"put_count":16}} -{"i":98,"series":2,"stats":{"get_bytes":22734,"get_count":16,"put_bytes":21583,"put_count":15}} -{"i":99,"series":2,"stats":{"get_bytes":23808,"get_count":17,"put_bytes":22629,"put_count":16}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3535,"put_count":11}} +{"i":1,"series":1,"stats":{"get_bytes":2757,"get_count":8,"put_bytes":1654,"put_count":8}} +{"i":2,"series":1,"stats":{"get_bytes":3662,"get_count":9,"put_bytes":2521,"put_count":8}} +{"i":3,"series":1,"stats":{"get_bytes":4529,"get_count":9,"put_bytes":3355,"put_count":8}} +{"i":4,"series":1,"stats":{"get_bytes":5363,"get_count":9,"put_bytes":4226,"put_count":8}} +{"i":5,"series":1,"stats":{"get_bytes":6234,"get_count":9,"put_bytes":5059,"put_count":8}} +{"i":6,"series":1,"stats":{"get_bytes":2707,"get_count":8,"put_bytes":1632,"put_count":8}} +{"i":7,"series":1,"stats":{"get_bytes":3640,"get_count":9,"put_bytes":2466,"put_count":8}} +{"i":8,"series":1,"stats":{"get_bytes":4474,"get_count":9,"put_bytes":3336,"put_count":8}} +{"i":9,"series":1,"stats":{"get_bytes":5344,"get_count":9,"put_bytes":4171,"put_count":8}} +{"i":10,"series":1,"stats":{"get_bytes":6179,"get_count":9,"put_bytes":5036,"put_count":8}} +{"i":11,"series":1,"stats":{"get_bytes":7044,"get_count":9,"put_bytes":5937,"put_count":9}} +{"i":12,"series":1,"stats":{"get_bytes":3585,"get_count":9,"put_bytes":2447,"put_count":8}} +{"i":13,"series":1,"stats":{"get_bytes":4455,"get_count":9,"put_bytes":3281,"put_count":8}} +{"i":14,"series":1,"stats":{"get_bytes":5289,"get_count":9,"put_bytes":4148,"put_count":8}} +{"i":15,"series":1,"stats":{"get_bytes":6156,"get_count":9,"put_bytes":4981,"put_count":8}} +{"i":16,"series":1,"stats":{"get_bytes":6989,"get_count":9,"put_bytes":5918,"put_count":9}} +{"i":17,"series":1,"stats":{"get_bytes":3566,"get_count":9,"put_bytes":2392,"put_count":8}} +{"i":18,"series":1,"stats":{"get_bytes":4400,"get_count":9,"put_bytes":3258,"put_count":8}} +{"i":19,"series":1,"stats":{"get_bytes":5266,"get_count":9,"put_bytes":4092,"put_count":8}} +{"i":20,"series":1,"stats":{"get_bytes":6100,"get_count":9,"put_bytes":4962,"put_count":8}} +{"i":21,"series":1,"stats":{"get_bytes":6970,"get_count":9,"put_bytes":5864,"put_count":9}} +{"i":22,"series":1,"stats":{"get_bytes":3512,"get_count":9,"put_bytes":2370,"put_count":8}} +{"i":23,"series":1,"stats":{"get_bytes":4378,"get_count":9,"put_bytes":3204,"put_count":8}} +{"i":24,"series":1,"stats":{"get_bytes":5212,"get_count":9,"put_bytes":4074,"put_count":8}} +{"i":25,"series":1,"stats":{"get_bytes":6082,"get_count":9,"put_bytes":4908,"put_count":8}} +{"i":26,"series":1,"stats":{"get_bytes":6916,"get_count":9,"put_bytes":5841,"put_count":9}} +{"i":27,"series":1,"stats":{"get_bytes":3489,"get_count":9,"put_bytes":2315,"put_count":8}} +{"i":28,"series":1,"stats":{"get_bytes":4323,"get_count":9,"put_bytes":3185,"put_count":8}} +{"i":29,"series":1,"stats":{"get_bytes":5193,"get_count":9,"put_bytes":4018,"put_count":8}} +{"i":30,"series":1,"stats":{"get_bytes":6026,"get_count":9,"put_bytes":4885,"put_count":8}} +{"i":31,"series":1,"stats":{"get_bytes":6893,"get_count":9,"put_bytes":5786,"put_count":9}} +{"i":32,"series":1,"stats":{"get_bytes":3434,"get_count":9,"put_bytes":2296,"put_count":8}} +{"i":33,"series":1,"stats":{"get_bytes":4304,"get_count":9,"put_bytes":3129,"put_count":8}} +{"i":34,"series":1,"stats":{"get_bytes":5137,"get_count":9,"put_bytes":3995,"put_count":8}} +{"i":35,"series":1,"stats":{"get_bytes":6003,"get_count":9,"put_bytes":4830,"put_count":8}} +{"i":36,"series":1,"stats":{"get_bytes":6838,"get_count":9,"put_bytes":5767,"put_count":9}} +{"i":37,"series":1,"stats":{"get_bytes":3415,"get_count":9,"put_bytes":2241,"put_count":8}} +{"i":38,"series":1,"stats":{"get_bytes":4249,"get_count":9,"put_bytes":3106,"put_count":8}} +{"i":39,"series":1,"stats":{"get_bytes":5114,"get_count":9,"put_bytes":3940,"put_count":8}} +{"i":40,"series":1,"stats":{"get_bytes":5948,"get_count":9,"put_bytes":4811,"put_count":8}} +{"i":41,"series":1,"stats":{"get_bytes":6819,"get_count":9,"put_bytes":5640,"put_count":8}} +{"i":42,"series":1,"stats":{"get_bytes":3288,"get_count":8,"put_bytes":2217,"put_count":8}} +{"i":43,"series":1,"stats":{"get_bytes":4225,"get_count":9,"put_bytes":3051,"put_count":8}} +{"i":44,"series":1,"stats":{"get_bytes":5059,"get_count":9,"put_bytes":3921,"put_count":8}} +{"i":45,"series":1,"stats":{"get_bytes":5929,"get_count":9,"put_bytes":4756,"put_count":8}} +{"i":46,"series":1,"stats":{"get_bytes":6764,"get_count":9,"put_bytes":5621,"put_count":8}} +{"i":47,"series":1,"stats":{"get_bytes":3269,"get_count":8,"put_bytes":2162,"put_count":8}} +{"i":48,"series":1,"stats":{"get_bytes":4170,"get_count":9,"put_bytes":3032,"put_count":8}} +{"i":49,"series":1,"stats":{"get_bytes":5040,"get_count":9,"put_bytes":3866,"put_count":8}} +{"i":50,"series":1,"stats":{"get_bytes":5874,"get_count":9,"put_bytes":4733,"put_count":8}} +{"i":51,"series":1,"stats":{"get_bytes":6741,"get_count":9,"put_bytes":5566,"put_count":8}} +{"i":52,"series":1,"stats":{"get_bytes":3214,"get_count":8,"put_bytes":2143,"put_count":8}} +{"i":53,"series":1,"stats":{"get_bytes":4151,"get_count":9,"put_bytes":2977,"put_count":8}} +{"i":54,"series":1,"stats":{"get_bytes":4985,"get_count":9,"put_bytes":3843,"put_count":8}} +{"i":55,"series":1,"stats":{"get_bytes":5851,"get_count":9,"put_bytes":4678,"put_count":8}} +{"i":56,"series":1,"stats":{"get_bytes":6686,"get_count":9,"put_bytes":5547,"put_count":8}} +{"i":57,"series":1,"stats":{"get_bytes":7555,"get_count":9,"put_bytes":6448,"put_count":9}} +{"i":58,"series":1,"stats":{"get_bytes":4096,"get_count":9,"put_bytes":2954,"put_count":8}} +{"i":59,"series":1,"stats":{"get_bytes":4962,"get_count":9,"put_bytes":3788,"put_count":8}} +{"i":60,"series":1,"stats":{"get_bytes":5796,"get_count":9,"put_bytes":4659,"put_count":8}} +{"i":61,"series":1,"stats":{"get_bytes":6667,"get_count":9,"put_bytes":5492,"put_count":8}} +{"i":62,"series":1,"stats":{"get_bytes":7500,"get_count":9,"put_bytes":6494,"put_count":10}} +{"i":63,"series":1,"stats":{"get_bytes":3303,"get_count":9,"put_bytes":2129,"put_count":8}} +{"i":64,"series":1,"stats":{"get_bytes":4137,"get_count":9,"put_bytes":2999,"put_count":8}} +{"i":65,"series":1,"stats":{"get_bytes":5007,"get_count":9,"put_bytes":3833,"put_count":8}} +{"i":66,"series":1,"stats":{"get_bytes":5841,"get_count":9,"put_bytes":4699,"put_count":8}} +{"i":67,"series":1,"stats":{"get_bytes":6707,"get_count":9,"put_bytes":5667,"put_count":10}} +{"i":68,"series":1,"stats":{"get_bytes":3315,"get_count":10,"put_bytes":2177,"put_count":9}} +{"i":69,"series":1,"stats":{"get_bytes":4185,"get_count":10,"put_bytes":3011,"put_count":9}} +{"i":70,"series":1,"stats":{"get_bytes":5019,"get_count":10,"put_bytes":3876,"put_count":9}} +{"i":71,"series":1,"stats":{"get_bytes":5884,"get_count":10,"put_bytes":4711,"put_count":9}} +{"i":72,"series":1,"stats":{"get_bytes":6719,"get_count":10,"put_bytes":5648,"put_count":10}} +{"i":73,"series":1,"stats":{"get_bytes":3296,"get_count":10,"put_bytes":2122,"put_count":9}} +{"i":74,"series":1,"stats":{"get_bytes":4130,"get_count":10,"put_bytes":2987,"put_count":9}} +{"i":75,"series":1,"stats":{"get_bytes":4995,"get_count":10,"put_bytes":3821,"put_count":9}} +{"i":76,"series":1,"stats":{"get_bytes":5829,"get_count":10,"put_bytes":4692,"put_count":9}} +{"i":77,"series":1,"stats":{"get_bytes":6700,"get_count":10,"put_bytes":5593,"put_count":10}} +{"i":78,"series":1,"stats":{"get_bytes":3241,"get_count":10,"put_bytes":2099,"put_count":9}} +{"i":79,"series":1,"stats":{"get_bytes":4107,"get_count":10,"put_bytes":2932,"put_count":9}} +{"i":80,"series":1,"stats":{"get_bytes":4940,"get_count":10,"put_bytes":3802,"put_count":9}} +{"i":81,"series":1,"stats":{"get_bytes":5810,"get_count":10,"put_bytes":4637,"put_count":9}} +{"i":82,"series":1,"stats":{"get_bytes":6645,"get_count":10,"put_bytes":5570,"put_count":10}} +{"i":83,"series":1,"stats":{"get_bytes":3218,"get_count":10,"put_bytes":2043,"put_count":9}} +{"i":84,"series":1,"stats":{"get_bytes":4051,"get_count":10,"put_bytes":2913,"put_count":9}} +{"i":85,"series":1,"stats":{"get_bytes":4921,"get_count":10,"put_bytes":3747,"put_count":9}} +{"i":86,"series":1,"stats":{"get_bytes":5755,"get_count":10,"put_bytes":4614,"put_count":9}} +{"i":87,"series":1,"stats":{"get_bytes":6622,"get_count":10,"put_bytes":5447,"put_count":9}} +{"i":88,"series":1,"stats":{"get_bytes":3095,"get_count":9,"put_bytes":2024,"put_count":9}} +{"i":89,"series":1,"stats":{"get_bytes":4032,"get_count":10,"put_bytes":2858,"put_count":9}} +{"i":90,"series":1,"stats":{"get_bytes":4866,"get_count":10,"put_bytes":3724,"put_count":9}} +{"i":91,"series":1,"stats":{"get_bytes":5732,"get_count":10,"put_bytes":4559,"put_count":9}} +{"i":92,"series":1,"stats":{"get_bytes":6567,"get_count":10,"put_bytes":5428,"put_count":9}} +{"i":93,"series":1,"stats":{"get_bytes":3076,"get_count":9,"put_bytes":1969,"put_count":9}} +{"i":94,"series":1,"stats":{"get_bytes":3977,"get_count":10,"put_bytes":2835,"put_count":9}} +{"i":95,"series":1,"stats":{"get_bytes":4843,"get_count":10,"put_bytes":3669,"put_count":9}} +{"i":96,"series":1,"stats":{"get_bytes":5677,"get_count":10,"put_bytes":4540,"put_count":9}} +{"i":97,"series":1,"stats":{"get_bytes":6548,"get_count":10,"put_bytes":5373,"put_count":9}} +{"i":98,"series":1,"stats":{"get_bytes":7381,"get_count":10,"put_bytes":6306,"put_count":10}} +{"i":99,"series":1,"stats":{"get_bytes":3954,"get_count":10,"put_bytes":2780,"put_count":9}} +{"i":0,"series":2,"stats":{"get_bytes":2312,"get_count":5,"put_bytes":1443,"put_count":7}} +{"i":1,"series":2,"stats":{"get_bytes":3451,"get_count":8,"put_bytes":2277,"put_count":7}} +{"i":2,"series":2,"stats":{"get_bytes":4285,"get_count":8,"put_bytes":3144,"put_count":7}} +{"i":3,"series":2,"stats":{"get_bytes":5152,"get_count":8,"put_bytes":3978,"put_count":7}} +{"i":4,"series":2,"stats":{"get_bytes":5986,"get_count":8,"put_bytes":1574,"put_count":8}} +{"i":5,"series":2,"stats":{"get_bytes":3582,"get_count":9,"put_bytes":2408,"put_count":8}} +{"i":6,"series":2,"stats":{"get_bytes":4416,"get_count":9,"put_bytes":3274,"put_count":8}} +{"i":7,"series":2,"stats":{"get_bytes":5282,"get_count":9,"put_bytes":4109,"put_count":8}} +{"i":8,"series":2,"stats":{"get_bytes":6117,"get_count":9,"put_bytes":4978,"put_count":8}} +{"i":9,"series":2,"stats":{"get_bytes":6986,"get_count":9,"put_bytes":5879,"put_count":9}} +{"i":10,"series":2,"stats":{"get_bytes":3527,"get_count":9,"put_bytes":2385,"put_count":8}} +{"i":11,"series":2,"stats":{"get_bytes":4393,"get_count":9,"put_bytes":3219,"put_count":8}} +{"i":12,"series":2,"stats":{"get_bytes":5227,"get_count":9,"put_bytes":4090,"put_count":8}} +{"i":13,"series":2,"stats":{"get_bytes":6098,"get_count":9,"put_bytes":4923,"put_count":8}} +{"i":14,"series":2,"stats":{"get_bytes":6931,"get_count":9,"put_bytes":5856,"put_count":9}} +{"i":15,"series":2,"stats":{"get_bytes":3504,"get_count":9,"put_bytes":2330,"put_count":8}} +{"i":16,"series":2,"stats":{"get_bytes":4338,"get_count":9,"put_bytes":3200,"put_count":8}} +{"i":17,"series":2,"stats":{"get_bytes":5208,"get_count":9,"put_bytes":4034,"put_count":8}} +{"i":18,"series":2,"stats":{"get_bytes":6042,"get_count":9,"put_bytes":4900,"put_count":8}} +{"i":19,"series":2,"stats":{"get_bytes":6908,"get_count":9,"put_bytes":5801,"put_count":9}} +{"i":20,"series":2,"stats":{"get_bytes":3449,"get_count":9,"put_bytes":2311,"put_count":8}} +{"i":21,"series":2,"stats":{"get_bytes":4319,"get_count":9,"put_bytes":3145,"put_count":8}} +{"i":22,"series":2,"stats":{"get_bytes":5153,"get_count":9,"put_bytes":4010,"put_count":8}} +{"i":23,"series":2,"stats":{"get_bytes":6018,"get_count":9,"put_bytes":4845,"put_count":8}} +{"i":24,"series":2,"stats":{"get_bytes":6853,"get_count":9,"put_bytes":5783,"put_count":9}} +{"i":25,"series":2,"stats":{"get_bytes":3431,"get_count":9,"put_bytes":2257,"put_count":8}} +{"i":26,"series":2,"stats":{"get_bytes":4265,"get_count":9,"put_bytes":3122,"put_count":8}} +{"i":27,"series":2,"stats":{"get_bytes":5130,"get_count":9,"put_bytes":3956,"put_count":8}} +{"i":28,"series":2,"stats":{"get_bytes":5964,"get_count":9,"put_bytes":4827,"put_count":8}} +{"i":29,"series":2,"stats":{"get_bytes":6835,"get_count":9,"put_bytes":5728,"put_count":9}} +{"i":30,"series":2,"stats":{"get_bytes":3376,"get_count":9,"put_bytes":2234,"put_count":8}} +{"i":31,"series":2,"stats":{"get_bytes":4242,"get_count":9,"put_bytes":3067,"put_count":8}} +{"i":32,"series":2,"stats":{"get_bytes":5075,"get_count":9,"put_bytes":3937,"put_count":8}} +{"i":33,"series":2,"stats":{"get_bytes":5945,"get_count":9,"put_bytes":4772,"put_count":8}} +{"i":34,"series":2,"stats":{"get_bytes":6780,"get_count":9,"put_bytes":5705,"put_count":9}} +{"i":35,"series":2,"stats":{"get_bytes":3353,"get_count":9,"put_bytes":2178,"put_count":8}} +{"i":36,"series":2,"stats":{"get_bytes":4186,"get_count":9,"put_bytes":3048,"put_count":8}} +{"i":37,"series":2,"stats":{"get_bytes":5056,"get_count":9,"put_bytes":3882,"put_count":8}} +{"i":38,"series":2,"stats":{"get_bytes":5890,"get_count":9,"put_bytes":4749,"put_count":8}} +{"i":39,"series":2,"stats":{"get_bytes":6757,"get_count":9,"put_bytes":5582,"put_count":8}} +{"i":40,"series":2,"stats":{"get_bytes":3230,"get_count":8,"put_bytes":2159,"put_count":8}} +{"i":41,"series":2,"stats":{"get_bytes":4167,"get_count":9,"put_bytes":2993,"put_count":8}} +{"i":42,"series":2,"stats":{"get_bytes":5001,"get_count":9,"put_bytes":3859,"put_count":8}} +{"i":43,"series":2,"stats":{"get_bytes":5867,"get_count":9,"put_bytes":4694,"put_count":8}} +{"i":44,"series":2,"stats":{"get_bytes":6702,"get_count":9,"put_bytes":5563,"put_count":8}} +{"i":45,"series":2,"stats":{"get_bytes":3211,"get_count":8,"put_bytes":2104,"put_count":8}} +{"i":46,"series":2,"stats":{"get_bytes":4112,"get_count":9,"put_bytes":2970,"put_count":8}} +{"i":47,"series":2,"stats":{"get_bytes":4978,"get_count":9,"put_bytes":3804,"put_count":8}} +{"i":48,"series":2,"stats":{"get_bytes":5812,"get_count":9,"put_bytes":4675,"put_count":8}} +{"i":49,"series":2,"stats":{"get_bytes":6683,"get_count":9,"put_bytes":5508,"put_count":8}} +{"i":50,"series":2,"stats":{"get_bytes":7516,"get_count":9,"put_bytes":6441,"put_count":9}} +{"i":51,"series":2,"stats":{"get_bytes":4089,"get_count":9,"put_bytes":2915,"put_count":8}} +{"i":52,"series":2,"stats":{"get_bytes":4923,"get_count":9,"put_bytes":3785,"put_count":8}} +{"i":53,"series":2,"stats":{"get_bytes":5793,"get_count":9,"put_bytes":4620,"put_count":8}} +{"i":54,"series":2,"stats":{"get_bytes":6628,"get_count":9,"put_bytes":5485,"put_count":8}} +{"i":55,"series":2,"stats":{"get_bytes":7493,"get_count":9,"put_bytes":6386,"put_count":9}} +{"i":56,"series":2,"stats":{"get_bytes":4034,"get_count":9,"put_bytes":2896,"put_count":8}} +{"i":57,"series":2,"stats":{"get_bytes":4904,"get_count":9,"put_bytes":3730,"put_count":8}} +{"i":58,"series":2,"stats":{"get_bytes":5738,"get_count":9,"put_bytes":4596,"put_count":8}} +{"i":59,"series":2,"stats":{"get_bytes":6604,"get_count":9,"put_bytes":5430,"put_count":8}} +{"i":60,"series":2,"stats":{"get_bytes":7438,"get_count":9,"put_bytes":6367,"put_count":9}} +{"i":61,"series":2,"stats":{"get_bytes":4015,"get_count":9,"put_bytes":2841,"put_count":8}} +{"i":62,"series":2,"stats":{"get_bytes":4849,"get_count":9,"put_bytes":3707,"put_count":8}} +{"i":63,"series":2,"stats":{"get_bytes":5715,"get_count":9,"put_bytes":4540,"put_count":8}} +{"i":64,"series":2,"stats":{"get_bytes":6548,"get_count":9,"put_bytes":5411,"put_count":8}} +{"i":65,"series":2,"stats":{"get_bytes":7419,"get_count":9,"put_bytes":6313,"put_count":9}} +{"i":66,"series":2,"stats":{"get_bytes":3961,"get_count":9,"put_bytes":2819,"put_count":8}} +{"i":67,"series":2,"stats":{"get_bytes":4827,"get_count":9,"put_bytes":3652,"put_count":8}} +{"i":68,"series":2,"stats":{"get_bytes":5660,"get_count":9,"put_bytes":4522,"put_count":8}} +{"i":69,"series":2,"stats":{"get_bytes":6530,"get_count":9,"put_bytes":5357,"put_count":8}} +{"i":70,"series":2,"stats":{"get_bytes":7365,"get_count":9,"put_bytes":6290,"put_count":9}} +{"i":71,"series":2,"stats":{"get_bytes":3938,"get_count":9,"put_bytes":2764,"put_count":8}} +{"i":72,"series":2,"stats":{"get_bytes":4772,"get_count":9,"put_bytes":3633,"put_count":8}} +{"i":73,"series":2,"stats":{"get_bytes":5641,"get_count":9,"put_bytes":4467,"put_count":8}} +{"i":74,"series":2,"stats":{"get_bytes":6475,"get_count":9,"put_bytes":5334,"put_count":8}} +{"i":75,"series":2,"stats":{"get_bytes":7342,"get_count":9,"put_bytes":6235,"put_count":9}} +{"i":76,"series":2,"stats":{"get_bytes":3883,"get_count":9,"put_bytes":2745,"put_count":8}} +{"i":77,"series":2,"stats":{"get_bytes":4753,"get_count":9,"put_bytes":3578,"put_count":8}} +{"i":78,"series":2,"stats":{"get_bytes":5586,"get_count":9,"put_bytes":4444,"put_count":8}} +{"i":79,"series":2,"stats":{"get_bytes":6452,"get_count":9,"put_bytes":5279,"put_count":8}} +{"i":80,"series":2,"stats":{"get_bytes":7287,"get_count":9,"put_bytes":6144,"put_count":8}} +{"i":81,"series":2,"stats":{"get_bytes":3792,"get_count":8,"put_bytes":2689,"put_count":8}} +{"i":82,"series":2,"stats":{"get_bytes":4697,"get_count":9,"put_bytes":3555,"put_count":8}} +{"i":83,"series":2,"stats":{"get_bytes":5563,"get_count":9,"put_bytes":4389,"put_count":8}} +{"i":84,"series":2,"stats":{"get_bytes":6397,"get_count":9,"put_bytes":5260,"put_count":8}} +{"i":85,"series":2,"stats":{"get_bytes":7268,"get_count":9,"put_bytes":6093,"put_count":8}} +{"i":86,"series":2,"stats":{"get_bytes":3741,"get_count":8,"put_bytes":2666,"put_count":8}} +{"i":87,"series":2,"stats":{"get_bytes":4674,"get_count":9,"put_bytes":3500,"put_count":8}} +{"i":88,"series":2,"stats":{"get_bytes":5508,"get_count":9,"put_bytes":4370,"put_count":8}} +{"i":89,"series":2,"stats":{"get_bytes":6378,"get_count":9,"put_bytes":5205,"put_count":8}} +{"i":90,"series":2,"stats":{"get_bytes":7213,"get_count":9,"put_bytes":6070,"put_count":8}} +{"i":91,"series":2,"stats":{"get_bytes":8078,"get_count":9,"put_bytes":6971,"put_count":9}} +{"i":92,"series":2,"stats":{"get_bytes":4619,"get_count":9,"put_bytes":3481,"put_count":8}} +{"i":93,"series":2,"stats":{"get_bytes":5489,"get_count":9,"put_bytes":4315,"put_count":8}} +{"i":94,"series":2,"stats":{"get_bytes":6323,"get_count":9,"put_bytes":5182,"put_count":8}} +{"i":95,"series":2,"stats":{"get_bytes":7190,"get_count":9,"put_bytes":6015,"put_count":8}} +{"i":96,"series":2,"stats":{"get_bytes":8023,"get_count":9,"put_bytes":6952,"put_count":9}} +{"i":97,"series":2,"stats":{"get_bytes":4600,"get_count":9,"put_bytes":3426,"put_count":8}} +{"i":98,"series":2,"stats":{"get_bytes":5434,"get_count":9,"put_bytes":4292,"put_count":8}} +{"i":99,"series":2,"stats":{"get_bytes":6300,"get_count":9,"put_bytes":5126,"put_count":8}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png index 93033725a15d865234ab2b95fd64cda95b72b9c2..39a9c397d70691ca83f59d86d3f717004ccbc9fc 100644 GIT binary patch literal 25461 zcmcG$c{r6{|3A8;GVC(jG9+XO+nC5KL&-dZ+w3OuEMrB~-WoO{Y*Pp!Y;}`)?l#3H zvy3T1#!x7d-|h1}-}5}@{LUZ0>pItQNm+YM@ArDI_xM^pHZr(&nwEnWg+iUauB~Z; zLLG-tC`uk`N~Gnuil-zBb?lmv-VLq8!$YLy#fuk5f3=`ck%uj)L)7|uiyG?vd(;*8 zFNZBWs7SSz$U{koNCFC_7Ku8HJiK=}oG&7R3|0HwkBsBs;JA44qQ1Vqi;K&>d-u}P z(%!y(+tSj~)6+9QKaVW2AuS!h9$z}Puwl0G`S5Uk`u<_nyEp6WhaXYMYN(sY&wG3X zK5>G@Kfe>$+5M2MpisRJKdhjRws?s8FmT9{czDlfBQo+(3$+&+utdDQu@KW(?O79z z-rk;i5y^9yk6ORKo`f2jelZLjR>p_R+)ECk;t8N{HBu(hQXQVkz`h?nl6r8w=YDs~pOG@jm1F>#z?PD zL6t0{IsLs?w<~hF)mIwxJgCu;z5^dkMN2u#pa1^8Wg?gEyiK_9@Z(BTDwf}#`x!Sc zr-zxPdG5+$P4ReEY|cTd=_N<&3p!=Tzppy8jdJi?$F9|dZ*`TQMn%&12OW&*lqjUN z#N10s;ovQO`|~`jnb}V1@Ou%`6?+Yb=2H0%)?03h#38x>ON=?V?U?dhOdqaI)dzaN zbqN=Ik`23C?nR0EEP5*7Rk450M59byv8}VDpvIsK0V=>1Wq%~O_x~L~GZ7Qak^2$9 z{oqd7%6)r<>B%5U%uV})F$q-#4_jp=%j#pO4c6#%)!*P-oXmrY52IWqL@)PVDB7mj z_G8kBiciEShc^neM9)6D zH~1pcM=ro&t(8OLq3gk`$2rD}ZcGO235#4G@zaZa8oGwK%AY;t?FV^PYl%Bn|5}v& zr?=eYn<=1}L|41*dU@7CiKu+}3(lT_9#@)Pn1e~&xT@6s^EU+&2oz^j6$y43|g_YW28enh`m_mX1UYTvu}{Q*?uLg^F+h4LRJ|J-|Z!#F9|-4lGye%BU-(i||K=EvSB z*6x@d6WeX){6148hC=1KrO#uoAwBhW%SG{D)`O!br;ed0o0`pbR|`XPfF8!iR=rw9?x{(hDS|I$%P**7<{6=JD<@a^!V zv#zcGyK{aHB==W0%7)9Cq5BYuje!`}tOh6zs}HyOyp*Lmfoh30AL!@*nn!w{xxPA$ zA7@KFIO$^kLU@z0Cv7B0Dee@xCaOSfnF_$+4R!&JTFUv_(alfuwysuVi5BxUxh<=5 zl5Z(^vA;X{u($Fl0+)-uSBiz`xq3?iY>XXtI8!a7-U)qIKlfHi_th%gWA5Q7RVNt#kg~|I6y?Qc9Rx)$0XCh9U9Zu7A()>m9}08MVrmaqjSC z-CG{U)oVPZ?||v}C)&5&$yLi8T8{-u1yw|${i#voy-a0sd+uG+uGdN|srF&?7veQ9 z3K^S-*;d+{dY2F2%qF$?XR6``vp<|siuv;tE7G}-?unrEWiqeytjwhLbTb<{m`XGc zk>2<0yw31)ZQ_f2wTva3Rxg{6s#JUT;rrZxhGvB14eldFpoA!brExpJY`nVlhfBpk zeByQb&Gf=F=V1 z?s+dtrmVlV1Pr-y=)q&37qV_kpEmHGT(|)Y#qS3v5m&1@@RiRyR>{-uJpxYF_#ma` z-;27rZ^BRFPDEzhS?=O1%1Q!HFMG@f8?c*8q*JNVCfm`dtPj#%aa_G^KhJL2X!dlZ6`eMs#*qof9a0T597Yn4Em+K# z(V`f3RoUOp99U!BZo_=hpgWG*-NM}-l$cXr_^HT%uS6($>|_R zGBirh#S(vx9RFu6-7EYH!@b`aiVdZt+tTK5mC|p}FNU5rK7R_``D;avF|yO2BxGtU zVjMOrEGnU)X%st!>c zq2qGKD_wiphm?Kg&JUlfR@z7xft|?MjXY?fdD-bFqv7Agy0r&h_1gO=i_dE_U;okr zFHYL+=nvm6m$^BJCP-?Ovzcb!QAX@p=DHaTX#4XD!>AD6jLZy}1%Spb+JexBG^MKY zXgNcRKVSQ)s`a$5_%f|`_NfC57+k)x&m37UTBgu^|G_wKZ@ivE=Ebl&B9z9|hCLZ) zpu$nb|7z$gd>`V$&QYaLCH*l`8a&mta`QczTCa=_?G~L<*Yu6(erT@I`|*ZrykjZ* z=+ES@GJQvk6n%}NUP**KUZNV-->{;*qi>GW@C!Mla+4b&Vo5HWRO$IIi}$a0*nYqW zU?>d5HbhHfWZF24{JC;~N5Xd=vQyt=j%+TBWd+bd@FWlID*I zn#@d%ZXntx=m9BeX}n=w&(gP_r`rOa9aXdhNofk0;xos4IDqPtiQ$Ed{O4OKJ`4Pz z%`KB=#W+WQjY#ere0a&DNSao(Qw3|tgWa&o62~G8*}h92-p1zGym|^R7fc%KT|9Tn zThDl~P}|@xv7ZfDFxmbJ8&Pzl;I?`yZVfYXRwEI#YhicwPwx z%*D=QTkv9+KRB%42r4mOzr)aH3vI6iQ`Y(wLc zZ$9~ICF3ppu+0geM2&Au9tdbITp-Dq^1s;?YHu<2leib+t7Z_7*P?C6&*IPE3-jSr zk!rsWZ9PzUoOlvfIrkgZk<;4x!?ZN(>a9L@CkpvQY32DIg&B~|Fv*Ea_IUD@*C&1b zX}=&2ihN)M9$WzwKk<=RO!-q1=e3!CT>(CG?P-4PzD4cX`F-PaT#pgZRiORa)i|L* zI~r<{zX7`Yn}fnpH_MdS%OSTYUqq2K24SSJEiK_N?fP0@eD>Bi+8$%Ty+GT?Tpv-T zk_;jwRZmlTc#G~v$N&MEY&oCnxxn$we&20%RwCw%80k;%&Je~XL7rB$ou#X^g{3Pp z;VaBXSo@lnHuo#+F_iiaX*ydBRn(|XE~!$J6^(S&XN(OcwWJ*$-u4wS1Mamu!?fI? zjGWh~VCnn9&yL9jg2F*0h+0=}(hPQFvsUB5ZHxQyNI#I=pXYcliVavCr^Ibv#LDyW zlCDKkCa@pTbpuRj58fK@fpx~i7D6A&8-*zRqjhgyeFgm;RgKoZBxwM=TabDh*pRhJf0Af zw?40Fiv$UqBGj7XBEVKVo>6KG=JY~at>~OjBeK5i4S3vGGfL^eGtJq zeHMVXSTpWeMIK4%5TAU%7^&RjVKIxJliG`mReJ?`9)2lJe$+e6qw8-75_(J)XOt@T zIT?OU9w>`#$kDM8S z6FkqBlwMdA-k|_DXBc=5-(CR8bm> z8wlRD0OX&n6)Gvr%79DuPi%pRsHrqS7N(F(&;~X`{CLs22UpaAbv5c0eg?QZEdw7E zAfddTBzD8gBE8oXAkS6de^V1|y&_Ils8qhW4`rc-q9msn-`VuNWyIX`z1J`nF>(KV zzXdSbBp$HHUJo$^Wm#rVSey+J#3c(EGGg{^)w{e0P9b6?21fHglCs*Bv!#f<*t$FgrEqtp6HzG#r11Cgj8xJ-WcN{#~YD7+X!R zavtk^?h26Ba*HQID-|O7cj1G;#kA=97EL%HHsS?ZS5;x5h7*^6RSkGE8vqW>zIQ=4 z>bf;RCIwza`1$U(9^?H;Nl@!itUa)xMi}NL>AklAro4OV75eVuS^27l5w&oW+{T0D zxz0yw-tG&VDjEyL2ig1H)8CcuEV-u|s?Jh)`JJ~m8gKD5-lvT*CFMZh z!`n4~=-%yR1#SoYGZ0oLj7xPiIILcHf-#HGRc<12nhjau}UI4dz_48Ut)CNsTz z=J;&Ax);*<5^2d${wloXTfuEtc5tSWjfd28otM-+J@fW*1laEC7*Zwmln_`ZoB^VL zvW})bFDLGlB}Y@)R!nFP#Mj<2(4o|%$w*{Ej)0~!>et#|oh>#@ z?T?uJ*f5flYj%UZ3mdU=>D3eOSVVSWW0*O0yMsf!hR?dO5J#36%mr@>llm!sF-N(P*p4_8d8P?!M)Nm}anb6bl?bC7y5h*qZUo$3C z_gz&SCqcH+YymyyNpXH7U^5N4+g)|9HxOopZwsEHnhgmZi>nZ@EqoH&7~3U`l_+b} z0(hg2`KM5mF`-jWPE(6ABdW1*Y-}?182rQ+S(dEprHF-1DB1DOV}k@-P!{F7-2>BpAi~iVFUjeSk>VC&xJ-ClwuF zxMmXX{Ojr}OzZBnZWqHa%0UG>Q`AFMEhV^ip(-j`UE4!f^iLKdSz`$|e}MtsldYy9 zpABh*_)KLp#=b>${|JQ3T^%28-;2JK6$Lt?nTf_4LnX({t^n2j!MP&y>ZXs2r6g8) zxT(9vzyLPl#q|+*E2drzaP=Pem82h*%ukvdVn%|sTsb18(zlX2->hu=eSA#O6b(gH%eH<>bfmE;h4&>EpLdcag00hZh^#V*i&e>o%j^` z+ayYQL?On_5#o{xRaulE-Ss3z*v_Ph9b&PCZtj%zHV z*=B(FTX|f*XRFDJiyP`-+NGOEigLY316V#h;EA-8seHA}+r=^YvD9rF-^#hpgy9uW zKorX#Z#^^MLrYMjRk?tq9gxFq(!8H7CW&QsVi~nhxC&@Pm%nL^{EWS@sOK?Ov56podVrgMUAPw_Zdi zArqo#>lp6nQ3HOqI0r!xvD$_o+bXBPYOZGE#(8V9+yPw6TG`foB{X#fbx_0kaP(a5 z^%_5`)$E)g>6~BA=YmQR?sSM_w-c3d`QG=X*4vL{*XKl$s-88WpC9XaV5Ioq${F?G zI(nbkQSTjy&L?O(%*cZO(;f!p&JX?5i? zua?mBOwUkZFK8&UC?dPQDTnT^XxMi3A z!->iK(bzMzs6VIaw0b%8_tbaE_HYd#$GgY7aZ9mW5GaOr3`cGcGUG;ub2ENAtI5UyUcufs5=z<%HLa?qSuwol9zmG&Lawsnk2jYIdBu)skorWbJu~qO=ZQkEDqEJRjw$m)YJYd!h*^+4)6aFbep=%$bKV&q zYs3YXVxtwc^IC;XRMW7e%zyw!V$ri15v=-xB$!gXL$rA`YNS#Iz^9ZZ&+R*o<>~-Y!K;4l&mii?c`k@G`XSEg3VQ zGjlKq= zf+Ak)F>`#5;!`=vGa+)aQXKbMl^^zKEKWc zgIbkR7a2waTQi?*n$$gf=j)*YvU^14OR2MP-YXmnf(KQ8%fo}8GBcq^`kfcR16n#T zJZpXRFY^^YTCz#eD>z`cL$knZQ!bdE6(NO`t$8U!A8_6ERNd$Dba*j#1{XASO~~;r zwdY|XJ;r<{Lbj3P75-bO?`2MLtVTNz4dMCYM#N^S1%u-hr*|DLHTtk&F2!T3sKHDj zAR`=WFOUh1b?U3cRlU{>@45@<#(qIMFe9e611&nHF#z+iZP z8n4hlaCf@yO4F3{)(e(C;Eu7Zd3baaZi3Tt$IW|RNiz^DpE2WEkJPQpNjUv`acAh- zcgiKQK_Kf#n`j9J^Sgo_qa77HxLauQ#0~yzDu{aJ=T0XWWynG--+j=>WKfypADmn` zAh)Gb)avsp&EU*QG~wC144f8*^A6{bh-~%vGa9l;`A2GmLM>|Ymy&&)nSQ36H|vq+ zc$`m3(4F{=xKL-ru^H#aCQ{Amrvo+f066jG-d zV65odZoe;QHV++2xIo&Fbs<5)`@cdkAvw@$vQjOCJ=ZIaZB=n(A}W=tw#enle`X@4 z+ zz|$6JGO#OeDtm|dVngipp0+W3e*0S1CjsOt%`lCoEQ#_8ah29R+58*h1G2~2T`|RV zG2Se*J=&FE<&rkN%@;=EC8M>BnORdbVGQG}JuVv|x`H?oEH&G??_-EDpTEZ!fi!0|H zGY?q0sSf!liw)dxxq1hB)li4W*P63Ef}}St zz*g4Bx$JH+QpA*PtR$8TeTmJfSnMx6cxXXjGk?X$&4V?~%MhuWW+p&%Q8&OeW~m4; zE!a4a^T!$WuD17Z?oXYdl~Ci?E8n=UR=0BSFBkbw$9=S|${k^g+gxx|!f(6Y9oaaa z^F+)J_}XFR*1e++E}e5>B!VSlc-EN?rRaYA_C=DyXyTD8$3;2DDHYHV^R67 zyFggfCVeiKC5@JRk=e=1$;wpmldQ|#(Mp(yG^CaRC0)*iRx^Lyq&!JW=B_@x(RkPC zB~wkQDPG|0jk>+T44tfU*R1ouVo#N-1|<4@0pPzWeG*s?LCjQ#9xrxubfy>Ha~BQS zya6&&w6=FhV0C?)HGqY^fv2$z=9j5(I|p)|F}DXFsk|Nv&vsBY28Q)w8e`edWAoJy zy!_c{$oIAA4{X4`}HbU&nXD zt0{`nan9T6lPD>>e8LpiJ7mIu?0*&I8I9w|lqhNu{*~TX?gC=Fy8*rp2REB)r@}i^ zEdz5B_f+n0!dnu#6cG3>dsyx4%K99X(T*jGxcfQ2aEZ|DWW3f>j#vQI65HKV@g znd^~iljCpR=h&xHg7TDe_@J!IQq*LQUl&PY;*6t~QO^;}7i10^i}xNAmZa7F{RQEE ze?i`*I4nt=gq`cdilD@%i_* zwUN-jS16u`#7hg`rm~UVf3>5QJ;HU*I-*%H0&zZwe{;}Nf@CZl7)IA$WFywpl|IIByavf9$=(AI zLw}*_4~~_$>U|Ot97}M+<&{2LY{X0JSHW7QLO0Noas<;9q>Fe0(mWZ^seLB+HjO#R zPVz^*p2^frxXwp2jpIshTb3x|cssHYH_Zs+dMn6sl3dhlu0brL*V#t>GxyGSA+dF% z7b26~uQZL?tI!qHx-@~eTUkR8_3y1`2U?D50BLHBqnm|y%pa)Zoe55cRdgb&6?FwY_I|29TG|bnDIFM?LwelciAS;5kq`#}mlQ9W0LL63K#__2N8y}*g;c3yy)hsZ z)fobzb(fZ6&?0Dql(rqbDoES$PmMeQgN~ctrWzT zQ1Bp$N8JH&0M~S>(PRB|IAJm!Bhktrb*u&-gy|gYxQW5odg&NdKvTB1h-aT3*M*Ra)FyKy*zBZIDe3q+{C^Qdp$(9z7lfkt*;1@kaBd=8-Fr%iD8sd3QhKkRdpq7 z3>ygD!F@E8L(8BefpTm>2Lo&cR6-saJx8C^FkhlahBjl9Oug!IP) zy;byehCot{Cd~IWm|@iQF_IpV=)$)rJuc(yU*;_y-!UH6(~}p!*8~q%%j7^@{(oH_ zKq_roFfFUKI#`t5BaU6coJ5n;A1C0q6*VnscbG6Ek;mZNlXZzkb0X9$kI&%q&vt83 zyvBg)pj5t>CH0CD>m)u*=IrjD1d2DTh{y5VyXQQ^D0+jD7(vBWXitnZLfivvfvjL5 z)X-Md%145O=}Xj_Uov8}lDXjAU$k&H-Mm_=HSRBLa!+p+p7l^Dua=_3IhQEN(Gplf z@}Un1m+2XWj<|e|`BK~!Ad4HxQZgYv6q_hE9JyM#m{b!$D0UP{co*DA{xgZ7bb8Q= zsrX7wL1uHWZWqy~_P|#zB%d$=%6mI4w=ZSfPJs0Gr44{Wj@$yr=nMH-f$AoSPf&C^ z#;5Ob{ySl!2M2h%eNh1YE2YjM{?GIMLq=1F1}N9G>Q-5MwG`_ayjeA+auKzkfyA;$ z=&SB8hSO=79WHvDz`HFBaWC5*8oQ6AL^?-1?&6_G{)QM#uZ1ia8$<*3Cp!yN3YZpM zJ%gdsq#^tD*W%kV%eBy=H@pJhj?W@eSYFfvZ!vKC7N%0*l6fDLzJ#oJe~g${?7^7* znR|zizs&5Y^C3!AEE&m@bK&PN^c$5^3iR(JDznD=d=itQ}Fa01H)YF1qDm5_pa z_seKkWbmpmb~~Dyjg}zhT(1X=8V0B9e_@4l+m){YW*;l^Bg@yU^6)X0&fA)`OS_DLV!WGTJtqYK@|=l@+y zc?;D{q{`8Xvg{Vr$s#obogpbS_?W%5uIOsindPuu;!A^&ORZZhL}jDLARESMD4{-f zNepW-avfRIBE&aRUQ*o)1In9}Wk{1w!cPiPlB9c!zrLzM68apuHpH@HvBeSr7$g|62Zo0z7PkI`6g<uDS_Vu#^yet4Pg76n|IDEmR(Q?-_|=Q)ck?cwf0Yz3Y2z~DhJp;ERMc#v?w?h# zI$o>ZJr}?P<)2)j)a`HTlunfC!E%fke1oTBp(u6?>gi4`Mk6t#-B_hH4pWXqUc=j6+n)K8jM18SRl^<-+7N06t%baw*)}}l`phN?*1;1Ta(p!?p0BRa}1bU zQ{E?WkP0P^_4K~hRS|k7%q2RQ0#pMAAp4syDL_V*%bPS72vIyFH^i|BM$-8Ki2oD? zN%Lw0Pm9!2S|TQl=fmswtB#x&g*zZ)oB%Gr^%LLU zh}19VOas_3xD(u`v!S|jQm*WYo(gZT*=?_~t%A}pdQ%EBy7~||Ok5%QH2Tihs297n8o=ia zF8qjYcX6}3(-?UI6$8ZFjIgMOvxX*$C^NSymZ=`Dt^1(Z6fTsFHq|JZzc?*qwuRq?B5s7?i z0ifrq~W*~4t4pz1~FhzK1i)25<>YQ8@M0&2Ico(y?L;xDMVg* z@F(n#`OzW!_bIl3tu0E0nLq75fNOMCBa{`b+|-tm}vnX`M5dS(}<&FGBD#sD>p^oAe`K`TzE1 z4qS5?`Ee@72EUxByl9s7viJ;owSzbCl6{ocWLd=gC&_LJx*aDzwAVQ8aKzx5?oiQ= z>xq=^B|Zvu|BSgttnTq3|0>lrX1#kNc+QMg**+@VY*)YiS8`{`g|C_!UY#%3tUoxJ z(5a?RD4eSi+A~al3#0DEBnnau%CrXt*3v3xPmE`Hn_D;eP80lzHlw}`;*uh;mY2abt=ANt2D?uNURAELzTqK-JiC)@OeU(Hi7bf8%>a^oq zXTSY!J8u}}8Qs2{mq2CdbKW$n+c3QrMj2l2e7CIu+i)+q#A#-)%EtwnTYvBs^KL;n z81365ejwVJYUimG-cjaKH*OMiE||4nB9e`#oVR$M`P)m~9h zq&dQc!6-->1+78Du@XOX*+0o%d{||BsU3M+fyIE8W^yzojQaLZf~%oHvo$rjhAL)r z)dm^rV(x~bEknS)asX$l|j9pprIqGamGi?XViDtwu3m0&k58t^q zg6Oz&d2b5MtwsVJ|JFCi2;cl~G=4cZul4^c8efX44_-FFS8^E0@=ON(_?j4mJYzWb zVr8!^**r-40sEo2#3gkr*ClO{;^I~7r3A#NHAS~XSl@~fUFZ+S7P79@y`Wim(vYtO+wkGmGheIpfg>f`Va{EX6CHe_fPK}HK1C5_`Q25pb z(!0yOkD-p&d$;eJR$G;2Ws#1+KyCCaIZA*tBNy=fY*!u@D&~xe}s%4)&G3#$U zSSoRPVkuaIYE*?J6&O1DH2qAvU!33<{&Q(pO3|Vjkc+99gcrQLI8YeS`}vjkj}8@* zGs5=ouF#d4;0GGP?4w)HY^LVLSH_#`o@t=#uAJ>f0D)K%?-wTL$@G(P#jZ4+?YuQh zMEWUu&-na`%rlC0dxxbNLWXRw|2HW9ubY6})7E80-fMQJqgF1HV181OhHbZIy-X)R zDR;^~^44YDF|k6L$zqR>y@`gS2oLUN{TC8{ic#elg1_I&9~ZAqK)iwf)2~RGpRh{Q zd=>KI$h41nLE>rTQ**w;q@hA;dp6`5&_r>DPn)bNg--{kmf1?IQsoI0Dy<;RH%RfQ zZ8F58y7uDKcRG}s10#~*xIdt`7mGU`!tz(ne<+w@gMo*X6jRzHoX9biFX5GN7<}&- z3UkyJ@(T~4K2yLt@A(CfEZ}iek=p5#-m(X4da*Y3Ejz#E&TXc;FV(!1wtJY`jxvRcgMrq<-dv*Rm|4nAk^H5XuDR9|WrW;?rW&CF;6Ec8?SVyhbF-bbjRtma8Bz+jh}Mvtbnw5> z^UAE%)FmYGU$i|(RA?epgrw{QgQsD41H75`75&1=n{)#BOCs(YLo%uE?t5b2)A8GF zvfx!Js>lnUE|AKy?W@)dZ~|=kcfL~V{q|)rSER37b^cqKIYx?Wy|_nZAe%h!?if$> zIe(xv4`(JOL}>x)#~CY5=jXhvxLSQ36f2b350H~}V7~&a2#-?;QkQhawsdMoxOG{;NzMLWK>C0$({Q?eWdp=U>}kAt&fGY7?oo6Zz2}F_ zy-i#4#h>7@c(m$-QAqVi>0;&#=!rp+_fTo=vk0PJL*4_M>7avt2Cuzfy~^|hOZBBH z?=}?#Fz&~B6Rd@i{)%Ai`I1paGez)m7}qGrr~iV}Gwz>3;Pl=N#z59(2~pC{liQAj zjd#d~m78Z*;N=wmoM5`R+m83Q#~x6&B=n(67&8@}CjJLcuY4JG;jSGpgQB8pprl@5 z{`@&IM2!W*dn$;}$$ouj6LpL{9(2Q4g3 zS)`z~)t8cFKdpY1YH{u*>!{cdU;Fo-_)4slL4u9AVFDQCK;dhxcQgy(2dlZVq43n| zv*e;O;+~B46X9o^IoUFI=@2j84=rlsWhjsXA-V!L@~FW6ZQ^U6k2x7j+EQ8pmwe<4 zv`-qV0aAbH=wDw}1uym99f}TWo8RM?rzz0>R97uYhsIHY?eQi+yOQ>c?7jwl3ET77 zBUo+%PAN5qO*m(J1F{bri=LXg0p6)3(_c(5_zf|xpaNC`|MiylztkI#Z&Lg(K` z0PuWPr*~1ygDFm_obB7V8W0v=3sD%-*t8p3U#6Eqdqxc}sSK(C7L?$8)cz>-t>>fZ zylK?10*Q`kH1LUclcq~uR?XGzT8`CCB_3K`{WdRn5J_(E83EywPfX}4Y^dUhj@s4B zvf+h_&e1njI}CQ+zp5xqU%Bj#-?kW=MWBl0c}U9<3|dhm&ku5qfBY0&zY=J2C5<<& zMb>reazG$^?NjhHPm=u=)=}ObaX#u5?1$>>p=?RN_o=;Z36gh9&+q=Y?A$LtH$EzQ z33&+do9nq@s|41BtFk298kn*pFcLpmeJC_Aa?!RE(#Z0SzSuV{*JjbXVe*k}=y}&O zE#q6JvJY8G-YhpIOg75aY~z)Wrbt8>(FbNFb$0hbfgHai!Aut*Xk;gVB6_K83hh}d za+`HXnk52*S(;tE$2>684u^Udtn%8t86IZa`FhJw{8BH+k(KkBK|ITSRpfA4R z=Jj5yM&wOh3=Y6+UG7b@0?aN}-Ta8o{)qs`w&X9{FI4~a?_7fsX!aL>P39A)>!1z+ z&Vy|QP)v7}{n@!}Fm{)dnPD`q<~lGO?Y4lFtl0V}lEZ(XKucHcb&KEFa3C*PHo4EshhBA^fLXNRE%QvkqOCSloZ46A`ReZ9UG^r4vN_dMS z2hD#AyvM{~t~VuwbI#-qJA1EEX(xccAaFp0YC`581SPLE9ryT4BjvRj4gilR8@i{gTen{*Gu<%KjVp4`q{;7ysqER24#E%{(^iftdO)VL) zU`9OC#~x*DB2NmA)P*c)d?~FKiV*4}m1O<%zTl%{(+Mx`Rt}}G`_Oy4J8mgshG9xi zP4V+G3Hn|P#P)f48qd3*Gx@?Y*(3;2c}@I`m~C#cH1vwdQMI^*yK3k%H9@mj_}Stn zV5nEN&VuL;8+Y}XCu z1m?1gx;DogT3tp)GhmpQCtn;|3H6BgT?fcai!5C$Nn+Nhu{v3N>~H|y7@vF@yi*Jf&x$hXX9`41I7u@~^@z*LfdpwFZDG)E;YYY{VAlk5X#;?B;|T2Ea83M3J9iFMpP zy#Gg}lGjwW*oxQyyh|>E=ypG225{Tezr?5c{Inp#(rp6>rtB6iVM5>}7#_2ab4Fim zM~e4<^O_?nYIbP|Ngva`un-Rl;5Y6}D%p#)XSI@NR0(}&iFdS8XGpKxpK@ZJ!os~|W48?<-uW4*%>BUXMJlw~el}~e?ieybDW#Z1 zvkaBnO$qEdM%x7F1e%Nqw&8!YW&*O$J+f0|<{a=W!sX*KY0+QXyy0>@`yBTL?AEt8 zbF*yt_IT*5eH8Rb_s_fE2(Vn7$$S>KrstIewZRiqmYRV@kXO3^hPW>Jd+<8?#|8y% zTa5?nH0I7_4A8N9ovXJOe56n2{Tl-o!X~X%)b}4h0t=C1tQtcK4Pp6`4=CUU7BGzh z|oiOueZ=>Z_NF5zM1HE?@2}FR3p<2O!Q6hddXKR0<)F zmMjD!kYK5=eOv`k#B!iN;oCB$|APf!mOX4h(%~ms-~B$zji8cy+8UUos8_6!**&J@ z8Ag%kP0fqkcACaeFw-O;Q z@G)LXzfGEr$h7H7SC249vKWG`Qw8ny6{!%W4%wWUJ8hRzEBO`Kh;O{EQ1FN_X&=lU; zQ$8K3#7hf;^6(?%P+Q)IR(}l2S=U40;z;G!GmM68Vxs_NVyczz)IvRCdM*fj;xsOZ zk^qge|JcS;_P`SnCsSz2W;T4_?@GyRA#C2GSFpm0NovM6^mQ$Bg<5l^=pfYUgr{x>&3(AF&kr10(q4 z;v-SK!&h|-obK-5J1<958d5*a?3cZb#m%QZVkF$E;-W;5PRygVk5UoyAuY(v&YzhK zb!2KY5Vu|*=rW9s$mA}r1q#ia2;3zmLS&@Sy3PTP!{!0V$;N%VR5+&eL&0?lxii?C z7_(u=kPIZv+$V}-wYGTyJvu!=#yR_d9w38x+4{=%k_y6#IJ@Dsv84}+em_M(%eg^X zQDd|zYleQJr<4qM;Qsw>Qb+=1K+qLJfUO9Q!MGyx;K}n1-COT>EoU0rxk5OINA|~` zhWwgh-YI3;o*V*<3zzM`>q1P_+7edBcbum|Kb zbEjs~kZK)f!5KkU4vp$sbl*k>P?>{kdD!MO7Wj#nxDoJ)hBE~^Rg}iNY6r^C1t71& zYH#z|+O3WDULX0VdoG0SP+%m^S8M4ZZ+twbvajeD%)VyAR9{<=U}Vu5K~Nd-Z_U}Jj%Pq$nO(r&)m(;if0)a3krTf##?&c_ zB|PqNyb}%C_}jt7^S|14Ruv|6Pu;CK-TnGd)D2eX0RE;ZZce0)xji0B{$H(qc|25Y z{I;j31>>>Lphk&o8T%4il(no$IQ9-ncB6)fq-I2eR%3}glx%}z-}Q8CF(_fINrW_M zktO?k&-47=|9y?{#0-HE0=bdFGNqe=#_V#*gohxw-({7nB( zhX~RF^`_7k*FGv@&OFG;vbsJEhR!iDpX|AWeTnQHo@?s)q_Ew8S#aDq9=Xw}o{gMV zG_m!2gQaDCD>zVJ>?uq;*z>vamJ`$h+!q-UU(1$j`rd#?$wQ|91f88q0RE`Tm-OoR zMFPQ8a;6u39^QRPvS4rrrLw#ulx*Y=CISNq>`5b&q;*prI+~W}9bJMj$~fjdoUz5w zH$j?{;TbY^EI3{WS4j!WK~5U;^Y(mLGdTr(O2J9U>DHeZ2GjMpfYUix!1bsAEx_$r zEYCyU1cn)O=YTRfNmYbXK~CU$BVt+hTpbq60rvj9)?L*q-Rp0^DsZwkos|iWk|Dr} z;h4h5Pb*5gmWJV?!5yQXfH*6f{E}fGsRW6It$g4S3W#FW0R3Cy)?rWhm{1X`A{~6)FhH;(LO)zOE%(BznIy8s;wV3 zfV+>Ve;xg$a#9v_i$bNzoD60#k1);OEC2ccJ~R`N6jZH4mfduP;BM(9tW%tofhD6) zD_*DqNQG82k6A-iA8`5H`p_?yA)M;FW`Sl)yQ`wXRru=A=lpgq)bQs8DW(zl;N^8= z&R5Cq3!tiS{Yfz@ytRR5W7`!8(;n%>e2QmW--#(fxK~hh+H7=+XIWd_j0b+!2P-f} z9R}bQC=TNsQ@YUL20bg75~di9^h72-Le}aEjfYGlL|ukE*ZTIECoy(y}kmdBmfjgx>U5d3hVH&!cVwD|9qM4>(J3eb4pJ8 zPi}=WN(X^pd$tGM##CPGi&_sm1D6w%{~R?f1i6j zz>ZCNn)4PA5Apb-T-@7#e7(>?vN?BnQO4zcGVhsFx>#!wxE}6J~>O$=dqazf%_-T zi7hH##9h}7&NtQhJvV^b5t5W9oKL($EI_*8)_t4v0H zmiiR$jTmnw)|aUO>QcZU?#ml)#_`)(V~yt^8yU@GeImR+XVEovr2@?}g{(feTyU^7 zsV=Iu;LcGZ^YK=xHYdXb`lh6si}3pE1FFNt1z4&5GaZ63 zX3CZcfSZ3`jQ4q^XXBDb!I!w~RQVs*ic!idsImG`wV&U;`g47BwvSWvj&z{$y75) z$P%=CJ1lhtt9ver-$e+4o>*qE59^Jya~{$qJCT^XM29I-mXV`^v)YZbVYIh)>2(h} zzw0X%L_)Ts%vZmZ7U#YC3f=vFlD^dBMZS#^A`vxyZLP?)@6d8yf zSq{Zb@B6Ey*AXa;HLVtGN$+D^^=f9t$lUYTF?l&!n# ztJI947%j13mHAMfD*#K28NUrd50F)YgcOSAo_^{hKV{+Z)cC_ygYW^Ci0YjMU06C0 zRy$(&L7LDUGkb_$Zb+aYGY;M(z+rZ$_dolK7pl9(Q{`247=W-M^j$-nGCh{&5sz3- zeMQx<)dbf=gi!o|??<#uB$Oaugmf18tygV)ONo6ium57-Yzkk`n;+}TB6g7ECF&H` z?i%J1&=|JXxj;Ow7y(=(#T#=p=F>rXn8c9}@iRA1K~c0pAq*=GC*uM=CM)VgUXtNO z2oEtpV3m{Q<=8$v)nDCxXOH_*<1v$=oC8ci3{K^_0(_?DIq#6}8n?s@eLN^ZKzi-< zEwvp<*r|x3LC}Cyf^KC9jmI**&A!i}@2E!waGEf14IzA2`0eyUU5GNGR@udA=dOak z&kwZSR^PZqM{7?*EhHOA~P{YL8ZgBx{E zy`)UCEYvHjfZJu2>)(FqMypQaZaAX_ETUc*--b%`waFX7&X@~M9nx)Fui~XG?mUzu z_}cV_D2s41ew1QDF)XhUIjfRgoYZHOTE5Ox&?u??fcP9QOc4%ELM|(sQwO#D(6YGC zxpa_%wCuf>EW^#9zg$Fh6Gn{$fJLGB)85IN5SPfSZM|NY|E@U&v2LV2L2kULB%)<8 zz|%UPG)7(;;4vHj6~1BfUY>B>N!kn^);d~>#D--UL6*S29CPn|c311%QQ3T?%1JEP z#biaLPTY?&LYdwo9&qyl97rOYOE9X$FI59BFt!{(gfV|q5*M0)m@As}Im*DA?%!Gt z@9OiE|0g1-t@$*TsbsEd{__yriWAXUv)$BogB4jGl-5a2`*fYJB=oRPZ<2+LU5&<_ z2f5sgdP*t;wUFnfQMEg*TncrRTB;;B%gdF){P^ztf~h`=e|58K9_4O$!$vRc`cK8+ ziF(tjpjuRSI72h$1SdG*2<4*!kMIdJ?cV2(sLg`7WmcZ+6pq%d60O(;!8(Gunzz#8 ztl6V>2}grxK|j!ZqRSY%xvvmebl3oWz<*ciqOoS1z$r{qj6VaJ5(8j?P2kU)D&%MG zQ^Dh}B!z)GMa2lyvg|hFTkilDKI1&(s@Qdb&4|4i;J0gQ;r88DILl*zWoE@g@cVu= zA`ZdB$53wOa}YpC1TylE!c17)8v0`O<4~gDe=Q@1A(Ndc_)(S+DJw|v zlaQDY0NwT2<}nF6Lr@C`r+#^xfle-@-% z2^ek5!a31!X&h*V1M2jlD(!+nzyK*H4h@j<0W~LvF;ybC6D?tJU^<&#OtOVM2XOOh zIZ1g(|L}ev3jzl1L8G)V1LhuX<{%+^o8G3~5G_$93?`s7!1;s&U|Zse8{QVUrMN{| zT7q0=tNt$1^m5>GiY2}gp>5?vwUPdiG#x>K@4_Q@{b1o?)gK?Vz;5}P&>?`~^e(ZX zR5|X2t=Rez{+4B%&Ze`_w+O|2WK8Wyu>_|XP&R&RXI?0eD#dDBTobv0o^zA>5BpIL zxf{Y6t8ZlJj}Q<3a%4=h?kof-g!cx{Vs=NzH)kgDcB6HF)g$I1rUd%08WzQf@Qnb2 zDXHEgK`)==k#5CM9mmQkq%OqRW&rf4F)GZhm;S`56YO&Wu_HJy%;+BLmgPgBQ3g-& zSW_8S;b)+}2>)95#pr!@-=<|YfF?avltKy#6iRVj1*-A}k~=QczvL>>b+EN{yL2iPflF4i>_pg51(mkp3lAP#r>w2P*{ zlns+DB3#FS9kBiA;n)=iWeuVChv9#a5Cg2$u(8V!$AQM zdeC4})r7R0K{4r!A2aXK1M**e*b_kqQ}|oHefGNs+($K4qF+=rw_osf-UUya4`Me{ zOf^9S!pW3^!4CmpCO+`+Bzr-?T|sxlsdwv*p`C6()!SRObnWM(V}Fd^wR6)pZ09sv z$Buzv)y|Fukqkw=o0r|CxLKkgULlh0GJ8qkf*YxpucM^^#Jgw<0Htmm^7sQzRp1%7 z0zz2@bo`#%fJasK$JyPFE#t@OSW=@AC1c*bwMG!b3BdTMTgBQYkUN{AJ`K34ytov2 z_K+{E50yLvR2in^&@Cki$Depwv^-?DvVkDVPr19+0<35EJT4^@u#-)wjL)i1M68!IHuEM>6P7(37XP{S#Cglh~ zTm@h?xCmJO6w35(9H$LL_c~S}VN7}{3H~#WTKLFAtWc=((!M^(!a25Oi zafM88wMx-BOI!}phbHqB&An1gpQ70@F7Kq<6zRJ-6+D2qXal?PI28Bp`+4C=mY zJC0@pN_hlSFi;vQkJjDE?D$@jC__ghe&x?{DU5;Re=HQ`#6Tbv(EV z;zkddgq$>m9RyV?Yj`Mt%eo{DPEyYq`lO9DV()Jk-7H{Tp2Y)&FStfyV|eq4Tz~sEuW*%# zv~%R4nBlvajq{p`b7pe)^z2J#s<#F7ToN#2luo-19kjwK^I%Nvr1QS-*D8uaBI@>r zmN^R8>5QLt$#BAtH6J3>F77kb+zQn=fm?X+8)Jh411VoQR^)fS556z??0&$7Y+kj1 z;QF^9>Vc?E8weD!v&KX#(VcH18%>529uv7y){x_}_my z6T8AQXdiYL(e5)ESK(wOs9>n4|3>JrMk(M-Dk!VguuEnvSQ_!YB&ZWVQWwyc`U*ywL4Wv{O_Wz|4b$dn%lT6W;}I zBTb!GgSO%$zugnS32?Gb05mqh2RDLk^O>E#`IEj0*Q=1cL;E+^cwkkL*-_)B`M@0 zCm?EBIn&+9+Z2(IlTb(2U5k${sASA3$n6=>zt;*YV?C^K-H+`s>&8 z1k%V4Gk*)sWWEaARNnzd?-BTIMnQ@%?ys2`nA<9Mm0eTd^{UlZmfbZ0>?IrCG#;OZ z2abdPxj-sULaR0dMJ<67a^zkAfHM&SnwtK1RxVw&6+vHJxvqh1p#D!hEOnP&i{?5X=F{72AE@J=$q@p~^oM87i@A*fexa6fa`Bso(HM_~L!j6%q|Bo%?sqE&}g~NZf zPM>p}oCK*=KX2?#@dokfcGBBSK>fXWc0<=8DP}TQe*V^E_>x}uKkagKZ@wb{qw!qY z`kGD!|L=sYGjpHUVav^^{VHnQh2o=Q>7Q7)NBkT? zhDFvs?aCJ@<8uKFI;u|K7~kVaK9hP!v%fj>Kj|w+O@3#6^+e;ML0^eq!j#cc^fR`7 z<}D>DF~Eni(r*Sb?`IdmOaIG{HidYRrlo#Gc>1hqhaEo9`(c5*Lcm@f`zu)c3h}Ar z_uYKyQxER9R+_gC=i>I%UP#x_;f%aABj?jwkwdtjQ&ZupcM!w)yVNEg3=Et&g z>yqvJBiPYom)Sj+bOrrCEQrSi|4!{^4!>;FPIbgzyiI)fNn$ZI^_g}ibNJDXji_^W zy2al|sl>a1ZQtvZ3`LIMBucs`kJe0i{vxy{ycB4XQpF7p;9E1y6?ubS9rF&BYd<3? zDyPshS(jK+QH-xjD6BDp!f>(=Drvdkw_`aLIghJ`U*6X0QGKReBh<{}Ab`*P=q?xZ zyW~-!_Hcpsu<1TOX-ez)^t@b4*{74?z5V(o=a{uR3|u^Ts!$`LF+ zwpt-&LIjSGC3WxFc@Oy>txE0JyPHahYh7K|=cg{17EiJwo-wguWM?5+yUp#-U z1mJ58uOoUviEJlG70co7(I<}h|7Gg_zh3%%Y@07D&`(<^PWIm^f5yntuvq`Xt^WfJ CzE|@A literal 22640 zcmb@u1yGzp*Dg4?yIYXpF2NlF1a}yGfB?Y*1osINJOpYo07em-6SKi79v9v?r?0br#8-ot(e(nZm6M(O|i9z{m>5mprdU^IN} z0Ps@9XTaFxGhxDWpvGxX(6b!iF35E++2?dSx~;*!F%0AKayd7M=D84X3_eZ-Os(Xu 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 L*xINBMLyAq+;8osliOjD25< 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 literal 14691 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 literal 18437 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 T+N2(|opsPu)m150LSg;~pioiq diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index 9e5154bad..a43700cf0 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2242,"get_count":3,"put_bytes":239,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2322,"get_count":3,"put_bytes":314,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2397,"get_count":3,"put_bytes":389,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2472,"get_count":3,"put_bytes":464,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2582,"get_count":3,"put_bytes":574,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2657,"get_count":3,"put_bytes":649,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2732,"get_count":3,"put_bytes":724,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2808,"get_count":3,"put_bytes":800,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2917,"get_count":3,"put_bytes":909,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2992,"get_count":3,"put_bytes":984,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":3067,"get_count":3,"put_bytes":1059,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":3145,"get_count":3,"put_bytes":1137,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":3254,"get_count":3,"put_bytes":1246,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":3329,"get_count":3,"put_bytes":1321,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":3404,"get_count":3,"put_bytes":1396,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":3480,"get_count":3,"put_bytes":1472,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":3588,"get_count":3,"put_bytes":1580,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":3663,"get_count":3,"put_bytes":1655,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":3737,"get_count":3,"put_bytes":1729,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":3814,"get_count":3,"put_bytes":1806,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":3923,"get_count":3,"put_bytes":1915,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":3998,"get_count":3,"put_bytes":1990,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":4072,"get_count":3,"put_bytes":2064,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":4148,"get_count":3,"put_bytes":2140,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":4257,"get_count":3,"put_bytes":2249,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":4333,"get_count":3,"put_bytes":2325,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":4410,"get_count":3,"put_bytes":2402,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":4486,"get_count":3,"put_bytes":2478,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":4595,"get_count":3,"put_bytes":2587,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":4670,"get_count":3,"put_bytes":2662,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":4745,"get_count":3,"put_bytes":2737,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":4821,"get_count":3,"put_bytes":2813,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":4930,"get_count":3,"put_bytes":2922,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":5004,"get_count":3,"put_bytes":2996,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":5079,"get_count":3,"put_bytes":3071,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":5155,"get_count":3,"put_bytes":3147,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":5263,"get_count":3,"put_bytes":3255,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":5338,"get_count":3,"put_bytes":3330,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":5412,"get_count":3,"put_bytes":3404,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":5487,"get_count":3,"put_bytes":3479,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":5596,"get_count":3,"put_bytes":3588,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":5670,"get_count":3,"put_bytes":3662,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":5745,"get_count":3,"put_bytes":3737,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":5821,"get_count":3,"put_bytes":3813,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":5930,"get_count":3,"put_bytes":3922,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":6005,"get_count":3,"put_bytes":3997,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":6080,"get_count":3,"put_bytes":4072,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":6156,"get_count":3,"put_bytes":4148,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":6265,"get_count":3,"put_bytes":4257,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":6340,"get_count":3,"put_bytes":4332,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":6415,"get_count":3,"put_bytes":4407,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":6491,"get_count":3,"put_bytes":4483,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":6600,"get_count":3,"put_bytes":4592,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":6675,"get_count":3,"put_bytes":4667,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":6750,"get_count":3,"put_bytes":4742,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":6826,"get_count":3,"put_bytes":4818,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":6935,"get_count":3,"put_bytes":4927,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":7010,"get_count":3,"put_bytes":5002,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":7085,"get_count":3,"put_bytes":5077,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":7161,"get_count":3,"put_bytes":5153,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":7269,"get_count":3,"put_bytes":5261,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":7344,"get_count":3,"put_bytes":5336,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":7418,"get_count":3,"put_bytes":5410,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":7494,"get_count":3,"put_bytes":5486,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":7603,"get_count":3,"put_bytes":5595,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":7677,"get_count":3,"put_bytes":5669,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":7752,"get_count":3,"put_bytes":5744,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":7828,"get_count":3,"put_bytes":5820,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":7936,"get_count":3,"put_bytes":5928,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":8010,"get_count":3,"put_bytes":6002,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":8085,"get_count":3,"put_bytes":6077,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":8160,"get_count":3,"put_bytes":6152,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":8269,"get_count":3,"put_bytes":6261,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":8344,"get_count":3,"put_bytes":6336,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":8419,"get_count":3,"put_bytes":6411,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":8495,"get_count":3,"put_bytes":6487,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":8604,"get_count":3,"put_bytes":6596,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":8679,"get_count":3,"put_bytes":6671,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":8753,"get_count":3,"put_bytes":6745,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":8829,"get_count":3,"put_bytes":6821,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":8937,"get_count":3,"put_bytes":6929,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":9011,"get_count":3,"put_bytes":7003,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":9085,"get_count":3,"put_bytes":7077,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":9160,"get_count":3,"put_bytes":7152,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":9269,"get_count":3,"put_bytes":7261,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":9343,"get_count":3,"put_bytes":7335,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":9418,"get_count":3,"put_bytes":7410,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":9494,"get_count":3,"put_bytes":7486,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":9603,"get_count":3,"put_bytes":7595,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":9678,"get_count":3,"put_bytes":7670,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":9753,"get_count":3,"put_bytes":7745,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":9828,"get_count":3,"put_bytes":7820,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":9714,"get_count":3,"put_bytes":7706,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":9788,"get_count":3,"put_bytes":7780,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":9863,"get_count":3,"put_bytes":7855,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":9939,"get_count":3,"put_bytes":7931,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":10048,"get_count":3,"put_bytes":8040,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":9917,"get_count":3,"put_bytes":7909,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":9974,"get_count":3,"put_bytes":7966,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":10050,"get_count":3,"put_bytes":8042,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":509,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2897,"get_count":3,"put_bytes":889,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3278,"get_count":3,"put_bytes":1270,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3656,"get_count":3,"put_bytes":1648,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":4035,"get_count":3,"put_bytes":2027,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":4415,"get_count":3,"put_bytes":2407,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":4793,"get_count":3,"put_bytes":2785,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":5169,"get_count":3,"put_bytes":3161,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":5545,"get_count":3,"put_bytes":3537,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":5921,"get_count":3,"put_bytes":3913,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":6300,"get_count":3,"put_bytes":4292,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":6673,"get_count":3,"put_bytes":4665,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":7047,"get_count":3,"put_bytes":5039,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":7422,"get_count":3,"put_bytes":5414,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":7690,"get_count":3,"put_bytes":5682,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":8063,"get_count":3,"put_bytes":6055,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":8332,"get_count":3,"put_bytes":6324,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":8495,"get_count":3,"put_bytes":6487,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":8872,"get_count":3,"put_bytes":6864,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":9034,"get_count":3,"put_bytes":7026,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":9407,"get_count":3,"put_bytes":7399,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":9743,"get_count":3,"put_bytes":7735,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":10079,"get_count":3,"put_bytes":8071,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":10417,"get_count":3,"put_bytes":8409,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":10755,"get_count":3,"put_bytes":8747,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":11024,"get_count":3,"put_bytes":9016,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":10970,"get_count":3,"put_bytes":8962,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":11200,"get_count":3,"put_bytes":9192,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":11575,"get_count":3,"put_bytes":9567,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":11700,"get_count":3,"put_bytes":9692,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":11929,"get_count":3,"put_bytes":9921,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":11983,"get_count":3,"put_bytes":9975,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":12358,"get_count":3,"put_bytes":10350,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":12375,"get_count":3,"put_bytes":10367,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":12712,"get_count":3,"put_bytes":10704,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":12942,"get_count":3,"put_bytes":10934,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":13317,"get_count":3,"put_bytes":11309,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":13653,"get_count":3,"put_bytes":11645,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":13775,"get_count":3,"put_bytes":11767,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":14003,"get_count":3,"put_bytes":11995,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":14232,"get_count":3,"put_bytes":12224,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":14493,"get_count":3,"put_bytes":12485,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":14720,"get_count":3,"put_bytes":12712,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":14768,"get_count":3,"put_bytes":12760,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":15141,"get_count":3,"put_bytes":13133,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":15373,"get_count":3,"put_bytes":13365,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":15640,"get_count":3,"put_bytes":13632,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":15906,"get_count":3,"put_bytes":13898,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":16136,"get_count":3,"put_bytes":14128,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":16297,"get_count":3,"put_bytes":14289,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":16459,"get_count":3,"put_bytes":14451,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":16543,"get_count":3,"put_bytes":14535,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":16770,"get_count":3,"put_bytes":14762,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":16891,"get_count":3,"put_bytes":14883,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":17120,"get_count":3,"put_bytes":15112,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":17135,"get_count":3,"put_bytes":15127,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":17183,"get_count":3,"put_bytes":15175,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":17231,"get_count":3,"put_bytes":15223,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":17422,"get_count":3,"put_bytes":15414,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":17469,"get_count":3,"put_bytes":15461,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":17448,"get_count":3,"put_bytes":15440,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":17603,"get_count":3,"put_bytes":15595,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":17790,"get_count":3,"put_bytes":15782,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":17875,"get_count":3,"put_bytes":15867,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":17854,"get_count":3,"put_bytes":15846,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":17795,"get_count":3,"put_bytes":15787,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":17737,"get_count":3,"put_bytes":15729,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":17646,"get_count":3,"put_bytes":15638,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":17656,"get_count":3,"put_bytes":15648,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":17704,"get_count":3,"put_bytes":15696,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":17820,"get_count":4,"put_bytes":15812,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":18011,"get_count":4,"put_bytes":16003,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":17878,"get_count":4,"put_bytes":15870,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":17852,"get_count":4,"put_bytes":15844,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":17793,"get_count":4,"put_bytes":15785,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":17666,"get_count":4,"put_bytes":15658,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":17570,"get_count":4,"put_bytes":15562,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":17612,"get_count":4,"put_bytes":15604,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":17799,"get_count":4,"put_bytes":15791,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":17634,"get_count":4,"put_bytes":15626,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":17358,"get_count":4,"put_bytes":15350,"put_count":3}} -{"i":81,"series":2,"stats":{"get_bytes":17326,"get_count":4,"put_bytes":15318,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":17373,"get_count":4,"put_bytes":15365,"put_count":3}} -{"i":83,"series":2,"stats":{"get_bytes":17346,"get_count":4,"put_bytes":15338,"put_count":3}} -{"i":84,"series":2,"stats":{"get_bytes":17569,"get_count":4,"put_bytes":15561,"put_count":3}} -{"i":85,"series":2,"stats":{"get_bytes":17617,"get_count":4,"put_bytes":15609,"put_count":3}} -{"i":86,"series":2,"stats":{"get_bytes":17516,"get_count":4,"put_bytes":15508,"put_count":3}} -{"i":87,"series":2,"stats":{"get_bytes":17595,"get_count":4,"put_bytes":15587,"put_count":3}} -{"i":88,"series":2,"stats":{"get_bytes":17531,"get_count":4,"put_bytes":15523,"put_count":3}} -{"i":89,"series":2,"stats":{"get_bytes":17398,"get_count":4,"put_bytes":15390,"put_count":3}} -{"i":90,"series":2,"stats":{"get_bytes":17334,"get_count":4,"put_bytes":15326,"put_count":3}} -{"i":91,"series":2,"stats":{"get_bytes":17201,"get_count":4,"put_bytes":15193,"put_count":3}} -{"i":92,"series":2,"stats":{"get_bytes":17000,"get_count":4,"put_bytes":14992,"put_count":3}} -{"i":93,"series":2,"stats":{"get_bytes":16973,"get_count":4,"put_bytes":14965,"put_count":3}} -{"i":94,"series":2,"stats":{"get_bytes":17127,"get_count":4,"put_bytes":15119,"put_count":3}} -{"i":95,"series":2,"stats":{"get_bytes":17101,"get_count":4,"put_bytes":15093,"put_count":3}} -{"i":96,"series":2,"stats":{"get_bytes":17323,"get_count":4,"put_bytes":15315,"put_count":3}} -{"i":97,"series":2,"stats":{"get_bytes":17370,"get_count":4,"put_bytes":15362,"put_count":3}} -{"i":98,"series":2,"stats":{"get_bytes":17306,"get_count":4,"put_bytes":15298,"put_count":3}} -{"i":99,"series":2,"stats":{"get_bytes":17348,"get_count":4,"put_bytes":15340,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2311,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":2,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":3,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":4,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":5,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":6,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":7,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":8,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":9,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":11,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":12,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":13,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":14,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":15,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":16,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":17,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":18,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":19,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":20,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":21,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":22,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":23,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":24,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":34,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":35,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":36,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":37,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":38,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":39,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":40,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":49,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":50,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":51,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":52,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":53,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":54,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":55,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":56,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":65,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":66,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":67,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":68,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":69,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":70,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":71,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":72,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":81,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":82,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":83,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":84,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":85,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":86,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2615,"get_count":3,"put_bytes":751,"put_count":4}} +{"i":1,"series":2,"stats":{"get_bytes":3143,"get_count":5,"put_bytes":1135,"put_count":4}} +{"i":2,"series":2,"stats":{"get_bytes":3399,"get_count":5,"put_bytes":1391,"put_count":4}} +{"i":3,"series":2,"stats":{"get_bytes":3720,"get_count":5,"put_bytes":1712,"put_count":4}} +{"i":4,"series":2,"stats":{"get_bytes":3848,"get_count":5,"put_bytes":1840,"put_count":4}} +{"i":5,"series":2,"stats":{"get_bytes":4104,"get_count":5,"put_bytes":2096,"put_count":4}} +{"i":6,"series":2,"stats":{"get_bytes":4232,"get_count":5,"put_bytes":2224,"put_count":4}} +{"i":7,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} +{"i":8,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":25,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":26,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":27,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":29,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} +{"i":30,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":31,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":32,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":41,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":42,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":44,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":45,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} +{"i":46,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":47,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":48,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":4747,"get_count":5,"put_bytes":2739,"put_count":4}} +{"i":57,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} +{"i":59,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} +{"i":61,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":62,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":4939,"get_count":5,"put_bytes":2931,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":73,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":74,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} +{"i":75,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} +{"i":76,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} +{"i":77,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} +{"i":78,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} +{"i":79,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} +{"i":80,"series":2,"stats":{"get_bytes":5107,"get_count":5,"put_bytes":3099,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":5107,"get_count":5,"put_bytes":3099,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":5339,"get_count":5,"put_bytes":3331,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":5339,"get_count":5,"put_bytes":3331,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":5403,"get_count":5,"put_bytes":3395,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":5403,"get_count":5,"put_bytes":3395,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_after_fill.png b/actors/evm/tests/measurements/inc_after_fill.png index 08adafcce7ada4864e7f1e5735e289b1700bb8e9..b85b41766c5f0d6f6ca340a3f4669fa199e8b51f 100644 GIT binary patch literal 15071 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 DP_FDw literal 17092 zcmbWf1yodD+xI^p2t!MEhYl$rA|RnihsX>=GZIR7BaMiJAl)D+Fbpu#9fO24Dj?k< zC>pR@P(1JQaqs^lchBp?uoTwU#GdB?0+2~P{>sW=nAy6)1e6J>jNp-EnRhR zf=54yK_b+t1Wc2(C|xwCUM2?AOJSp)wDbH{Lt#2DhP zW0;s+fv$i+Ls7%qAZ!<}K*M8Kv`JUNdV3)uS1O=CA?`m?{rA>mTk9PfUXmUk&*q14 zUcCqHgzY4QzRu;(CFm(a9YjNDK_RrXA&S6vNQh-eh~-rXw*QJ(`M|g3Rle(0N5>U# zKUWuo1mN z8OvXRYeJag@)%VJW&@6!Z6IJ39l3Kn?iO>v(*Ntrm8a^@3CY)2{^y_T$V>)nN}6sc zL!2mQc%t;@lq~X{ixT4QALD@*WxlP?2to>m%3vnCnT_aVJ>&W>Rd?)*ycxe>t>@{2B>b#v`}o^;?xH90(#I(ZSt+WMePb88;8_2yvmk`W}w zI+If-c#C;v(=NEb?fhACu;sZ&%7rSW?m*qzs>Nz9s~1>sjA17BvImKN$iJpvq>HZrrSI0CuYi5 zL<<;X$`<98`8rF9( z!_d#2+*vJW$^gJ8{f_m@=HA z&-MDY$B=^_^i?DWE!KX=h&x{oa~woP+6d)j%$C6-$7TBi8Bzrcg|99c&eu~PAKu{r z$)zWv`FYkV8f`vVla&Rv7eCX6HwikFMUG#|pV!bly<(VSOnoxmlMDsHw)KUkrrv_Q@vr>jcBQM@(sV$^lE$lJ;;`r)h z)}TQQqUpi7)0F!&jq6Jqlm5sldVh3?PM#7F3Z5A{nqWO$U4<;|F`HcOG0bt4WkeDF zU!o1&=_;T$SIJxD1_Bw18Nazvr}u6mrPZpPzSe@pX9WbR$frKofmnw*m`%O+J7ooR z5HgPK1`-6gZi}7O`qWc^7D+Pug}>p+{xEc|CN*|uoi$MpQMPl6L#p)Z--0bivY>+Fx-e8qFyZd_N;9uiAq4{F8ulyE>B)`u&PD zQ@$o~;@SCm*5B-lci#m*eDjWxil|LX12BkPk z{r&8`3pyLf^R|ap)Qyu%5WO@Tn-gE~J`Ifv&m|RMGNP8y+5Je z`tHv!p%v+m@d--zO$}_>Rc!T#hFqLJgbrScPt6Jc! zhE4@|^ani(GIZvjBoRL&IV|DNll#T(QM%4;+v4P-u(=Z_bz@?Ct**MJvd-ydeQ!fE zS7kfVhuuHNyIT=-?Yh3NwAwIo$j<`0&3&fG##aomUsAR6d^J%M@+U75=L$aCF?lSO zj_2F*L35NoUYLGsZ(Du~1pD}EQn8t}$hhg5CQRtjw*M3OiOPf?%-DOjA6&*UcW3-L zakI-ua!mizsnY+|f|ls5YG8gKHD{X6=u@*?mKMriPU&t7qk`LAyPugwMY4c&s6<0O zpQZkyPi?*TyccZ_U9pvHxQ(j#Wu5)@k7_IePcHry*Z>UOHU=NJX*7s!?o?7w(84E)TyHc4NpYoP?~ak+=r_+8JKVv zHu70f<77$Fd9swGi!w!7iWV+{$yO>hUwE|c&V!f|?C~>p1*LRZQ`D~sPWfK5R(c0E zh*=Mav6FQ4>a{12Qj?qq`BB{t^+t&t(##v43X6IWM-aM(`GRYnd1bJXZ4WiDFXapj zu;L@|vN?_Me;J$E5_S1Y>^Mn_h8sF z=H;2;h3?lHh4Pr|h{j`E^A|Dprw8Jud;~W`0%@M#Q@Tve*s)K)wtnnC@(2kU;aVWb zglxTx-syS~Teq2=)+6B^Wokh~>`ot}nst!${y}>|z4CN~H?L7|lFj8eo4rJDndm!} z*#qgSe>L!vuK^au@|#gq^c~{~iqH0>)x1x=zVZEJLy6c1Z*-I7! zb6&5=qCH;Pkq(U5IAy*MUt+8OMnh7RG~QLDg3o5#@aChN75sH{;U4MLWscj>YR<;% zeiG_UvE6gVMk%78m0xMWS3&hd{o8M7Q`mU*OkbU@7eSo*`OJ%~Oc#GDZ4;bT8ugZq+G6kOH|U!s+X^?qv<%B;_o_b@ zlU;x9Z3_Nw@vSy56>C%^>u9ZJaJk`IbMvW{(FSj8sUOyAv$gwpip@uLUyOXzeA8a9 z0`eh#ccgEc=5SNf>%5}cjrZ7Ad=jm1>w7WCKB(S>;;z-LC6GbV3p_Q_FLBrF)?UY= zTz7W!{&FSb5+)W)R1ZQTXud;a_`%k*L0}DUU1%W@)%A!J z%1ww8J$1>#BT9rB2-8pKnZla~F7le8If{Y?<9r1*z@1bu zODfobD^$#89lUxXR@b29BSPbxe=l$QWt;&io)O=$GXA)?#LDDA!v~o5%kl!7l}MW+ z@meW*lkpAmIt_*~iL-Kv6#6;Jiy)B4*0e>M@2!9o<@z!AA))iG4W^{18Yw7%yTUMJJfsG#v{nlP#6&5n-Ml9 z1?9^(VBVt*{p3K9zew9#`xEX7mbhlJMg;b}4m?aQo#tvBMqli23C)6<`fpHgkeS1G z)(14&%y&p4Uq~u2&bvJjM&Nm-2=I;~v}RVyejW)5CtD;(MHe=PRJcbH+|yb)_kL;O z;eO3*jT9ScFJw0%&v0>^rDW*$02?_ABu)KnxOjau3cXCgdb~cgLIdaVHd0S00X85` zhj~1Whbr%Ls(gV6gKYK$V`t3aNUnl zG#vS-iv*s>w74WeMZ-6t1ouJ@OvX)$YObmhx7bEo{HcJUsd2oHfGW3rM_$s8ju&!< zQaqWgG>1RBId$oJK3-<~BR|SFiqK4+4DdoLIurR8W>R+x8WRaPb=k1Wa&&x!Gjw43U**UyDlJPRq80AS@)vL@T(9T z*=|GeJHpTkc5<*Mf09 z*Qjsa`E*Vt#7Dx8bC^#89T{#I+r!E&39zLucs5CT{z(e0w8Y0Hk$Ms>0X2bTi)fTC z5a*0tB8yHkY*K*qZ<2Lf`G8JCbcZTFRKE|g4VjB&SEQnvxXWL>_E5M(UX ziu;HOLwko-SZ1i>Gu5L*W|I&GX{8QK!Kcbc>kWf&FSFe4ZHLi?Q5iZ+di0*lY zZ&zOAV!-rRa0xaT;mV1s6bmHW4&>M4d00Q6eWU75R|tIs9^wU_SttD=!56c#e&X|L z?yfN9yV6RA=pkVG2aQ{-WEwY_;4@-E(03F8n+M)cbaYw^O{dwbY0ZL>2Bg`Ty#Ak0d^`H)Ul@jcDmsmjwpY21% z#UN$y*Y~Z^z!f0IC>J5W*?_PpbSOy)WSUGyc4Iov**=z~L;6VKUByD1bmIMfFH7(s zq>x$GWs$iYEekhVew5eg3S*)05o&v(CNvZTqY{4J67y|6}coc%Iwn7nauKs6j>R1+=j%519Vw0K zWG9sBWucjJDMa=AH1A7#u*P3d3xlNBiB_f1Neftd(4j9=1mZxn$B6BTorsAq7A2PqjN*hjaOadZxI<6ZF->XDe_(jEkEl zQbtQRg*o`D;jsgmg6cGx#Neswi1*ClUP%-Ea}|ycWz)XhOTr#I~J&d zpGv3n(&6%)lvMO{N)7QI;G%x|3Bm!|R8gRd4DKSKBo-aJVKyW&4D@Ybf--4}O{;&K zturuhubsS;B{uZ%{m}d%wMArtZ-5qfne!wx^f+377DeWlW)^0ezw$ORvSQI)Lnr^M zB+I!jYZ2P_Dxh*e^^kxRmsdd`!Ssw6D*C3d z=DYV^@(DV5LVpj96lz8kh|s5U3I!6@yGe#7H)}AuemsmyXh&~SVB%PHI19JG2s~x7 zs=RSW%YiYxVo|3`H-A7(|2v_o4UeHT;%Bj!M{g9wqI1l(6*`Z4K+|xA9p;xt&avwBbRdV7BZX@F+O=RCH)^1+^2)W_w*F1Yy zY;)d2^97}{50yx9+7}a@tLu+MWs~n}NJ7mF5BBTVJ-;$LGCE2)u2}j4FAj3xrZCbB zO*Iy{^~r$FH+@d|IsChOXp-I>u+U8_)5Xp6q3hy^w5kdDh=1 z6}7{e{j^IXz(SV4nV>&=78}_&tuA68);xdDhCEnJRcuT$YT2h_eX6c7(52rDUHdE) zS|w2C`;{%i!iYu4;>C|QYr(vFe8nc8)XuE@(Aqi}Wy8H|3coiJkaAyv(VaMczFVQu z_uxH-nF&m4(P{p)q+ddvb4BEND}$BT@(VJhzDICi>7;BX_cVs)MiTgscsL?8axY26 z_!$rdg5PbgETNoGzxy<54t(c+@#W>fOP}}+VBV06u)!AAB|@2R&vHC>iFg1H{j7|& z6xmABqzKLR!?&B7<9g0uqX=Xu0WrKjTLBrtJ`gzmOpAPvN8=~8p@G#+pqciliwt#5 zNYrClYmLEet4@RN-J;Em3#T8@yl=IxGJ`adUyHi0QgWmI^~ZW?mM1AKqQh*wB0`yl zi&Fvx>Bs{uni4k})p1VYricP{W1uY|J7oXi7@xshxeW0(_ZPYe;bM-WZg%py_YmDC zumOK*4~vO>mRUIT6xp9-kDsEEx!-m!6d|kb2!D|!NmF9_Hr9vw8TY4WQBhwWrwDu| zfTYu|1NEW87kg@H5A5!0Rm?e?(Zl;(V z)F1lNxi@WM|66HTKnPX!>)ovOc;Z8R8y!)hl}!Cw_+8)jlUzvrtwmJitv7lp0-*%` zxf)w(^j`G`1;F-CtmWjym%(E_Y~wcaU^Z8gFaIwF^{)q5m*#G~kI}Q;!YS5x2yo}k zG$jPHz+OL@kitVC+aI>{cYpz539`)Yq>omOO3cRUyfSmX{pV&hBH-G z4TUt)XI-j5>qLkg+vkYZ+4y+vYR^#*tCl=BWrn^9%kgl7H6{3l-7I6OM!@oB;qaEb z?;`ro9b!Qc(`@Epi-fGtMC6ek)Q<{g>iqijiSDkD5|o7#)i6u5`z&uoCKTBgf?jqX z7+2CcIx=E2Qiak0C6{}%lH}cBaS=#*MYvlun~ToxqA7cW{UPB{^fI;deD=}js-L`C z$K(-oY3cjxgSv?+>omw|BFt;ePre=}0=@5&^u|SjRzAf@-%6m4fhj}V!-1ND^Q%gb zd}Kb5^YFEFV*)`5*u3=se9Pjh1KdlmjM)4UBJ+^3s_r4YG+1 zXnJ5-n?3S6^gS`q7<8XMElj+M2B|?3K?jEb(eOCU;A6MnG)x;7qKvVDbG~R2(c`ZS zH3~uNSUoI{FmU40Oi#No9HD&4K=ejh#+%9@g^ze!@=@RPw|85{xKjqPmb;!UpR0L} z!jz#?q1bveP5#hj3Phr#!U|m^Qk)Qx^WhhDn`y9^e_BH9d1SEFP6Zk3Do-fn)*r6b zH$>>yXeE2>Zmd8B{)c?M!jlposw%Ss6y72)EvV!+a(Q zmc`XM75JLBf(%#6_Ocl!R-Vv8b4!*qAFmXo03$p1Zx(1Cg1cX7fufk1_$8;#{ zq>-DaRJSl7Gy%ehHQwh;Myr~n4Ty4g$$6EH+g(sJ0}w)1`yx^)x9R?3L#V))A${Z( z2?a2R+QCZf^->K+8NXsWr;8-^$X8Gl)K2cKlClyTk!I?>)E zyC9?rb;ZMe?v@=fP&II60zNBr%Qi>!8o1uZrhk$xuGy7~L@gRq!5D!Z3L*qy0rdf$Z`q%tq~LcDYr@-#a4s?B&nHnPX2w#QMDhVEIo_97S?)ewi!z)j zDoH3`d7a}EBt0q7HQALM)U`d@SrQD1qJyBIP@3#wwiei zg99tz{Y(zB$~l_dPl00gx?F+{Oc7qWk6Q6b1iARM{Db;Ps6jwzrmoXfXr{lU?Lj{M zMDhg>k@Lt)b{N)teJ%h-(mY4{^^)U5SdAjQ_;zalooeeD=|HKWFG?e6z6*9yy3jc| zT?id;i;ih~N1w);dH1LAT8imG3rcW=E9Rb`!!Y@~bn-GDK{aN;1#!N;Ufrtol!i+5 zz@VafWiv(T4I)vT*o6+Z!uEjakb)B#)&eYajdu609U}9|uo=rQa_)cd#uy|>0|Ohg z-}Z<*kNlQ3;ThnhD%#ye=8V&`1iEBkxf)Pzd5~#s1nncyz(aJ~u78|<~5?!SNp!vX_^H?KBWLPf{1mbb0k6VRm-zCt=fq;VQEtJHzja^Zw zMJ}LV?8-%!6+N`O@17hfkm=KM>R_v+VR|j%Sr}0BQ$U&n3`*Ak17!e__@(ze%N-^g z!tf2u$pUn~$=AjA*$X(EQRj*h+--l~95!O?LTQNS3E2JaTUnC_1MVM$T_-;QuisF- zzd&TiFb39E@EQ=J@qy33>!I(ed*8$J-MWTLocE%Zuki*UP>CE;1 ztuVyu4RIY?DrkIbq8{Hh8INl0dq=A@?s>IWjA~zPcCtSM%^Gx zz+5x#j_~+TA9S3%#`GB9Uz+PjxXlq;( zD1=5!d?Z7I2enXgPM_?MHSIuu7#=Tq$UJc*F!8BO57pNB9ZKkO=g2RUZkGtH=`k@w{9q^=&f9R|)1 z+j~RSh9~=FEUeqFtBt;~0H2W*Nyis_-raoG zxAfX`LvL}nH)X0s(Pmips3U_QX3#K8ChnX)JWbYIP=HFCrmpXUy&zLfpE(?Fpe0`| zM*CZc$_a)9Ui9Oc=XF63|92J%>SE8@rU&h2PZ?Wg_l;$^`VSpKB7~}d6DwFBcjUn} zouz)scQDNSPpT7A_bFy{U;hhfuRiX8f^9NF7jC)OMdICnjAf~2)Fdv`($MrcIcsWG&rpOK0{3;MCa6tjJ3`8?grE#X=o=Cxi zDAWfxd$93p*~s*0t)nKn@j%`92jx`tw7u(9jGlH<{c-dtEzOo6@;-dCqfYGG9UtR~}O4siPGae>i_|Vm3_javGMf z5ZsXLV&+6Bw9zT`spr1CSPd;6+^QQ~d}7-yHTWP%!JAC*=V@V4t2nDY{m$Qq&z7IF zV&?TmBigmC?8>vNTP8u^Kl?f8u=z(cmvwIz>(`IS-{q^AyE*8b=ScG#WRMEn`%n1( zUj*gw5(iU$P}($FRA<{(q`JCHtfsvm9}v5olB{t<;hEWP=|DK%@s=Rll+;#F!8`u z6BFsc#iJK(hT|OFl^4Pw-+OhkhKBl*b>m+zlV6;U+`Q`W-hrIIVb4wV;`tjhn^Y)* z7{6#^fK}f@eNb@kesh)Y+#I7kYC;eKhTW+WpML#N-N}B;=$&0`>CbDERQAcgF2{d; zt#y(vHFwzg?1gZuL)#bpxhxS5`W=4KS@xph#ZhfL2}l>y-^1K}$7el!+uTZ2^ai3i z@FX$0i}Zek)MCUqbMZXYM45AzLI2suAHfcJb(FD({B8Ilw{sk)cjh%?j2KW%-~k1* z`tsZnx02ejh_-8uBB)8t`W$V$M;QBih$&kRHf2z{_zhU_u@3RtI_vj59)#atzRn#e zpq(x~UE~xJ;5?Ig=WJU4xOF~w6g&{y`xOKdA{7>rJ@2Stzgfbo+P^6# z^!PP;_Rwj6SK+&KeQc)m8EElyY=q6HzPta$GDM;8Jvr{$3rGJ^c$VjS|L0XcOPBlZ zRQg8}5T!LQDs4;jT3fbGth1xfFzJ9fCdunuu$B)sQWkLuH)wKhn)u@Tx9p-d$8_uZ z`cQBeM@_J1J#-1e1=0dsrd_jTW1uRZR(eiF8%dqh{n@n4yO&-%jBZ@$KR*_u(}V@$he3q3@3ddPoN<2FrdSgzekacK(7uVTIP^ z4xt~tA5R92&5eC&-pMz*2MQTwoDBHkb3d$7E7GP(%9jD48n~I62?F*p$f6*-$3B`z z_5~Y6^%KJRr>>|;D2VE6UXnTUWD1gUM(m&V(wUprFU6tP#?`F9hF+Z6+ z89=UK>iGAb{3wf9yHo5LWA*0bgTV`xK;5HaBTHvC7lnZ8rZtVun#i@u^SV4IVDq{X zrV$|uJUjmzZm6e2mQ`Syhi=u*1zGb7HOrd{ehixI&p>Y-Qy`_b#R96M=M$YAoa?pS z@&+2>kAO#+(dq+{@xUzsU5~(9!;BM>>2XUmOH; z(5UJ}r7&XZ!8Ut9FZ#klP-sq@LagrMn7w1?ydnpEke-|I*)UyI>TK1H0oxl_L$H~| zbE;II!HxW8spOPYk1RTvLW>h}jL}Gp!C=@G)va_M>q%`6XU*x*962UF!FDoum3pN+jP{c^lECpN(F z>eX$bkYcwtX!E!d&p9%u_h(S$nWyf$(s6dOVYOowrO-E()W-GX)zWzZDO8Ixv%Esm z)%NL+rKnRk*Ae8Rllp)B;3sLvj_eZwC+k)l03{I>L9@9{Hu1TN+V6d9O8r8M24h`vfkPLt~jK4dZf67d5GFNTfwOhpO5+d{}9V_CW#m%Ipov~p7fOO3C z$EHDw`R$?EmX<*-KoSSPBN@2(-g4?kw0mspY;L|zg{1=aBWoL*2GI#x?Vpkv>v+p& zETq=gCbK@GTgW=TssSjKbpFS$ix(sILa3leKREeQ)9`$mKSmzgcxCKJ_fQtad_-qX zq651e4(iP6*JCGyTIjINe>Viz8>WptaH+J=TNf587L%c$&>a|X{N+f0cXutFMy3 zbhqCr!JS)~27r#ZrcmXB+;#GS@p}BirKZt?2O6@G;DJ8}43;ZPVPgNc3HYfalyZ-? zN+&-cEse?9$}`-~n^jv!#7>98zban3V-AZLV74|4)1Ae8up-RZHnAR{6f$L_V@p!d zD|StJJNE!BZ{7a5&3mYN6^LEd&YDH_CER_snVMApm44PSa9keP`Rz6or0VgT z!zh#J0Yz`=&LiPuZ|VEuZnW=iPkd_+elvzmK6Q$n&Airrup6GY&}MF(@aH{FBfaoV zEkB59Y-_PMRBC)3hZRVJ?kvdfci%bB(qpMS8nG@F!YO*6e;R(8xl`N}FX+@5*=5QZ zx~$y7F~BuLAl6@eem}3ZP^Xs*o0j)^$SFg3TSylJ9zsPmq)5cb0(2lRz`dvR9eK2hB z^v}#rI<|)*>48pOObU^!E8Q^|UTmu|yl)DSCg0i8UR3g6uQj=|9v=rx(d4(@#^p;0 zl$8blK(m%zE5Kb>Usq+b<$rI55x|y~xNeqXHJf^mSrdD!vL>TY#^K`+>R41m2+;wd z!*%zkBJ9?FqGAf|Mb9<|B_}blphN=HSc^t2+Su@2Qu5pB=b<0N!q8RtX776>Lrudq zyqfi83~w%J@@kC$mq81?7kZIYyyjfP`82cLaODdHk2ql{F%TRI>7| zb*^H{oZU!4ge9?{7_s2BhF|<e6Q$9|000XV65;`nA`jqJAvL^tl2Y#WLS@yvEdFL7`G%_l;;Va*c8@UjkN~^!hRxa=Sy;WW{p}u`V+tt;04vsgPlaVgI+JP68GIDlj2NHp zCmE=JG?V%ROEmzDPL^d^Y&vhkPE&Lb7z-;bN80?)kp#{Z8eE6PW&j=Adzl&2Pt9!D zsPsQVwkjGLgq=q|qF!u2>?iZj55|pNQU1dz=)imQr)*Q3mB%DCiMnKWN^b^+RUAYR z+>5mf2u#`W57>!!f_644T}@ma`}EK!Z@Y%BEKva7nHl#&1}eV z+P3H>!~^F}6D7mOf7k`$p|ZcYQZCobQb4#;44X(|T&aM3KPWNI7F&MVQ(VxmgjbM+ zVWTjfya_^r3mB12q!_MvNq*TETp_El%GcqzLPT?&_&Do@01y#@OCoH8P~+-RcoLCD z;*#*2AauBCrg3>ZYj&dnXZQTx-nVNwbCvjJ^7y!X?$L1xT#{($`uG#hP@Vi)*s(VQ zlxyw=OX)m*lRpKHRcJvcmw4GwP9ZNTM#qzIw1TLp&}5VT(7F=kJ_T8mKRvKgHHj3w ze=GX$qjw%}x`FFv@>q@pkdL?CN7_-=jAAi`catAY(>ebrs^v?xL_+_w+XpZOck%&3 z6C9=xj)pvLr-D5k9e=mRAMqC|+ zj{yz>=e0cG_E&J@O4%j%m^1Y0gBHrps@_FhQZSKGC(e-OO8PgF$AU0yr-R?a>Y5l6=A|Er(U{$ys1Vo)Z38J_&~{NQS;7FB5tM zd18e^bD~~|%~Hf@(LApbOgS(87n?xpNjLyZY`zO!W12h4emx9l5UdJ{np}? z5>j-|ZuFd-4-?}=X^+wgRaeb@^X(#-WO)cjD8X6s1%Cr;&er~QQD-0IUWX zEgyp+ETuakJDfiO{wFpyj$xvQ;@A<|e$A7x8F0vx$)NS#^VI#gaQ0>Sy~>T9*NVU> z131cmBvI$mXRjA;-34KZyjf%6qaNV-cxoUi^W3nLi-KP-Ep&aU%ti)rcHLkmeHxH` z0N~{bfm!EmAn)`JPdJoU-SoV~yj=#2V7q3~sYJn-psy@bL}r>(gO+bHCI+a4B28a{ zrf}Ev2VeuiG2i|j>N_c|CO@E34x)f2+7|frEJ@MOH9I`IIuoNZR`#Ya+KZq$gE zju}i3|3#(EBxwg1->qK{Bs5fFjQ0k=N3tRuk{FGN?E)f?Pt$hR5&=z<>UH5)=6-f3 z4zZqy9LEzyKpQ0P?Id_R0>9h%YqZwvKHXl|eQh|IsgfRWw@WWN<26c}=wLLi9z6o! z7=$8kgTb5@g0CPxc>+aoF*?mPeTg;XhfGe-t4~+1U>*k(Dc>7XM6V_ z1flJJAqX_RJj>cHR29pcawrO>8u8DM8%qngh_-S$0ahT*O)T_JH_%m27q@1~>#KNJ zE`e=>S}jEwMVfj`$O8VGL}b+zpRO%r0gEJv2)(Ra)F0+c5RthOG5UaB4Ji<8$nKJu zW2;*A`iM=j2JW>XyHHv|*1E6xC-o{mTN*#=RNQcrgj>%TpYZ+E_uyqto9+NtI_YIa z)_8%0F&pVh<76uBj#{)3ADC2K$upi`@As_~^>-<>uaD96N`_Qd|!*G@2IMdSsy?_yvBHsV|?3)?Cd{*-150 zkAZA}m~lw3A-YglNTH(82K~B7jpq*@%FA~#+(!Qo+O zK=bbzBhcT0AUe8-Ig~Fk0B#}eem69>LNo7`O>FCM!raNu$^L}It*W`P5UNA4fJ$f4S#C(C^;}? zv@WKmMs)#pu@z}!ic<`BC(WNsF^;x`oy)vc+*QhW`>bh~0mkQbp4oJf=TKU292lrCi)#2%U4dhk_22o_B%U5dKKd@=zWw28bA)6iCy5}bQar;ex&NXQg!b?M zBX@1&HnHQIbsit-`w#qB=-9N0qE?OmfZWz_cZjzEG}fZHD5p1o+T~6SB?2&){}WKC zkOW>pQe@nccQ{SEUnjwWe#vAi_Rz{bJ2E6`lOi(fl(~bdIjyyF*UX>&EHwX{ghFCs zMD1Y=Y`OrVYYR@6H|oyX;bgV`519Z7CE+agEfoBnQQLVio^}bp1XlXO+id!maCXO} zM8NrfF_f1wMUWAIKq>K(nr<$mKF}*gKe~ergY)PDY{q@qgWJokV%G?`aP&d9^R*mU z$}_G|$P|av_&H-(LNs6}5b+O`Ku`U=0gA4r!;omP1JexM{l05!W{{?2>yEmYG=>Y+ zvC0X|F3Y%ohy)}2R+r~>!om@B>0|(GV;MRFT{{Dw0<*tiGlVlYV3uF7IFPRQt<5MV zI1$`v-P*ITV#{1_uj-d{LKBvhdG&(2so}8{B2)4BIRekBRAZiDgMylYz6pQiKDgT; zWy6X+^-F3{uwYX}C%_>{R(8DofRsxDxH!>KPE?tiA-q02D1kme(lZSy_k&+A1K4Fc z9y@xI!{*8$x=64A#IEOjr5EprySn(idUiBb9vi!cDm0guro>V|)DM39sSoxxoAGUeO|iVK%FOdG17Ev03M+aP8MwX?8-C#QwREe zXtEQ@3SCnd2tDXlv$1zT1=b-)jvOO*f8K`nSOjr{lck&|DFOz5qoaEN<_kpoZUka5 zkQtHwIRiFzUcL1rSsCVkXgWpweUbkH5ZHL^`HHGy%)WPbVc&+9y6bzJ(-aJm( zI3eig;IVeHf9&NANxrvPj0VF92B_tX9dMHo}Ilikb(WI2JHZ-o0DfJ3NYpfBxG8==pVU0Q9=YfuYlRGO>4 z9&N=#u)^1s&R{?kE7@Pq)=je!KpRx1%h6qB=hvf?*q~ncTZNef((651TqM) z%4go99-0iVp+^3}D*Nz#eoB2jC?O$`qu=s5l2;Mh2}otpG%nQ3wyBRM9z%%%6YoA{ zp&H)s2^Sz;vG!j{m%0Iy#2;(?Q`X;CBc@s!#T#jL>ZHg{h=CU$pcWeTW@$we zb!|ywMPj#>D2P2SAv9hmIi+Vy&16Wg_X&eF`tjFz%=20FOb@0%pYu)oBw z<;4ToVCc!BB^imz(i{H;G;K=na&S|!1fqg%3;-u+6QXnUp!%FBF*UoZRwo`PHH54L z;zb3cf^b=!Ize?|ks#bZ@hfd)|6NriTIVF?9L=(03U=WgXoc zgaa@GDc~$)6C;Ej!u-TSUV+h^S#EyyyOJI>6(AbEdnePSx~SK_Stsgpc+i_Acb-$9 zuLt!8vLv^>($H#k7QdsARY3Fr@~)RIfE~1YaT!VMRAv1$-ofh`Sfi0Fio(a5YUK$`*fLB^^r{{N~h$lXpVH#*u zKeub^K{JGS69!tl0gB$(M~59gI7Z79?+M+oTqwlH z#^DEEwm>XetyiXG>&75;3H5RRHDX07L}C)p=9T>G5d!^{8_S=s5#xPj3hUGxPl9Jq zN;bZOE|o13P|;7xq>j%?g*E`Z6uTg@$8bqT6xCaN6X`Gh&#ts*d=oEX1%+nMEiT#m zp+&9cGxyLc@;C^zboUl&V;zFTCzV#{T{CUTOGC#(I|+aQl-Lh~O}vA^L4fBUws0gj zmXN@JnIOZy8&xEV3=;+%P0`!@m-T}YA)soz$uRG}J4|1Kd?y72=a0T=nf>V`L0Di# zuZ48**+ar8Y+dTzsxQpYPB6Car{?t857&r`fx2h%^L$0pF@lPmKt@o!&-$(5cTg%? zpc57=IWDP%kOr*!ys(_}49Np&AOk+zwry z4X{>k&mJU^74uD=POA_k%LNL__90i-gF>RN;s`#*cTk$Kz&hs@ zu$+@l`REs*zyot9OfUKmof;f`3-RUf0W1MTEJ)CQ>y}!Y82PPOX!k3+qHBO!E$xPw z%numRcw8TVn7-;VNT4U)mh)N&dyBj%wo9e(Z~JtvBO8d^>Oq14nq!zJr7k<~u74Wu zec6!HZ}^L{0B_T@y_!*7zdpw|{r$sbns0$PNASbTtS*Maw7DnS{Rpa9Ht z&3%fBM87;rBPAwarVD+NEmBVfbi908z+3D46g)|1YWJ+R^*Di|)xLRm`roFB(b19s zZ9i)n@wL$g+I|hJVm8dXh4xC&Jw2cn|Ni~_G#lXph%ay~B(marwVtj5y>>&9KtNoAH6^T<`olndQcO=$MLa`y#okmhW)XYX&4k`jAxC-2LO~{4lfqY7ZM1B z<&CR03IN3jkpg=5sVTvgu0xt?qW0&s+VO12IGx23p$#D4^N$Y}_7RVxrvD-_S1G+! ztQaX{5B}Iga72>j-Se{bix_8iSYJ$tQLbI-fAWV9NOcgVD@b-i zqX-UU|ACffZJ!nrP#6-CMxXbB*9)E_X4`k~7Z58{H@Ib2{FI^2j*mn~A z3LNovpO0K-U!I;N34HPAj@H%e* zOa0k7n*|c!)b#Q@9u&eXSMt||eBfvv(+uEOohVFJ2ZlQmEAv(E?7U6_8+`yyp2ar> zdwF4p(coG1aFh7$^vXLP%%}3#MeE;o z4mf6_RqmviL3yncThaKbeoBe-StWQeKO^bTY8D23m=qcLG0{HWbO{Tmb;+FoGIQti z;5em$njA*9n!X7b49*op+fR8nQt<05V{kzAgYTk!HVz%!f~RxexBx3p!~SjHu+jGc z2tW&(Oq(rLN5xqXE~S+4BH>RYg4V(|E2_Jg6uStNHd~Ea%r-buNFTfug~rhRFmb{V z{2NXXpg}-Ge?1*@dcb3u=G%5Ko!OHq60BA8q+Rqbw z5yn=q&0H0*hkWlxxJ!M{v(KKnI5l6T?GK9FY@qJ5dGZMGXBTL(e;43eeyyX46T~{h z6YRnnqjb9S)UEFZ{9HHSb)=xaS9KzC@kaRrm`7h#{`k>k4~DMv{3=}I&op`Xj?Z4> z^q^l;S?f|i8~smW*oU1;#z?_0*yk$A@6S1g&_4S=%8r_1s|*Wf{$vb;1u;wql2r?h z<#r|F3OYx783d#RxjVMgAKcz|=@ha6CJb$X-w{KOax6HdQoFt_ey-rRc&oZeSmJl` zl(YI`!onN2E|T+$n|76CQJIml3S${}nWNCsw316MUq=~c{~*5c)vy1a%TfOS3kY;P a`OT4&QP9Xk_j4H=?&>N!4=a={!~Q>DxJ29l diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index a64b004da..a45975d3a 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2208,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2117,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2129,"get_count":3,"put_bytes":121,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2335,"put_count":7}} +{"i":0,"series":2,"stats":{"get_bytes":2244,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":1,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":2,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":2,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":3,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":3,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":4,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":4,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":5,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":5,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":6,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":6,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":7,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":7,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":8,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":8,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":9,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_one_vs_all.png b/actors/evm/tests/measurements/inc_one_vs_all.png index d59d47c6e6813b5f665a312a963802f83da120f2..6d395dc339c31c6fe8b1bee144bcbbfe9b483ee1 100644 GIT binary patch literal 14022 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 literal 14527 zcmd6Oc|4R|__sCLl4K95D0>NGDO(ANEMY9eNcJt+cT<*REg?I}I)kw@7!#thW-Qr5 z_I;UznD@TxdHOw1&*yzVzdv6e#hm*(*E#3f&-J}UXltp`({Rv`kdV-;tKHNkA)!E% zkdU7}P7aJv$U9snAt9C5zH?87NF)LySy@@&4;%?eFcC*WBw1U-DU!T@PoiWuMZ}#Y z30A}f6EDMqBS}aUgGq?N#2{jSuAm?=Rq;zNFpq$n`19O`{>B#~ac%rDvHET4+8S|)1Xzva0q}p&xyW;(k%m7% zN3yd&1hzs#(iPslOag9^NYed@csiaKq`eUwOjIG+4}P?m=({l&)mYJzjfBf*;QskJDDvcMuCcO%i}(nDvl|!x4e= z5OG~K85_intm>|}#J62Iv_DZXi+C1`#q#j*n46ntW@a`uH7O`4OioUcWVjhrF3x3= zw6wIaB?$tOB%yw&X5>afa-tRdN17mXf|G>gEQ$I}rF-6KtK-y(ZwffVrr=y_`Gar0 zw|0&oy^p6)$J6|{(%91ov!hctez3uOZhDps;Pij_`npg8t6MHFHoVQr3OOiledT&% zc0ONi${`q0p6TWC}1PMVyUpTg1b z?$dtv)2yA}Gp?Xz6M|yQfArQPBpU^|B`~H?U-Xd-NuUm$@Hi!_Ws5Bvo?92V$hO^^`H z%#0Rmr-B5Y2@pL;o#muRQe1TS@B$&gHDoq&Tzj6PWxpqlew{ z)Tr+zpYtQxC7Bb+giv97_^7`UkQ`MHgHToYly(pD?f|dtm;>%t?`9A3;)nz z5L#FCuj?tOyWDgLY_IPeTGZNyMXBWn>Lt*Ia25GdV8=jc@KIGR=W@K5l8108h(&qa3GW?Go3GZdCOBq z;p0-^s6YT0w8aXttl`!diL400`=t#!3ku6aiD&+6gN~g^VXro2KQFR(jGR-vibR&@ zwN?AFnA|cNdiUnh2m`{Z(qoav6uE*n77bp?3uDoYb|MIuyty132{o1z{hs(pUNlys zks_!t+s{GfRyz zmdaVt+1a_pDwe6hB%g@tGf*>nFPn%_1deQkjl;Oo(Q&(?wy=Y6mFHBO-!vms(bC!U zw(I!kF+$40t4;_zG|FPwJBQ^2HKMxBq}GWe)yin8o`s1u4EK@Q`}#r&$!Ta+gNm-m zvGJrH1IDB}?nGQS@^wV(o9Oj)_ijW`v9w#)*emIe`P!I=o~D?GZ@Dr#jHB`k%QSlipRd1v=`2~8R4Ux| zp@ghsjJUTsx|P;dbp35Wo(b*z(fv&9+(fR7KTR&JO?xga>)2*tcJ9$VXGgbfq_S8{ z95;ml@rEWFLw;1O(HGIZl1XQuFK!o3LMEPz4SS8K%K9-u-_*-aKG= z9dQPi>jFf}pyZzlsP&$znT?~V_k{+y*VV?~U=X_5;&r#8gJ%bQ#Pw-<-8a`RfMqB zS_NL{%00-(qbyq-?gQOa_mgCB0B9?=(MPc3!u}CKL+X;g>Ts@Y_wFTo-iTfKa~IIT zRBOldzkNe6XOwm|d@R^qgI6J4wrF@(0+Z}##>qPbi4Em zFL?$tZZmP_h3t>dS{)Z|D3w)8h@cw84p>Eb@^**tU_CoaL2k-VvS1Cy(7orl{>vfB$Ic&*k;%!?6;k{tcJP46;s9g@^VD=G-W1CM0? z(hMJ^WEr$>$f62-mV2~&*6OH_XqFooKr{&;HOtjZvcC~X=g_kxUYGPB-z0jR0ae&u z?d6l))HF1!f4I+Ix?z7xDd7XvV8(zI$2auRvCyChy#3H_Qi31>xBwgCrSrL#JW1 z#dE`xJI_Q1+Jr>AhRZOW5=6ludMuHn!j=YAInl|tTi_`swykeNO@uj-&^O2zlzTOO z_X#IZ?3t%59<8^rX!mfs7fNBzq6ZlX6HMdCL5NSN!Gvf*IiRY6f&qr&!HxAfe(f)Z zn0uU)C!v`kxMmaI-K>wtg^dV+>Xa#6CUZy5q95oZ*Mq#O5ioh-0&y}8gW~-ouU*oO z_xT=$QUGZJsAZi@u6yhVKBQIv(Qo58bZQ}prHq3mkiGr~?||NE7Eb)gDUDfzrWx-+Hn4Zp?Y77gRs?gVP8(9+ zmU(D*Ab&ewzCl$jd*l$G~Z4jx< z<1zE3!)8zDQ!d65|8Pzq?)s<#+OX$-hiISOanxe+loWf_3F;5Xbye-_)y|5yOl-Dw zqV=WTWHd|ApMuz3o!GO%c-#%-F9^&T$vP;$dnI#4Oex7eQ=gos@P^gShVAu(PYrt` zEu}N}u$FhR9z8$qV3c~dwE8I#9@`b2R6p>H_rk|)6$786uezVJmX$yU+b*s+;C-45Y2)=t26Bge{Pr=nN_rKi<~2$UW9=<2^@U@AK$CQ zAD>`{6+Lsl3pc$m@Tj-(e5rk*l*P`}wzt6JqI7+74vgMjUyZvh+g*0!t)i47gi5@( zn0w)f3FK@he@qH*WnM%MzE$nC95d7%J#j{f&)QARwk`6Zu9!oEgiXe=i2F8V%g#5l zLL1m%g07}NgqLpy#2c=MXZbRdX$)@eV>H0VcwiZXA&KaA*4_HceFh3Gt9G6b0b4&=cKD>7FF9_dWV8PBXsLLbvm z1KaQkShsuGgnziGYY~cjq0fg7%-%b7uNL^7P>Bs{e4>&>6MGU`o-wdn61JaEoSfBs z0@%hy!r>Di;1Lz@hJk8*l;d0vuW`lP zla9p++Pw6D-4V<`JPE~LH5l#{fHdvu1O)k+G2Xj$3!b16ATV>_U7o~?&a=sjUSTDV z6lE^rMGG#v?~8Mvis=dIuPcwd9jHC&$GI~v;s2PXiV>RN9gh_zLk6`K_PW%*yE1jS zfAqTHwMMAHag;3Pv}OnHSr1(R5`7WvS+nL9L;9E|nh~n+Zhwtgn$p16Itae%@^zh! z%DwPx2(IYuo#!vf9lWh~d8hK{n3X7vfa63)(E4x>ZgfT~?K;Am^C?+if(<|@=@#|V z9$#YT3ZFj2+tE{T97SGzoYkzs2%W8T)tStGq~+mi>1#dN`ibGa5aymYErr1q2=~{} zNFKOGp>(AhoQhy?KJK8bN3V58PAnGeWpYYvzB=x6067*$Me{ z)uqXhMk(}emAxXbM?n-fH%x3hDbx1MD+ z*L*&f=yr>Nslt$T?G!ZO=`rBQ{;5uIp9I$1Mk3M5aQ$-BdW%wdP?zvyn#slexZXvJ z)jMR!P?Z1(c-ZfcAZ_Rsv^Rfvh)5?P;F>F(U=?UL?=0qCrG% zlp(VB96Fec!hmnwd$LWFp(8fOg#G8HmKI)eNev<-u~JRz-arV%0S4cE3F16{T)3vz zPUGA-X?eWag?Ru z^|kTc7gwnB zMDt4uDoOyUv_`EekHO|gPBL2W;y!-1s3G;}bRQF}+a?*OP@tNc8L7s*@Q@LA^@K(r zLoETWSen)C8hLQ$vwTVaUE6~TA=*~u!)2gm68dm`yL0>jtjL)QlzK-=e1PC`5roX*KrWZ)g^`C0Q5Ns&GF|PTh>9+1gW@yZHYMgn`x@)Hx`(Tf3W2F0PRCX~$<(7?7mj?i~WZYBq{Z35wlQ*eDZ zmL&GY&?Q-!jP?>5Ow<9dd8V*F8`u%?(4ayr(Gz^dp)DUGgwtTn@?7 z9%R3}zV)4PYSO%d;WoZ}*mbd7!Moi>B5M{_827z?H&glzQNL)ssXGTjfJis!m5Qcv zC5axK_qyg0Rl=cdpST8xQ|=N-ZC-B8I#|GBcFp+C-IH+LdCz8$k2x~w+`VYfiF#s4 zYAW+6aijIs!FBl)E%QO&+F+*^ko)J9Mb)o3iBK4)*JK`h# zz}U5RiAGqVi&dntbuE1_baY*<5ecOnG}G0dE!`FZ0|@Xubtq^(MudWxAe)Z;8tU0M z6enxwsq+C`!*S)qrAebvJJGIWOezw`!Q$c(Y1zyLdhzb*6Nx5Vr58{Y{u?&SZK0k9 z2+5sdG6ZG6j=!f^h*-u|%%r>a9XRQ#|E4>Iy7k95(>Ij2Xlf&IMHW}wriQcpJZpga z5BM1;TCd%K!@NoD+rNS500Ofp8IsJ&^z_g%x1wdp)({hu>Ib1O@?^*~&e$zw(}U|y zUrR5FcJZQv4}J%10tySz0MYs1p*kK62&%uS{tD}{Kp{rPeeUmo{@0Z2U<&Y#`W^fM z0RWkRPyjNf{*&<&kg?65jK?MbueDPIY9$l-he-ODV88%^08kxaE6j!z)Vw4zq_m^J z5WuOu1WVq;W(w3c7&&Hr9><+44W4(F$kNIeYjGNslXS^C18ArAQ*ax$KTgje)b56R zJN7g<CmXS-^Ba-$FKd~gUzySE7}8-Yv*B8Z zcT3L39qO3H1}q4bJPYCG53?9d`1CRVRf?4Mk|$91<3#ClJATH)L_mv|6yf@pOI?X~4kd({Ioo-4&)D*W(EpD0r>q&4%S zWsBWwfChSo3N@IFP6+3qmiu36!icy6`LZa_w*se-U_eBlK=sFp2l@-hZ&;6AHumnp z;-Mgew&*J;z?ykIOZJ^v5c*7@?J`2k^gKQwtn?R{I$>$$<#OM00lDEk;2r@t>BVwm z^!*>aynh@SlMSBhB@(xkSMpLv${oXil33`rYZe5FrO^CSF~5 z`OeQwRfktStF8b?V7gZwh%xy(un~f--AU_EefBwp|B3W!IIg_RjRWa4@?IA_wahZE zyrbG`#&gmwE1b|O!QMJz@x>+3TJr7%Pz1k*c`m9)cHM3>58_TkTc+HCE?sxJ;}S`@ZAN<0HOo*n z1DNNqpi3A>+FAPY{ItNLSNu;-LKSaN7|?G&h~o+m0b~Zq>FxdLX1tPD?$P)wEiyO* zhgRL}gE&{?8)p!^fn`@3wHRo@3l~K581>;5`25&RL=G^I1EKGjK{vhKmZRYY#7~Le z79p@j1Cak+&i;yMDgoq_wuYAgi$(^;e+USknt5@(J2A=<&mE=2Ahqv;KP zpfjmR$`L2L#Cb`P{KfxwlLQwk`(R_}!?3QN+9Ral^W@2qN~{S>cZ>#Wcg*@Fe|z6O zrOBno93hDZn;)XeOY{P&jI3_t?+Y&P+uRx3Xd|#lw*R96o?<3Zh50;}_DRHRCMWkP zB(UsxQE%#IGM{`X*m~{@rCs~pKi#KMH=x4H&$_m}164JM=1f0_p2+&yLto@tPc@UM z!CcB`TU&@9&&{#UH`OY6__O)6DezJ>4Je#UecRi$2F8{|-O(9K`@ZFc;+Fck-Gz&p zeSxk~T)(Nj+v~4kXY`Lh+A^wPtMMUf1(;+XTz zs*WWRT&hHWb3-Y4Myk>98M|q2+qa_I_Vyl-?ruw^{NZfpE3W+u(yk&q0VPMd16gvx z_IY`%?GbLs`pY4NIL?D23s<`sAT^MRWM-0T50CBiMHG)M?Z=Qgod7YN;B(cBafx=2 zh9={-nH-uX(@N)i8w1lyk}{Jh-I0=}z8^WwDvM}^8VldoU}u(k*+3uZ8RV{SJR;g zG~%{Su4NXSPUOh(CdbY1Z*+2Z>;{Cw`v9rDI(I?r;xH8;1H@ zjS-)P9DOg1E|BoOiI1gc2uwMY!e{v*$wvqnP1}`t25OVcR6Bf6&1PSDHDWep<@-Zd z_0;klYx2UD7>Lk>@Q{%6o22kC*Bc8rtZ;`&4$kfKX|A?NY$)5y>v=i@c-X&@4jl6&3LjId5eK zBvb0Hip+d^e3waeG@a35U?o+Q`|Ex)=Lt*=XWWh*9rBZlTiIi}3zZEL?KSli&616Y z=U-JP%}FfhOTexbOy$Zr4Vl~d+I3exru#3F_x~6PjvZKvStuK`8a5+Tdb#5BZ6253 zif&8A<{u2^?~>poIY(alF+)cP4Ual-G>3WtiZYOu5WTk0sq2HnyRLgAB=+?uP*ZkI zbf}%qv|HBRv5T3Ijwo>omf!*gW+%B~|CO&h=2cy#tyEWB*BY+1Y7uQF`E>CXrk7{@ zfEMSy!1@gAav*%KaO3Fmr`htpCxlqijrjZUMbX+Ghy;x>31Hl+9EvulXptD~y#jyzIRS_~R@Q)hH?8=0Vs z1nko~6o@nDZT*EaUv`uwEz z8EwAZZ~w0$!g%C@GW{E#;oDg=-RTxXE1Dm7_%c+l+(kv zxD|R?{kX6Gf^c>FkeZ~jcZnroQb?3K&Os!K_I(#83CB5f%rgfOff+nJu|s1=i?eh> z?hA4@FFd_(%W;I^iP4QDd*d+3SKNLFkxE<7+4pmP>QK{a|2S@`Vkx$PY-EAWZ`jV~ z2`Q^f*Q52gjdgJBAxgQi$9}rmJ*azGX;;4FbTCKBXtonG)GpD`n9a}C{C2$81L;~> ziN}cst5PkEx?kr{-tkhqJ7CpcJLrW;SDIiuEX=!(%7aZ;A3Av+QicZgl-$)Ujc~WT z&FXT!{5ZIQwdTt;g(jr8gyc@z&7@yD2=Kt}*gs|7Q3@(G!Dwh#6m>dzLnk zD-%?h!pT2a<9{Sy^w9H=In?ELW~v#jC48?PfbqpG{KGdlFZaQ=M4HC8uwNsIoq#0=Y@6-Qt>giL4`y}$6tbv_L6vp&vd ziN+s+49k;@(MRPDrq@XNH&SpM@q!9vbg0j}w^^w?y{jH6Wd9FEvwOM^bCofQlKJkZNH-%XntMN}BGt=DVuR7F|Hw`0L zpG!<44eUxKpaF&f2`Z$A9K5BrxPHLAh*0joBf=F4#AF#k7RD>~~1JZwV; z_w1(NF!g_;w0GQXeb~ECk(qyqH#ybOL837C_s^W4pPNDxFFdwCW}n3uk(GkruvI@- zV4Zc$_{H%e_5#HJ=8-nhtdR-_fKE4oI|V&O;&C$C0tD_G?HJrCPzxua=Yadu zF*1#t=3T((iY&U48xl6#H|fjU6AKcUQX1s%)XqkCc*(2q0`o06p-RzSGU+b&o;@l{ zQ5VkYmqPnPL|$sa2W|l5j%3KYNCEWA&1bzu=k>2fP@XoXGSGHDA-r0=d^$p(9Iyr8 zZs_gSL=c&jo4ekx2+TokyTG)SLJJ@=_-MX5MUQAf@tOb z#VfD-SX+24ehzqW=iX@6ddF@QqqM6kdp$#?>M*Ra@d^hLnEq^x0TsEOY7owx(^gRc zLQh@}=YiHMhxD|Z2%#h@sP;L~Zlk9#NX9B~=o)@3;^d5PP`^*gcN`@?5p`7|?a>e? ze)OkUkZMD!_}=&>KYd^bXeg+ZK%JZa7p;7ozw zyRQT20JyjDJq8Tzx7MCoNybBQM!(hfm)Z;l?%XH6$oRMZS}}k!zMLV`0NqUy9&r{p z?{Q(qRD1riU_Ia$U{~b789V;m(eD-hvI|#zaCx%dRKJYo=`zS-=1&%51=go%rKA3^ zqSj)NIOjjaD3JK+Z(?Udz%tIbjlZZ&oQy101oAYJxFqV0mTIZ z(DA=jKm`gL=K#vXZ!+an^l4;D(diRBGMnPhRwG0FrQl4Y{0v6=D4(ojP6o zO-}&bFJ*WFRG5EUfQ;4_Xk3*YM;%?>_`%B?@#_giqW;t;;FSWuK;e^69PqAI1TVTz zQ|^v=ysQ>C0qieqsw+{jpn=e6yuffMJHLPQ($%i_aHaLrQ()DzXf4i?_c~M4KL(g( z!bQ6x0Ca@vw;6hy0V3Ks^QVZoKoQ;jmpmVequXSjzr7yFdCLzB0vR&w(3u)an3L*+ zE+%X^Z0j)~y)Zq5Rg`$dV1n0tW2-(ZO1G$iM;#Df8jY~SOCJAS-t6^0kIwXx)q0RpbmQMoel?arK_OxJWSj`$fINffL^heYq`j=84q0#n~CUk(}ubbX4wlD z%7QO;bDy%u^LgyfbxG&CUlXkSg%0&U^fYeqimkq@d|aPDbrm#f?}Z2OMH;tw`QQOp zg#0gHi9_qyU=j~)jI5O-4|L*m(oJxKbbsMW`Y|*8$6%&?6QHB00_#}od(Dlvj?&U} zw_pJITXwei!dEPeV5X-)1GiI8H<0C-qxT9G;Az@31yLr0B%beUM_!0lGHz(ig`DDw zJ_-dY1C&%gzq%>0B4FIp+YTYOG8n;#7w0{|)2f53XS7CG8u=e9x z`aq;5&>ze$AE*!rGGMoIp&E4AkX%!E4(u;8?nLZp(GXK#atpfEf-++R?>SW`0Vs4(bLpB81i*!H%Q`+IT z2qTZoyjF_8Dhwr6wZC?6ZRzSUXv2u;1r4Vq8w;=MmWqJE9>qClYO4N0d)#j)C5 zp7d83kREWXz5QWvx;e%Gu<5Z!{OB!?kM%5khtR)~A~7H{Pu(#|D;?UKO9t@R4bSe% zcRnZgQw`4q*a5i;Ct`&l3|vQfTQ2W}^P_`zALfbd+hJ`N`8?M<#ZI$=At;SnICWj` zQ=x3fXh72p=%7PdT&rxeat?A`xiWrW=$#L?|uanz}q%CK5n6;p|2UrXPax-DQ| zp*UHQo$$c)dKvS|kFB3*5#W0HdsAn{DuFr@PzNVP;?oLHCK|uT#@rJsK%kjLOC@D9#DMu9wLms>xy^1)DO5A^184-N4VS)>|j2@G`l8Z>sHq&eml zLX!YCWMBK~MIvoacFgN+#Qy+q83y1(Ku$=f6->TDwE&|i9 zRCq|3hFc0)K}i2#(lG`i_{ra)`sUBX6pA|q2<0EuqYvN$BnUpx_J5ZZPia7bQ6$p+ zDm(uYll?uwhJTlxe`V4FU0{(w?tjS#khmzRej&e$AA|zz@kvb#Z+wPO0MI=OB2Tj%s`>k%y1t4anZTX z;>Z1;RpfEhBN?>vlAe*I%blMI9hvqs8y6`EuMpkwZ9fv9Da5okuLIbToeL}HQC zRhk#QDDum&)$@T#l4QsiN0+r1;^KZ13YH08<8be>|8a?oRv{86$Xc(*iXVCt6R+B6 z?)YE_P{eW6eI@p!W8+r*F=lGQKL9W^ZQIcBYh}H}#dGKlq?AY@-ae(~y7{?qU0|6O z3?I8>=cU_u8eg?hk5m&|n`4b@-V8`S)H9@g2U-^=K;L3WhU5n0#x0Gl8MHq3=80MR z%+a%D8>uBGy7_GuNdJg4A3>QIiWKU39%?(AV*%)|AMK=WEi=CPBI%A&_EOp1`&0R2 zb+yH#U-cVqYB}d)S$~yfp2>!IzDJh~npo6@f4uIxhLSMs&&@{eosu~XnwWvehv@JK z-sq~Hr1#kXg4vH_%zSzbR3O!#3bX}_;y{K!O@kjKp8wO1Z1q55`ag+pfyC|qAqMN+ zK-xcvtwG}a{}3C3#Q)>L5@7gN2>HYNZ*~BFz9IK#{VP!n5OEnd`{bV@r2RTx?-@p6^Vk@@zflz{fq4Lbt(Zn6D+A zRCXF1LBV>whcY}&TYG+B z_eAJ6>!gQE%26xN<(*>X6R4(pT|v=-pUsbm?abXJO|Lf?CJD=l{kdzTG{7aW)M8-8 zi%o;*VCakg*%vw*t`8h|A#3H*#99yII(1a&+0=sFX$s4Qh@=7SnJJ#e<r;0@7USGL|B8BgzQ8bRgtCDD;7 zhYgu!7>V$B`QM{!Z7nyon122-lXkmjRr1F>Ez1L=C6k|gJx&&zB&^`Ss3Rkl(_9S2 zvR83;4;bhySBhc9QhEt^EDT-+YihAwcI>Fs$LN2OPtUaPUw9(*R?6YqVr$ALCmP5I z<!2OI$X5aioIE1p6W@b7Yeygl}VTa`S*A!ae?d3a*dQRe())#!G$YI#@E8;5T_Hh3I|v2MHg2Pasml%B~9&i?yd z<7-lO`eFgbRkXrZBlK4(t)lxx4j8*+p#jnLM>!L=I4L5&rvaH)9SP|ll0qAoOit-n zM6OzfLRKvHJfHX}*>M`jtX7%6!CxA1?g>BQL<|guQyI(<XNk>3r4uTlDxSe6+Eryh(K4n{;P3=xWUI@SsG_wf#`ZvC+{IDLq)d z!La+`iJ`4GApTG^`{t2mPEbgnO|C_~qZdoLD?>*c10Cd?WASS1r!Q$s#+oB`^?~64 z2Emze!eMaX$&ygsaFp>-(gg4V@l?T^yOz-6z}#Ey_>XiJRf9WyXC^mpN470{QQ*J# zrFk}M(()r?;cbe`=eBeWM6|I{k%J9ek E2L~@62><{9 diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 685f9450b..1a0aef16a 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ {"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":145,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":190,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2198,"get_count":3,"put_bytes":238,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2246,"get_count":3,"put_bytes":276,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2284,"get_count":3,"put_bytes":314,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2322,"get_count":3,"put_bytes":352,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2360,"get_count":3,"put_bytes":390,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2398,"get_count":3,"put_bytes":428,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2436,"get_count":3,"put_bytes":466,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2474,"get_count":3,"put_bytes":504,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":542,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2550,"get_count":3,"put_bytes":580,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2588,"get_count":3,"put_bytes":618,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2626,"get_count":3,"put_bytes":656,"put_count":2}} -{"i":15,"series":1,"stats":{"get_bytes":2664,"get_count":3,"put_bytes":694,"put_count":2}} -{"i":16,"series":1,"stats":{"get_bytes":2702,"get_count":3,"put_bytes":732,"put_count":2}} -{"i":17,"series":1,"stats":{"get_bytes":2740,"get_count":3,"put_bytes":770,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2778,"get_count":3,"put_bytes":808,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2816,"get_count":3,"put_bytes":846,"put_count":2}} -{"i":20,"series":1,"stats":{"get_bytes":2854,"get_count":3,"put_bytes":884,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2892,"get_count":3,"put_bytes":922,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2930,"get_count":3,"put_bytes":960,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2968,"get_count":3,"put_bytes":998,"put_count":2}} -{"i":24,"series":1,"stats":{"get_bytes":3006,"get_count":3,"put_bytes":1037,"put_count":2}} -{"i":25,"series":1,"stats":{"get_bytes":3045,"get_count":3,"put_bytes":1075,"put_count":2}} -{"i":26,"series":1,"stats":{"get_bytes":3083,"get_count":3,"put_bytes":1113,"put_count":2}} -{"i":27,"series":1,"stats":{"get_bytes":3121,"get_count":3,"put_bytes":1151,"put_count":2}} -{"i":28,"series":1,"stats":{"get_bytes":3159,"get_count":3,"put_bytes":1189,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":3197,"get_count":3,"put_bytes":1227,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":3235,"get_count":3,"put_bytes":1265,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":3273,"get_count":3,"put_bytes":1303,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":3311,"get_count":3,"put_bytes":1340,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":3348,"get_count":3,"put_bytes":1378,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":3386,"get_count":3,"put_bytes":1416,"put_count":2}} -{"i":35,"series":1,"stats":{"get_bytes":3424,"get_count":3,"put_bytes":1454,"put_count":2}} -{"i":36,"series":1,"stats":{"get_bytes":3462,"get_count":3,"put_bytes":1491,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":3499,"get_count":3,"put_bytes":1529,"put_count":2}} -{"i":38,"series":1,"stats":{"get_bytes":3537,"get_count":3,"put_bytes":1567,"put_count":2}} -{"i":39,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1605,"put_count":2}} -{"i":40,"series":1,"stats":{"get_bytes":3613,"get_count":3,"put_bytes":1643,"put_count":2}} -{"i":41,"series":1,"stats":{"get_bytes":3651,"get_count":3,"put_bytes":1681,"put_count":2}} -{"i":42,"series":1,"stats":{"get_bytes":3689,"get_count":3,"put_bytes":1719,"put_count":2}} -{"i":43,"series":1,"stats":{"get_bytes":3727,"get_count":3,"put_bytes":1757,"put_count":2}} -{"i":44,"series":1,"stats":{"get_bytes":3765,"get_count":3,"put_bytes":1794,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":3802,"get_count":3,"put_bytes":1832,"put_count":2}} -{"i":46,"series":1,"stats":{"get_bytes":3840,"get_count":3,"put_bytes":1870,"put_count":2}} -{"i":47,"series":1,"stats":{"get_bytes":3878,"get_count":3,"put_bytes":1908,"put_count":2}} -{"i":48,"series":1,"stats":{"get_bytes":3916,"get_count":3,"put_bytes":1946,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3954,"get_count":3,"put_bytes":1984,"put_count":2}} -{"i":50,"series":1,"stats":{"get_bytes":3992,"get_count":3,"put_bytes":2022,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":4030,"get_count":3,"put_bytes":2060,"put_count":2}} -{"i":52,"series":1,"stats":{"get_bytes":4068,"get_count":3,"put_bytes":2098,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":4106,"get_count":3,"put_bytes":2136,"put_count":2}} -{"i":54,"series":1,"stats":{"get_bytes":4144,"get_count":3,"put_bytes":2174,"put_count":2}} -{"i":55,"series":1,"stats":{"get_bytes":4182,"get_count":3,"put_bytes":2212,"put_count":2}} -{"i":56,"series":1,"stats":{"get_bytes":4220,"get_count":3,"put_bytes":2250,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":4258,"get_count":3,"put_bytes":2288,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":4296,"get_count":3,"put_bytes":2326,"put_count":2}} -{"i":59,"series":1,"stats":{"get_bytes":4334,"get_count":3,"put_bytes":2364,"put_count":2}} -{"i":60,"series":1,"stats":{"get_bytes":4372,"get_count":3,"put_bytes":2402,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":4410,"get_count":3,"put_bytes":2439,"put_count":2}} -{"i":62,"series":1,"stats":{"get_bytes":4447,"get_count":3,"put_bytes":2477,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":4485,"get_count":3,"put_bytes":2515,"put_count":2}} -{"i":64,"series":1,"stats":{"get_bytes":4523,"get_count":3,"put_bytes":2553,"put_count":2}} -{"i":65,"series":1,"stats":{"get_bytes":4561,"get_count":3,"put_bytes":2591,"put_count":2}} -{"i":66,"series":1,"stats":{"get_bytes":4599,"get_count":3,"put_bytes":2629,"put_count":2}} -{"i":67,"series":1,"stats":{"get_bytes":4637,"get_count":3,"put_bytes":2666,"put_count":2}} -{"i":68,"series":1,"stats":{"get_bytes":4674,"get_count":3,"put_bytes":2704,"put_count":2}} -{"i":69,"series":1,"stats":{"get_bytes":4712,"get_count":3,"put_bytes":2742,"put_count":2}} -{"i":70,"series":1,"stats":{"get_bytes":4750,"get_count":3,"put_bytes":2780,"put_count":2}} -{"i":71,"series":1,"stats":{"get_bytes":4788,"get_count":3,"put_bytes":2818,"put_count":2}} -{"i":72,"series":1,"stats":{"get_bytes":4826,"get_count":3,"put_bytes":2856,"put_count":2}} -{"i":73,"series":1,"stats":{"get_bytes":4864,"get_count":3,"put_bytes":2894,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":4902,"get_count":3,"put_bytes":2931,"put_count":2}} -{"i":75,"series":1,"stats":{"get_bytes":4939,"get_count":3,"put_bytes":2968,"put_count":2}} -{"i":76,"series":1,"stats":{"get_bytes":4976,"get_count":3,"put_bytes":3006,"put_count":2}} -{"i":77,"series":1,"stats":{"get_bytes":5014,"get_count":3,"put_bytes":3043,"put_count":2}} -{"i":78,"series":1,"stats":{"get_bytes":5051,"get_count":3,"put_bytes":3080,"put_count":2}} -{"i":79,"series":1,"stats":{"get_bytes":5088,"get_count":3,"put_bytes":3118,"put_count":2}} -{"i":80,"series":1,"stats":{"get_bytes":5126,"get_count":3,"put_bytes":3156,"put_count":2}} -{"i":81,"series":1,"stats":{"get_bytes":5164,"get_count":3,"put_bytes":3194,"put_count":2}} -{"i":82,"series":1,"stats":{"get_bytes":5202,"get_count":3,"put_bytes":3232,"put_count":2}} -{"i":83,"series":1,"stats":{"get_bytes":5240,"get_count":3,"put_bytes":3270,"put_count":2}} -{"i":84,"series":1,"stats":{"get_bytes":5278,"get_count":3,"put_bytes":3308,"put_count":2}} -{"i":85,"series":1,"stats":{"get_bytes":5316,"get_count":3,"put_bytes":3345,"put_count":2}} -{"i":86,"series":1,"stats":{"get_bytes":5353,"get_count":3,"put_bytes":3382,"put_count":2}} -{"i":87,"series":1,"stats":{"get_bytes":5390,"get_count":3,"put_bytes":3419,"put_count":2}} -{"i":88,"series":1,"stats":{"get_bytes":5427,"get_count":3,"put_bytes":3456,"put_count":2}} -{"i":89,"series":1,"stats":{"get_bytes":5464,"get_count":3,"put_bytes":3494,"put_count":2}} -{"i":90,"series":1,"stats":{"get_bytes":5502,"get_count":3,"put_bytes":3532,"put_count":2}} -{"i":91,"series":1,"stats":{"get_bytes":5540,"get_count":3,"put_bytes":3570,"put_count":2}} -{"i":92,"series":1,"stats":{"get_bytes":5578,"get_count":3,"put_bytes":3607,"put_count":2}} -{"i":93,"series":1,"stats":{"get_bytes":5615,"get_count":3,"put_bytes":3645,"put_count":2}} -{"i":94,"series":1,"stats":{"get_bytes":5653,"get_count":3,"put_bytes":3683,"put_count":2}} -{"i":95,"series":1,"stats":{"get_bytes":5691,"get_count":3,"put_bytes":3721,"put_count":2}} -{"i":96,"series":1,"stats":{"get_bytes":5729,"get_count":3,"put_bytes":3758,"put_count":2}} -{"i":97,"series":1,"stats":{"get_bytes":5766,"get_count":3,"put_bytes":3795,"put_count":2}} -{"i":98,"series":1,"stats":{"get_bytes":5803,"get_count":3,"put_bytes":3833,"put_count":2}} -{"i":99,"series":1,"stats":{"get_bytes":5841,"get_count":3,"put_bytes":3870,"put_count":2}} -{"i":0,"series":2,"stats":{"get_bytes":5878,"get_count":3,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":5878,"get_count":3,"put_bytes":3908,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":5916,"get_count":3,"put_bytes":3945,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":5953,"get_count":3,"put_bytes":3983,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":5991,"get_count":3,"put_bytes":4021,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":6029,"get_count":3,"put_bytes":4059,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":6067,"get_count":3,"put_bytes":4096,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":6104,"get_count":3,"put_bytes":4134,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":6142,"get_count":3,"put_bytes":4172,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":6180,"get_count":3,"put_bytes":4210,"put_count":2}} -{"i":10,"series":2,"stats":{"get_bytes":6218,"get_count":3,"put_bytes":4247,"put_count":2}} -{"i":11,"series":2,"stats":{"get_bytes":6255,"get_count":3,"put_bytes":4285,"put_count":2}} -{"i":12,"series":2,"stats":{"get_bytes":6293,"get_count":3,"put_bytes":4323,"put_count":2}} -{"i":13,"series":2,"stats":{"get_bytes":6331,"get_count":3,"put_bytes":4361,"put_count":2}} -{"i":14,"series":2,"stats":{"get_bytes":6369,"get_count":3,"put_bytes":4398,"put_count":2}} -{"i":15,"series":2,"stats":{"get_bytes":6406,"get_count":3,"put_bytes":4435,"put_count":2}} -{"i":16,"series":2,"stats":{"get_bytes":6443,"get_count":3,"put_bytes":4473,"put_count":2}} -{"i":17,"series":2,"stats":{"get_bytes":6481,"get_count":3,"put_bytes":4511,"put_count":2}} -{"i":18,"series":2,"stats":{"get_bytes":6519,"get_count":3,"put_bytes":4549,"put_count":2}} -{"i":19,"series":2,"stats":{"get_bytes":6557,"get_count":3,"put_bytes":4587,"put_count":2}} -{"i":20,"series":2,"stats":{"get_bytes":6595,"get_count":3,"put_bytes":4625,"put_count":2}} -{"i":21,"series":2,"stats":{"get_bytes":6633,"get_count":3,"put_bytes":4663,"put_count":2}} -{"i":22,"series":2,"stats":{"get_bytes":6671,"get_count":3,"put_bytes":4700,"put_count":2}} -{"i":23,"series":2,"stats":{"get_bytes":6708,"get_count":3,"put_bytes":4738,"put_count":2}} -{"i":24,"series":2,"stats":{"get_bytes":6746,"get_count":3,"put_bytes":4775,"put_count":2}} -{"i":25,"series":2,"stats":{"get_bytes":6783,"get_count":3,"put_bytes":4813,"put_count":2}} -{"i":26,"series":2,"stats":{"get_bytes":6821,"get_count":3,"put_bytes":4851,"put_count":2}} -{"i":27,"series":2,"stats":{"get_bytes":6859,"get_count":3,"put_bytes":4888,"put_count":2}} -{"i":28,"series":2,"stats":{"get_bytes":6896,"get_count":3,"put_bytes":4926,"put_count":2}} -{"i":29,"series":2,"stats":{"get_bytes":6934,"get_count":3,"put_bytes":4963,"put_count":2}} -{"i":30,"series":2,"stats":{"get_bytes":6971,"get_count":3,"put_bytes":5001,"put_count":2}} -{"i":31,"series":2,"stats":{"get_bytes":7009,"get_count":3,"put_bytes":5038,"put_count":2}} -{"i":32,"series":2,"stats":{"get_bytes":7046,"get_count":3,"put_bytes":5075,"put_count":2}} -{"i":33,"series":2,"stats":{"get_bytes":7083,"get_count":3,"put_bytes":5112,"put_count":2}} -{"i":34,"series":2,"stats":{"get_bytes":7120,"get_count":3,"put_bytes":5149,"put_count":2}} -{"i":35,"series":2,"stats":{"get_bytes":7157,"get_count":3,"put_bytes":5186,"put_count":2}} -{"i":36,"series":2,"stats":{"get_bytes":7194,"get_count":3,"put_bytes":5223,"put_count":2}} -{"i":37,"series":2,"stats":{"get_bytes":7231,"get_count":3,"put_bytes":5261,"put_count":2}} -{"i":38,"series":2,"stats":{"get_bytes":7269,"get_count":3,"put_bytes":5299,"put_count":2}} -{"i":39,"series":2,"stats":{"get_bytes":7307,"get_count":3,"put_bytes":5336,"put_count":2}} -{"i":40,"series":2,"stats":{"get_bytes":7344,"get_count":3,"put_bytes":5373,"put_count":2}} -{"i":41,"series":2,"stats":{"get_bytes":7381,"get_count":3,"put_bytes":5411,"put_count":2}} -{"i":42,"series":2,"stats":{"get_bytes":7419,"get_count":3,"put_bytes":5449,"put_count":2}} -{"i":43,"series":2,"stats":{"get_bytes":7457,"get_count":3,"put_bytes":5487,"put_count":2}} -{"i":44,"series":2,"stats":{"get_bytes":7495,"get_count":3,"put_bytes":5525,"put_count":2}} -{"i":45,"series":2,"stats":{"get_bytes":7533,"get_count":3,"put_bytes":5563,"put_count":2}} -{"i":46,"series":2,"stats":{"get_bytes":7571,"get_count":3,"put_bytes":5600,"put_count":2}} -{"i":47,"series":2,"stats":{"get_bytes":7608,"get_count":3,"put_bytes":5637,"put_count":2}} -{"i":48,"series":2,"stats":{"get_bytes":7645,"get_count":3,"put_bytes":5674,"put_count":2}} -{"i":49,"series":2,"stats":{"get_bytes":7682,"get_count":3,"put_bytes":5712,"put_count":2}} -{"i":50,"series":2,"stats":{"get_bytes":7720,"get_count":3,"put_bytes":5749,"put_count":2}} -{"i":51,"series":2,"stats":{"get_bytes":7757,"get_count":3,"put_bytes":5786,"put_count":2}} -{"i":52,"series":2,"stats":{"get_bytes":7794,"get_count":3,"put_bytes":5824,"put_count":2}} -{"i":53,"series":2,"stats":{"get_bytes":7832,"get_count":3,"put_bytes":5862,"put_count":2}} -{"i":54,"series":2,"stats":{"get_bytes":7870,"get_count":3,"put_bytes":5900,"put_count":2}} -{"i":55,"series":2,"stats":{"get_bytes":7908,"get_count":3,"put_bytes":5938,"put_count":2}} -{"i":56,"series":2,"stats":{"get_bytes":7946,"get_count":3,"put_bytes":5976,"put_count":2}} -{"i":57,"series":2,"stats":{"get_bytes":7984,"get_count":3,"put_bytes":6013,"put_count":2}} -{"i":58,"series":2,"stats":{"get_bytes":8021,"get_count":3,"put_bytes":6051,"put_count":2}} -{"i":59,"series":2,"stats":{"get_bytes":8059,"get_count":3,"put_bytes":6089,"put_count":2}} -{"i":60,"series":2,"stats":{"get_bytes":8097,"get_count":3,"put_bytes":6126,"put_count":2}} -{"i":61,"series":2,"stats":{"get_bytes":8134,"get_count":3,"put_bytes":6163,"put_count":2}} -{"i":62,"series":2,"stats":{"get_bytes":8171,"get_count":3,"put_bytes":6201,"put_count":2}} -{"i":63,"series":2,"stats":{"get_bytes":8209,"get_count":3,"put_bytes":6238,"put_count":2}} -{"i":64,"series":2,"stats":{"get_bytes":8246,"get_count":3,"put_bytes":6275,"put_count":2}} -{"i":65,"series":2,"stats":{"get_bytes":8283,"get_count":3,"put_bytes":6313,"put_count":2}} -{"i":66,"series":2,"stats":{"get_bytes":8321,"get_count":3,"put_bytes":6351,"put_count":2}} -{"i":67,"series":2,"stats":{"get_bytes":8359,"get_count":3,"put_bytes":6388,"put_count":2}} -{"i":68,"series":2,"stats":{"get_bytes":8396,"get_count":3,"put_bytes":6425,"put_count":2}} -{"i":69,"series":2,"stats":{"get_bytes":8433,"get_count":3,"put_bytes":6463,"put_count":2}} -{"i":70,"series":2,"stats":{"get_bytes":8471,"get_count":3,"put_bytes":6576,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":8402,"get_count":3,"put_bytes":6431,"put_count":2}} -{"i":72,"series":2,"stats":{"get_bytes":8439,"get_count":3,"put_bytes":6469,"put_count":2}} -{"i":73,"series":2,"stats":{"get_bytes":8477,"get_count":3,"put_bytes":6506,"put_count":2}} -{"i":74,"series":2,"stats":{"get_bytes":8514,"get_count":3,"put_bytes":6543,"put_count":2}} -{"i":75,"series":2,"stats":{"get_bytes":8551,"get_count":3,"put_bytes":6580,"put_count":2}} -{"i":76,"series":2,"stats":{"get_bytes":8588,"get_count":3,"put_bytes":6698,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":8519,"get_count":3,"put_bytes":6549,"put_count":2}} -{"i":78,"series":2,"stats":{"get_bytes":8557,"get_count":3,"put_bytes":6586,"put_count":2}} -{"i":79,"series":2,"stats":{"get_bytes":8594,"get_count":3,"put_bytes":6623,"put_count":2}} -{"i":80,"series":2,"stats":{"get_bytes":8631,"get_count":3,"put_bytes":6661,"put_count":2}} -{"i":81,"series":2,"stats":{"get_bytes":8851,"get_count":4,"put_bytes":6881,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":8669,"get_count":3,"put_bytes":6698,"put_count":2}} -{"i":83,"series":2,"stats":{"get_bytes":8706,"get_count":3,"put_bytes":6736,"put_count":2}} -{"i":84,"series":2,"stats":{"get_bytes":8744,"get_count":3,"put_bytes":6773,"put_count":2}} -{"i":85,"series":2,"stats":{"get_bytes":8781,"get_count":3,"put_bytes":6810,"put_count":2}} -{"i":86,"series":2,"stats":{"get_bytes":8818,"get_count":3,"put_bytes":6847,"put_count":2}} -{"i":87,"series":2,"stats":{"get_bytes":8855,"get_count":3,"put_bytes":6884,"put_count":2}} -{"i":88,"series":2,"stats":{"get_bytes":8892,"get_count":3,"put_bytes":6922,"put_count":2}} -{"i":89,"series":2,"stats":{"get_bytes":8930,"get_count":3,"put_bytes":6960,"put_count":2}} -{"i":90,"series":2,"stats":{"get_bytes":8968,"get_count":3,"put_bytes":6998,"put_count":2}} -{"i":91,"series":2,"stats":{"get_bytes":9006,"get_count":3,"put_bytes":7035,"put_count":2}} -{"i":92,"series":2,"stats":{"get_bytes":9043,"get_count":3,"put_bytes":7073,"put_count":2}} -{"i":93,"series":2,"stats":{"get_bytes":9081,"get_count":3,"put_bytes":7110,"put_count":2}} -{"i":94,"series":2,"stats":{"get_bytes":9118,"get_count":3,"put_bytes":7148,"put_count":2}} -{"i":95,"series":2,"stats":{"get_bytes":9156,"get_count":3,"put_bytes":7186,"put_count":2}} -{"i":96,"series":2,"stats":{"get_bytes":9194,"get_count":3,"put_bytes":7223,"put_count":2}} -{"i":97,"series":2,"stats":{"get_bytes":9231,"get_count":3,"put_bytes":7261,"put_count":2}} -{"i":98,"series":2,"stats":{"get_bytes":9269,"get_count":3,"put_bytes":7298,"put_count":2}} -{"i":99,"series":2,"stats":{"get_bytes":9493,"get_count":4,"put_bytes":7523,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":272,"put_count":4}} +{"i":2,"series":1,"stats":{"get_bytes":2166,"get_count":3,"put_bytes":339,"put_count":4}} +{"i":3,"series":1,"stats":{"get_bytes":2231,"get_count":3,"put_bytes":401,"put_count":4}} +{"i":4,"series":1,"stats":{"get_bytes":2295,"get_count":3,"put_bytes":467,"put_count":4}} +{"i":5,"series":1,"stats":{"get_bytes":2359,"get_count":3,"put_bytes":529,"put_count":4}} +{"i":6,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":596,"put_count":4}} +{"i":7,"series":1,"stats":{"get_bytes":2423,"get_count":3,"put_bytes":593,"put_count":4}} +{"i":8,"series":1,"stats":{"get_bytes":2556,"get_count":4,"put_bytes":660,"put_count":4}} +{"i":9,"series":1,"stats":{"get_bytes":2487,"get_count":3,"put_bytes":657,"put_count":4}} +{"i":10,"series":1,"stats":{"get_bytes":2551,"get_count":3,"put_bytes":722,"put_count":4}} +{"i":11,"series":1,"stats":{"get_bytes":2615,"get_count":3,"put_bytes":789,"put_count":4}} +{"i":12,"series":1,"stats":{"get_bytes":2679,"get_count":3,"put_bytes":852,"put_count":4}} +{"i":13,"series":1,"stats":{"get_bytes":2743,"get_count":3,"put_bytes":915,"put_count":4}} +{"i":14,"series":1,"stats":{"get_bytes":2877,"get_count":4,"put_bytes":981,"put_count":4}} +{"i":15,"series":1,"stats":{"get_bytes":2876,"get_count":4,"put_bytes":979,"put_count":4}} +{"i":16,"series":1,"stats":{"get_bytes":2875,"get_count":4,"put_bytes":980,"put_count":4}} +{"i":17,"series":1,"stats":{"get_bytes":2807,"get_count":3,"put_bytes":979,"put_count":4}} +{"i":18,"series":1,"stats":{"get_bytes":2871,"get_count":3,"put_bytes":1043,"put_count":4}} +{"i":19,"series":1,"stats":{"get_bytes":3049,"get_count":5,"put_bytes":1082,"put_count":4}} +{"i":20,"series":1,"stats":{"get_bytes":2935,"get_count":3,"put_bytes":1110,"put_count":4}} +{"i":21,"series":1,"stats":{"get_bytes":2999,"get_count":3,"put_bytes":1173,"put_count":4}} +{"i":22,"series":1,"stats":{"get_bytes":3063,"get_count":3,"put_bytes":1235,"put_count":4}} +{"i":23,"series":1,"stats":{"get_bytes":3198,"get_count":4,"put_bytes":1299,"put_count":4}} +{"i":24,"series":1,"stats":{"get_bytes":3195,"get_count":4,"put_bytes":1298,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":3195,"get_count":4,"put_bytes":1299,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":3196,"get_count":4,"put_bytes":1300,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":3197,"get_count":4,"put_bytes":1299,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1299,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":3191,"get_count":3,"put_bytes":1363,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":3255,"get_count":3,"put_bytes":1425,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":3319,"get_count":3,"put_bytes":1492,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":3383,"get_count":3,"put_bytes":1552,"put_count":4}} +{"i":33,"series":1,"stats":{"get_bytes":3447,"get_count":3,"put_bytes":1620,"put_count":4}} +{"i":34,"series":1,"stats":{"get_bytes":3581,"get_count":4,"put_bytes":1683,"put_count":4}} +{"i":35,"series":1,"stats":{"get_bytes":3580,"get_count":4,"put_bytes":1683,"put_count":4}} +{"i":36,"series":1,"stats":{"get_bytes":3511,"get_count":3,"put_bytes":1683,"put_count":4}} +{"i":37,"series":1,"stats":{"get_bytes":3646,"get_count":4,"put_bytes":1749,"put_count":4}} +{"i":38,"series":1,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1811,"put_count":4}} +{"i":39,"series":1,"stats":{"get_bytes":3691,"get_count":5,"put_bytes":1724,"put_count":4}} +{"i":40,"series":1,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1811,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1812,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":3645,"get_count":4,"put_bytes":1748,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1814,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1746,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":3839,"get_count":4,"put_bytes":1943,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":2005,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":3709,"get_count":4,"put_bytes":1813,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":3640,"get_count":3,"put_bytes":1815,"put_count":4}} +{"i":49,"series":1,"stats":{"get_bytes":3774,"get_count":4,"put_bytes":1877,"put_count":4}} +{"i":50,"series":1,"stats":{"get_bytes":3704,"get_count":3,"put_bytes":1876,"put_count":4}} +{"i":51,"series":1,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":2004,"put_count":4}} +{"i":52,"series":1,"stats":{"get_bytes":3768,"get_count":3,"put_bytes":1938,"put_count":4}} +{"i":53,"series":1,"stats":{"get_bytes":4031,"get_count":4,"put_bytes":2132,"put_count":4}} +{"i":54,"series":1,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":2004,"put_count":4}} +{"i":55,"series":1,"stats":{"get_bytes":3966,"get_count":4,"put_bytes":2070,"put_count":4}} +{"i":56,"series":1,"stats":{"get_bytes":3832,"get_count":3,"put_bytes":2002,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":3896,"get_count":3,"put_bytes":2068,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":4095,"get_count":4,"put_bytes":2198,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":4095,"get_count":4,"put_bytes":2196,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":3960,"get_count":3,"put_bytes":2129,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":4397,"get_count":5,"put_bytes":2432,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":4024,"get_count":3,"put_bytes":2197,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":4156,"get_count":4,"put_bytes":2263,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2326,"put_count":4}} +{"i":65,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2324,"put_count":4}} +{"i":66,"series":1,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2323,"put_count":4}} +{"i":67,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":68,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2327,"put_count":4}} +{"i":69,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2323,"put_count":4}} +{"i":70,"series":1,"stats":{"get_bytes":4157,"get_count":4,"put_bytes":2261,"put_count":4}} +{"i":71,"series":1,"stats":{"get_bytes":4157,"get_count":4,"put_bytes":2262,"put_count":4}} +{"i":72,"series":1,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2324,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":4088,"get_count":3,"put_bytes":2259,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2324,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2325,"put_count":4}} +{"i":81,"series":1,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":82,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2324,"put_count":4}} +{"i":83,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2324,"put_count":4}} +{"i":84,"series":1,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":85,"series":1,"stats":{"get_bytes":4285,"get_count":4,"put_bytes":2386,"put_count":4}} +{"i":86,"series":1,"stats":{"get_bytes":4396,"get_count":5,"put_bytes":2429,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":4527,"get_count":5,"put_bytes":2560,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2323,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":4349,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2516,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2327,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2322,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":2,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":3,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":4,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":5,"series":2,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2323,"put_count":4}} +{"i":6,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":7,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} +{"i":8,"series":2,"stats":{"get_bytes":4524,"get_count":5,"put_bytes":2557,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2390,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2391,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2391,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2326,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2515,"put_count":4}} +{"i":25,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":26,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2582,"put_count":4}} +{"i":27,"series":2,"stats":{"get_bytes":4396,"get_count":5,"put_bytes":2429,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} +{"i":29,"series":2,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2327,"put_count":4}} +{"i":30,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2515,"put_count":4}} +{"i":31,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":32,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2582,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2389,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":4463,"get_count":5,"put_bytes":2496,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2516,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":41,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":42,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} +{"i":44,"series":2,"stats":{"get_bytes":4285,"get_count":4,"put_bytes":2390,"put_count":4}} +{"i":45,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} +{"i":46,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} +{"i":47,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} +{"i":48,"series":2,"stats":{"get_bytes":4349,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2389,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2391,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":4398,"get_count":5,"put_bytes":2431,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":4526,"get_count":5,"put_bytes":2559,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} +{"i":57,"series":2,"stats":{"get_bytes":4332,"get_count":5,"put_bytes":2365,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} +{"i":59,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} +{"i":61,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} +{"i":62,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":4525,"get_count":5,"put_bytes":2560,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2710,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":73,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2582,"put_count":4}} +{"i":74,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2708,"put_count":4}} +{"i":75,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":76,"series":2,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2326,"put_count":4}} +{"i":77,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2711,"put_count":4}} +{"i":78,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} +{"i":79,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":80,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2708,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2519,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2516,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2646,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2325,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png index bf73af71ce94bd85f0dd98a34a93221c29d321f7..f0d1cf63d9b224e02c25753df729c4c4aa6b4d6b 100644 GIT binary patch literal 19902 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 L{M?(|K4<<0Q8+Hy literal 16081 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 diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index 1693bf473..a8fda0223 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ {"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3907,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":5915,"get_count":3,"put_bytes":7820,"put_count":4}} -{"i":3,"series":1,"stats":{"get_bytes":9641,"get_count":4,"put_bytes":11949,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":12667,"get_count":5,"put_bytes":14935,"put_count":11}} -{"i":5,"series":1,"stats":{"get_bytes":16391,"get_count":9,"put_bytes":18947,"put_count":19}} -{"i":6,"series":1,"stats":{"get_bytes":19500,"get_count":15,"put_bytes":23092,"put_count":38}} -{"i":7,"series":1,"stats":{"get_bytes":21180,"get_count":19,"put_bytes":24464,"put_count":38}} -{"i":8,"series":1,"stats":{"get_bytes":24657,"get_count":34,"put_bytes":28320,"put_count":58}} -{"i":9,"series":1,"stats":{"get_bytes":25047,"get_count":35,"put_bytes":28141,"put_count":52}} -{"i":10,"series":1,"stats":{"get_bytes":27347,"get_count":42,"put_bytes":30442,"put_count":58}} -{"i":11,"series":1,"stats":{"get_bytes":28033,"get_count":44,"put_bytes":31148,"put_count":61}} -{"i":12,"series":1,"stats":{"get_bytes":29542,"get_count":52,"put_bytes":32815,"put_count":71}} -{"i":13,"series":1,"stats":{"get_bytes":28836,"get_count":51,"put_bytes":32175,"put_count":70}} -{"i":14,"series":1,"stats":{"get_bytes":32671,"get_count":70,"put_bytes":35569,"put_count":83}} -{"i":15,"series":1,"stats":{"get_bytes":34959,"get_count":75,"put_bytes":37231,"put_count":80}} -{"i":16,"series":1,"stats":{"get_bytes":34133,"get_count":71,"put_bytes":36838,"put_count":81}} -{"i":17,"series":1,"stats":{"get_bytes":34716,"get_count":74,"put_bytes":37143,"put_count":81}} -{"i":18,"series":1,"stats":{"get_bytes":36660,"get_count":77,"put_bytes":38769,"put_count":80}} -{"i":19,"series":1,"stats":{"get_bytes":40178,"get_count":83,"put_bytes":42211,"put_count":85}} -{"i":20,"series":1,"stats":{"get_bytes":37502,"get_count":75,"put_bytes":39665,"put_count":78}} -{"i":21,"series":1,"stats":{"get_bytes":43262,"get_count":86,"put_bytes":45344,"put_count":88}} -{"i":22,"series":1,"stats":{"get_bytes":40950,"get_count":80,"put_bytes":42928,"put_count":81}} -{"i":23,"series":1,"stats":{"get_bytes":44273,"get_count":84,"put_bytes":46178,"put_count":84}} -{"i":24,"series":1,"stats":{"get_bytes":43287,"get_count":82,"put_bytes":45334,"put_count":84}} -{"i":25,"series":1,"stats":{"get_bytes":43014,"get_count":75,"put_bytes":45089,"put_count":77}} -{"i":26,"series":1,"stats":{"get_bytes":47462,"get_count":85,"put_bytes":49446,"put_count":86}} -{"i":27,"series":1,"stats":{"get_bytes":48030,"get_count":87,"put_bytes":49872,"put_count":86}} -{"i":28,"series":1,"stats":{"get_bytes":51826,"get_count":91,"put_bytes":53643,"put_count":90}} -{"i":29,"series":1,"stats":{"get_bytes":50558,"get_count":84,"put_bytes":52381,"put_count":83}} -{"i":30,"series":1,"stats":{"get_bytes":54057,"get_count":90,"put_bytes":55945,"put_count":90}} -{"i":31,"series":1,"stats":{"get_bytes":54204,"get_count":87,"put_bytes":56000,"put_count":86}} -{"i":32,"series":1,"stats":{"get_bytes":53558,"get_count":88,"put_bytes":55422,"put_count":88}} -{"i":33,"series":1,"stats":{"get_bytes":54603,"get_count":84,"put_bytes":56400,"put_count":83}} -{"i":34,"series":1,"stats":{"get_bytes":54599,"get_count":80,"put_bytes":56394,"put_count":79}} -{"i":35,"series":1,"stats":{"get_bytes":58554,"get_count":89,"put_bytes":60346,"put_count":88}} -{"i":36,"series":1,"stats":{"get_bytes":56411,"get_count":82,"put_bytes":58210,"put_count":81}} -{"i":37,"series":1,"stats":{"get_bytes":62770,"get_count":87,"put_bytes":64568,"put_count":86}} -{"i":38,"series":1,"stats":{"get_bytes":61924,"get_count":87,"put_bytes":63726,"put_count":86}} -{"i":39,"series":1,"stats":{"get_bytes":60897,"get_count":83,"put_bytes":62691,"put_count":82}} -{"i":40,"series":1,"stats":{"get_bytes":63342,"get_count":85,"put_bytes":65125,"put_count":84}} -{"i":41,"series":1,"stats":{"get_bytes":61627,"get_count":81,"put_bytes":63432,"put_count":80}} -{"i":42,"series":1,"stats":{"get_bytes":63280,"get_count":84,"put_bytes":65091,"put_count":83}} -{"i":43,"series":1,"stats":{"get_bytes":66073,"get_count":85,"put_bytes":67873,"put_count":84}} -{"i":44,"series":1,"stats":{"get_bytes":69931,"get_count":86,"put_bytes":71725,"put_count":85}} -{"i":45,"series":1,"stats":{"get_bytes":71888,"get_count":85,"put_bytes":73709,"put_count":84}} -{"i":46,"series":1,"stats":{"get_bytes":67636,"get_count":79,"put_bytes":69431,"put_count":78}} -{"i":47,"series":1,"stats":{"get_bytes":71600,"get_count":84,"put_bytes":73403,"put_count":83}} -{"i":48,"series":1,"stats":{"get_bytes":72099,"get_count":83,"put_bytes":73892,"put_count":82}} -{"i":49,"series":1,"stats":{"get_bytes":76239,"get_count":87,"put_bytes":78042,"put_count":86}} -{"i":50,"series":1,"stats":{"get_bytes":79740,"get_count":90,"put_bytes":81530,"put_count":89}} -{"i":51,"series":1,"stats":{"get_bytes":77912,"get_count":85,"put_bytes":79705,"put_count":84}} -{"i":52,"series":1,"stats":{"get_bytes":77176,"get_count":84,"put_bytes":78978,"put_count":83}} -{"i":53,"series":1,"stats":{"get_bytes":81615,"get_count":89,"put_bytes":83423,"put_count":88}} -{"i":54,"series":1,"stats":{"get_bytes":79262,"get_count":85,"put_bytes":81048,"put_count":84}} -{"i":55,"series":1,"stats":{"get_bytes":80577,"get_count":87,"put_bytes":82363,"put_count":86}} -{"i":56,"series":1,"stats":{"get_bytes":82322,"get_count":86,"put_bytes":84118,"put_count":85}} -{"i":57,"series":1,"stats":{"get_bytes":91542,"get_count":90,"put_bytes":93340,"put_count":89}} -{"i":58,"series":1,"stats":{"get_bytes":83936,"get_count":84,"put_bytes":85731,"put_count":83}} -{"i":59,"series":1,"stats":{"get_bytes":90594,"get_count":90,"put_bytes":92385,"put_count":89}} -{"i":60,"series":1,"stats":{"get_bytes":87501,"get_count":85,"put_bytes":89302,"put_count":84}} -{"i":61,"series":1,"stats":{"get_bytes":88390,"get_count":83,"put_bytes":90191,"put_count":82}} -{"i":62,"series":1,"stats":{"get_bytes":96267,"get_count":93,"put_bytes":98060,"put_count":92}} -{"i":63,"series":1,"stats":{"get_bytes":93748,"get_count":87,"put_bytes":95541,"put_count":86}} -{"i":64,"series":1,"stats":{"get_bytes":94245,"get_count":86,"put_bytes":96044,"put_count":85}} -{"i":65,"series":1,"stats":{"get_bytes":96937,"get_count":85,"put_bytes":98721,"put_count":84}} -{"i":66,"series":1,"stats":{"get_bytes":100692,"get_count":89,"put_bytes":102496,"put_count":88}} -{"i":67,"series":1,"stats":{"get_bytes":101493,"get_count":88,"put_bytes":103286,"put_count":87}} -{"i":68,"series":1,"stats":{"get_bytes":97208,"get_count":88,"put_bytes":99006,"put_count":87}} -{"i":69,"series":1,"stats":{"get_bytes":84053,"get_count":73,"put_bytes":85848,"put_count":72}} -{"i":70,"series":1,"stats":{"get_bytes":93845,"get_count":80,"put_bytes":95720,"put_count":80}} -{"i":71,"series":1,"stats":{"get_bytes":97725,"get_count":84,"put_bytes":99521,"put_count":83}} -{"i":72,"series":1,"stats":{"get_bytes":99189,"get_count":82,"put_bytes":100981,"put_count":81}} -{"i":73,"series":1,"stats":{"get_bytes":112101,"get_count":92,"put_bytes":113897,"put_count":91}} -{"i":74,"series":1,"stats":{"get_bytes":99827,"get_count":85,"put_bytes":101609,"put_count":84}} -{"i":75,"series":1,"stats":{"get_bytes":100685,"get_count":81,"put_bytes":102470,"put_count":80}} -{"i":76,"series":1,"stats":{"get_bytes":104504,"get_count":85,"put_bytes":106301,"put_count":84}} -{"i":77,"series":1,"stats":{"get_bytes":108099,"get_count":83,"put_bytes":109888,"put_count":82}} -{"i":78,"series":1,"stats":{"get_bytes":110409,"get_count":86,"put_bytes":112191,"put_count":85}} -{"i":79,"series":1,"stats":{"get_bytes":113220,"get_count":87,"put_bytes":115082,"put_count":87}} -{"i":80,"series":1,"stats":{"get_bytes":113519,"get_count":88,"put_bytes":115309,"put_count":87}} -{"i":81,"series":1,"stats":{"get_bytes":118591,"get_count":90,"put_bytes":120379,"put_count":89}} -{"i":82,"series":1,"stats":{"get_bytes":112509,"get_count":83,"put_bytes":114290,"put_count":82}} -{"i":83,"series":1,"stats":{"get_bytes":125634,"get_count":93,"put_bytes":127419,"put_count":92}} -{"i":84,"series":1,"stats":{"get_bytes":115870,"get_count":82,"put_bytes":117655,"put_count":81}} -{"i":85,"series":1,"stats":{"get_bytes":122115,"get_count":87,"put_bytes":123893,"put_count":86}} -{"i":86,"series":1,"stats":{"get_bytes":121715,"get_count":87,"put_bytes":123495,"put_count":86}} -{"i":87,"series":1,"stats":{"get_bytes":114652,"get_count":82,"put_bytes":116435,"put_count":81}} -{"i":88,"series":1,"stats":{"get_bytes":122296,"get_count":82,"put_bytes":124076,"put_count":81}} -{"i":89,"series":1,"stats":{"get_bytes":117496,"get_count":83,"put_bytes":119275,"put_count":82}} -{"i":90,"series":1,"stats":{"get_bytes":121325,"get_count":85,"put_bytes":123106,"put_count":84}} -{"i":91,"series":1,"stats":{"get_bytes":126312,"get_count":86,"put_bytes":128093,"put_count":85}} -{"i":92,"series":1,"stats":{"get_bytes":125627,"get_count":84,"put_bytes":127408,"put_count":83}} -{"i":93,"series":1,"stats":{"get_bytes":135350,"get_count":90,"put_bytes":137134,"put_count":89}} -{"i":94,"series":1,"stats":{"get_bytes":131879,"get_count":88,"put_bytes":133657,"put_count":87}} -{"i":95,"series":1,"stats":{"get_bytes":130329,"get_count":87,"put_bytes":132108,"put_count":86}} -{"i":96,"series":1,"stats":{"get_bytes":139717,"get_count":91,"put_bytes":141495,"put_count":90}} -{"i":97,"series":1,"stats":{"get_bytes":136987,"get_count":87,"put_bytes":138767,"put_count":86}} -{"i":98,"series":1,"stats":{"get_bytes":132411,"get_count":82,"put_bytes":134184,"put_count":81}} -{"i":99,"series":1,"stats":{"get_bytes":142862,"get_count":90,"put_bytes":144641,"put_count":89}} -{"i":0,"series":2,"stats":{"get_bytes":141205,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":138023,"get_count":85,"put_bytes":139806,"put_count":84}} -{"i":2,"series":2,"stats":{"get_bytes":139718,"get_count":87,"put_bytes":141497,"put_count":86}} -{"i":3,"series":2,"stats":{"get_bytes":139149,"get_count":86,"put_bytes":140932,"put_count":85}} -{"i":4,"series":2,"stats":{"get_bytes":141015,"get_count":85,"put_bytes":142791,"put_count":84}} -{"i":5,"series":2,"stats":{"get_bytes":142312,"get_count":87,"put_bytes":144096,"put_count":86}} -{"i":6,"series":2,"stats":{"get_bytes":154998,"get_count":93,"put_bytes":156768,"put_count":92}} -{"i":7,"series":2,"stats":{"get_bytes":138680,"get_count":84,"put_bytes":140462,"put_count":83}} -{"i":8,"series":2,"stats":{"get_bytes":152097,"get_count":90,"put_bytes":153882,"put_count":89}} -{"i":9,"series":2,"stats":{"get_bytes":146463,"get_count":86,"put_bytes":148241,"put_count":85}} -{"i":10,"series":2,"stats":{"get_bytes":149126,"get_count":86,"put_bytes":150906,"put_count":85}} -{"i":11,"series":2,"stats":{"get_bytes":153505,"get_count":88,"put_bytes":155290,"put_count":87}} -{"i":12,"series":2,"stats":{"get_bytes":155262,"get_count":87,"put_bytes":157035,"put_count":86}} -{"i":13,"series":2,"stats":{"get_bytes":156118,"get_count":88,"put_bytes":157895,"put_count":87}} -{"i":14,"series":2,"stats":{"get_bytes":154036,"get_count":86,"put_bytes":155816,"put_count":85}} -{"i":15,"series":2,"stats":{"get_bytes":155312,"get_count":85,"put_bytes":157089,"put_count":84}} -{"i":16,"series":2,"stats":{"get_bytes":153399,"get_count":84,"put_bytes":155174,"put_count":83}} -{"i":17,"series":2,"stats":{"get_bytes":161654,"get_count":89,"put_bytes":163431,"put_count":88}} -{"i":18,"series":2,"stats":{"get_bytes":157490,"get_count":85,"put_bytes":159273,"put_count":84}} -{"i":19,"series":2,"stats":{"get_bytes":158532,"get_count":85,"put_bytes":160317,"put_count":84}} -{"i":20,"series":2,"stats":{"get_bytes":173672,"get_count":93,"put_bytes":175445,"put_count":92}} -{"i":21,"series":2,"stats":{"get_bytes":166720,"get_count":89,"put_bytes":168503,"put_count":88}} -{"i":22,"series":2,"stats":{"get_bytes":168101,"get_count":87,"put_bytes":169956,"put_count":87}} -{"i":23,"series":2,"stats":{"get_bytes":154171,"get_count":81,"put_bytes":155946,"put_count":80}} -{"i":24,"series":2,"stats":{"get_bytes":153916,"get_count":81,"put_bytes":155689,"put_count":80}} -{"i":25,"series":2,"stats":{"get_bytes":173727,"get_count":90,"put_bytes":175500,"put_count":89}} -{"i":26,"series":2,"stats":{"get_bytes":172555,"get_count":87,"put_bytes":174329,"put_count":86}} -{"i":27,"series":2,"stats":{"get_bytes":157897,"get_count":80,"put_bytes":159673,"put_count":79}} -{"i":28,"series":2,"stats":{"get_bytes":176015,"get_count":89,"put_bytes":177790,"put_count":88}} -{"i":29,"series":2,"stats":{"get_bytes":153602,"get_count":78,"put_bytes":155381,"put_count":77}} -{"i":30,"series":2,"stats":{"get_bytes":179929,"get_count":90,"put_bytes":181697,"put_count":89}} -{"i":31,"series":2,"stats":{"get_bytes":177035,"get_count":86,"put_bytes":178806,"put_count":85}} -{"i":32,"series":2,"stats":{"get_bytes":172539,"get_count":84,"put_bytes":174318,"put_count":83}} -{"i":33,"series":2,"stats":{"get_bytes":177122,"get_count":85,"put_bytes":178891,"put_count":84}} -{"i":34,"series":2,"stats":{"get_bytes":183439,"get_count":88,"put_bytes":185207,"put_count":87}} -{"i":35,"series":2,"stats":{"get_bytes":178417,"get_count":86,"put_bytes":180195,"put_count":85}} -{"i":36,"series":2,"stats":{"get_bytes":181727,"get_count":86,"put_bytes":183501,"put_count":85}} -{"i":37,"series":2,"stats":{"get_bytes":178489,"get_count":84,"put_bytes":180262,"put_count":83}} -{"i":38,"series":2,"stats":{"get_bytes":188242,"get_count":89,"put_bytes":190018,"put_count":88}} -{"i":39,"series":2,"stats":{"get_bytes":192113,"get_count":88,"put_bytes":193889,"put_count":87}} -{"i":40,"series":2,"stats":{"get_bytes":191380,"get_count":88,"put_bytes":193220,"put_count":88}} -{"i":41,"series":2,"stats":{"get_bytes":200355,"get_count":92,"put_bytes":202128,"put_count":91}} -{"i":42,"series":2,"stats":{"get_bytes":176399,"get_count":82,"put_bytes":178167,"put_count":81}} -{"i":43,"series":2,"stats":{"get_bytes":185422,"get_count":86,"put_bytes":187191,"put_count":85}} -{"i":44,"series":2,"stats":{"get_bytes":197277,"get_count":89,"put_bytes":199052,"put_count":88}} -{"i":45,"series":2,"stats":{"get_bytes":180394,"get_count":79,"put_bytes":182160,"put_count":78}} -{"i":46,"series":2,"stats":{"get_bytes":185558,"get_count":83,"put_bytes":187331,"put_count":82}} -{"i":47,"series":2,"stats":{"get_bytes":206641,"get_count":92,"put_bytes":208418,"put_count":91}} -{"i":48,"series":2,"stats":{"get_bytes":207995,"get_count":92,"put_bytes":209765,"put_count":91}} -{"i":49,"series":2,"stats":{"get_bytes":190466,"get_count":84,"put_bytes":192241,"put_count":83}} -{"i":50,"series":2,"stats":{"get_bytes":178030,"get_count":77,"put_bytes":179807,"put_count":76}} -{"i":51,"series":2,"stats":{"get_bytes":199612,"get_count":87,"put_bytes":201387,"put_count":86}} -{"i":52,"series":2,"stats":{"get_bytes":209834,"get_count":90,"put_bytes":211607,"put_count":89}} -{"i":53,"series":2,"stats":{"get_bytes":199663,"get_count":85,"put_bytes":201430,"put_count":84}} -{"i":54,"series":2,"stats":{"get_bytes":199872,"get_count":84,"put_bytes":201642,"put_count":83}} -{"i":55,"series":2,"stats":{"get_bytes":212308,"get_count":91,"put_bytes":214072,"put_count":90}} -{"i":56,"series":2,"stats":{"get_bytes":215905,"get_count":90,"put_bytes":217672,"put_count":89}} -{"i":57,"series":2,"stats":{"get_bytes":202816,"get_count":86,"put_bytes":204581,"put_count":85}} -{"i":58,"series":2,"stats":{"get_bytes":202211,"get_count":83,"put_bytes":203978,"put_count":82}} -{"i":59,"series":2,"stats":{"get_bytes":206061,"get_count":84,"put_bytes":207835,"put_count":83}} -{"i":60,"series":2,"stats":{"get_bytes":206608,"get_count":85,"put_bytes":208379,"put_count":84}} -{"i":61,"series":2,"stats":{"get_bytes":212754,"get_count":86,"put_bytes":214529,"put_count":85}} -{"i":62,"series":2,"stats":{"get_bytes":210182,"get_count":85,"put_bytes":211952,"put_count":84}} -{"i":63,"series":2,"stats":{"get_bytes":217747,"get_count":88,"put_bytes":219516,"put_count":87}} -{"i":64,"series":2,"stats":{"get_bytes":218136,"get_count":88,"put_bytes":219910,"put_count":87}} -{"i":65,"series":2,"stats":{"get_bytes":216594,"get_count":85,"put_bytes":218361,"put_count":84}} -{"i":66,"series":2,"stats":{"get_bytes":228775,"get_count":90,"put_bytes":230552,"put_count":89}} -{"i":67,"series":2,"stats":{"get_bytes":233992,"get_count":91,"put_bytes":235761,"put_count":90}} -{"i":68,"series":2,"stats":{"get_bytes":226690,"get_count":89,"put_bytes":228460,"put_count":88}} -{"i":69,"series":2,"stats":{"get_bytes":226093,"get_count":88,"put_bytes":227861,"put_count":87}} -{"i":70,"series":2,"stats":{"get_bytes":228183,"get_count":88,"put_bytes":229955,"put_count":87}} -{"i":71,"series":2,"stats":{"get_bytes":216394,"get_count":85,"put_bytes":218166,"put_count":84}} -{"i":72,"series":2,"stats":{"get_bytes":235823,"get_count":91,"put_bytes":237590,"put_count":90}} -{"i":73,"series":2,"stats":{"get_bytes":216677,"get_count":83,"put_bytes":218588,"put_count":84}} -{"i":74,"series":2,"stats":{"get_bytes":225441,"get_count":86,"put_bytes":227212,"put_count":85}} -{"i":75,"series":2,"stats":{"get_bytes":216075,"get_count":82,"put_bytes":217841,"put_count":81}} -{"i":76,"series":2,"stats":{"get_bytes":241198,"get_count":91,"put_bytes":242962,"put_count":90}} -{"i":77,"series":2,"stats":{"get_bytes":246037,"get_count":93,"put_bytes":247868,"put_count":93}} -{"i":78,"series":2,"stats":{"get_bytes":238781,"get_count":89,"put_bytes":240552,"put_count":88}} -{"i":79,"series":2,"stats":{"get_bytes":241251,"get_count":89,"put_bytes":243026,"put_count":88}} -{"i":80,"series":2,"stats":{"get_bytes":232284,"get_count":86,"put_bytes":234125,"put_count":86}} -{"i":81,"series":2,"stats":{"get_bytes":233160,"get_count":85,"put_bytes":234928,"put_count":84}} -{"i":82,"series":2,"stats":{"get_bytes":243991,"get_count":87,"put_bytes":245750,"put_count":86}} -{"i":83,"series":2,"stats":{"get_bytes":233702,"get_count":84,"put_bytes":235476,"put_count":83}} -{"i":84,"series":2,"stats":{"get_bytes":244326,"get_count":88,"put_bytes":246243,"put_count":89}} -{"i":85,"series":2,"stats":{"get_bytes":253255,"get_count":90,"put_bytes":255027,"put_count":89}} -{"i":86,"series":2,"stats":{"get_bytes":266227,"get_count":94,"put_bytes":268077,"put_count":94}} -{"i":87,"series":2,"stats":{"get_bytes":252145,"get_count":89,"put_bytes":253975,"put_count":89}} -{"i":88,"series":2,"stats":{"get_bytes":244183,"get_count":85,"put_bytes":245944,"put_count":84}} -{"i":89,"series":2,"stats":{"get_bytes":239608,"get_count":83,"put_bytes":241456,"put_count":83}} -{"i":90,"series":2,"stats":{"get_bytes":248855,"get_count":86,"put_bytes":250618,"put_count":85}} -{"i":91,"series":2,"stats":{"get_bytes":255937,"get_count":90,"put_bytes":257709,"put_count":89}} -{"i":92,"series":2,"stats":{"get_bytes":238236,"get_count":83,"put_bytes":240007,"put_count":82}} -{"i":93,"series":2,"stats":{"get_bytes":253838,"get_count":89,"put_bytes":255763,"put_count":90}} -{"i":94,"series":2,"stats":{"get_bytes":268140,"get_count":90,"put_bytes":269968,"put_count":90}} -{"i":95,"series":2,"stats":{"get_bytes":240255,"get_count":83,"put_bytes":242024,"put_count":82}} -{"i":96,"series":2,"stats":{"get_bytes":253966,"get_count":87,"put_bytes":255727,"put_count":86}} -{"i":97,"series":2,"stats":{"get_bytes":267604,"get_count":90,"put_bytes":269444,"put_count":90}} -{"i":98,"series":2,"stats":{"get_bytes":243444,"get_count":82,"put_bytes":245268,"put_count":82}} -{"i":99,"series":2,"stats":{"get_bytes":248233,"get_count":83,"put_bytes":250084,"put_count":83}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":12985,"put_count":128}} +{"i":2,"series":1,"stats":{"get_bytes":10211,"get_count":39,"put_bytes":18447,"put_count":126}} +{"i":3,"series":1,"stats":{"get_bytes":15154,"get_count":44,"put_bytes":23084,"put_count":127}} +{"i":4,"series":1,"stats":{"get_bytes":22384,"get_count":65,"put_bytes":29015,"put_count":129}} +{"i":5,"series":1,"stats":{"get_bytes":25985,"get_count":63,"put_bytes":32809,"put_count":130}} +{"i":6,"series":1,"stats":{"get_bytes":28428,"get_count":64,"put_bytes":34638,"put_count":122}} +{"i":7,"series":1,"stats":{"get_bytes":34408,"get_count":79,"put_bytes":40062,"put_count":129}} +{"i":8,"series":1,"stats":{"get_bytes":39961,"get_count":82,"put_bytes":45673,"put_count":133}} +{"i":9,"series":1,"stats":{"get_bytes":42029,"get_count":92,"put_bytes":46762,"put_count":129}} +{"i":10,"series":1,"stats":{"get_bytes":45715,"get_count":81,"put_bytes":51469,"put_count":132}} +{"i":11,"series":1,"stats":{"get_bytes":49696,"get_count":93,"put_bytes":54515,"put_count":131}} +{"i":12,"series":1,"stats":{"get_bytes":52164,"get_count":106,"put_bytes":56010,"put_count":130}} +{"i":13,"series":1,"stats":{"get_bytes":50580,"get_count":99,"put_bytes":54787,"put_count":128}} +{"i":14,"series":1,"stats":{"get_bytes":55870,"get_count":103,"put_bytes":59775,"put_count":128}} +{"i":15,"series":1,"stats":{"get_bytes":56795,"get_count":105,"put_bytes":60667,"put_count":129}} +{"i":16,"series":1,"stats":{"get_bytes":59531,"get_count":110,"put_bytes":63104,"put_count":130}} +{"i":17,"series":1,"stats":{"get_bytes":59946,"get_count":111,"put_bytes":63388,"put_count":129}} +{"i":18,"series":1,"stats":{"get_bytes":57360,"get_count":109,"put_bytes":60717,"put_count":126}} +{"i":19,"series":1,"stats":{"get_bytes":67465,"get_count":117,"put_bytes":70551,"put_count":130}} +{"i":20,"series":1,"stats":{"get_bytes":61499,"get_count":107,"put_bytes":64854,"put_count":124}} +{"i":21,"series":1,"stats":{"get_bytes":64324,"get_count":115,"put_bytes":67382,"put_count":128}} +{"i":22,"series":1,"stats":{"get_bytes":64015,"get_count":112,"put_bytes":67078,"put_count":125}} +{"i":23,"series":1,"stats":{"get_bytes":72005,"get_count":120,"put_bytes":74891,"put_count":130}} +{"i":24,"series":1,"stats":{"get_bytes":71164,"get_count":121,"put_bytes":74018,"put_count":131}} +{"i":25,"series":1,"stats":{"get_bytes":71688,"get_count":125,"put_bytes":74206,"put_count":130}} +{"i":26,"series":1,"stats":{"get_bytes":70582,"get_count":122,"put_bytes":73145,"put_count":128}} +{"i":27,"series":1,"stats":{"get_bytes":72469,"get_count":123,"put_bytes":74779,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":72814,"get_count":124,"put_bytes":75299,"put_count":129}} +{"i":29,"series":1,"stats":{"get_bytes":73038,"get_count":120,"put_bytes":75755,"put_count":128}} +{"i":30,"series":1,"stats":{"get_bytes":78115,"get_count":126,"put_bytes":80598,"put_count":131}} +{"i":31,"series":1,"stats":{"get_bytes":71793,"get_count":122,"put_bytes":74073,"put_count":124}} +{"i":32,"series":1,"stats":{"get_bytes":78950,"get_count":124,"put_bytes":81502,"put_count":130}} +{"i":33,"series":1,"stats":{"get_bytes":76082,"get_count":127,"put_bytes":78139,"put_count":126}} +{"i":34,"series":1,"stats":{"get_bytes":78285,"get_count":123,"put_bytes":80920,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":77256,"get_count":122,"put_bytes":79533,"put_count":124}} +{"i":36,"series":1,"stats":{"get_bytes":78418,"get_count":122,"put_bytes":80780,"put_count":125}} +{"i":37,"series":1,"stats":{"get_bytes":79451,"get_count":129,"put_bytes":81595,"put_count":129}} +{"i":38,"series":1,"stats":{"get_bytes":81457,"get_count":129,"put_bytes":83643,"put_count":130}} +{"i":39,"series":1,"stats":{"get_bytes":75464,"get_count":124,"put_bytes":77751,"put_count":126}} +{"i":40,"series":1,"stats":{"get_bytes":80913,"get_count":128,"put_bytes":83131,"put_count":129}} +{"i":41,"series":1,"stats":{"get_bytes":80407,"get_count":127,"put_bytes":82774,"put_count":130}} +{"i":42,"series":1,"stats":{"get_bytes":82877,"get_count":127,"put_bytes":85068,"put_count":128}} +{"i":43,"series":1,"stats":{"get_bytes":80876,"get_count":123,"put_bytes":82994,"put_count":123}} +{"i":44,"series":1,"stats":{"get_bytes":86523,"get_count":129,"put_bytes":88663,"put_count":129}} +{"i":45,"series":1,"stats":{"get_bytes":83642,"get_count":129,"put_bytes":85749,"put_count":129}} +{"i":46,"series":1,"stats":{"get_bytes":85511,"get_count":131,"put_bytes":87607,"put_count":131}} +{"i":47,"series":1,"stats":{"get_bytes":85112,"get_count":129,"put_bytes":87147,"put_count":128}} +{"i":48,"series":1,"stats":{"get_bytes":88700,"get_count":126,"put_bytes":90751,"put_count":125}} +{"i":49,"series":1,"stats":{"get_bytes":86599,"get_count":130,"put_bytes":88856,"put_count":132}} +{"i":50,"series":1,"stats":{"get_bytes":83343,"get_count":123,"put_bytes":85475,"put_count":123}} +{"i":51,"series":1,"stats":{"get_bytes":86737,"get_count":123,"put_bytes":88869,"put_count":123}} +{"i":52,"series":1,"stats":{"get_bytes":87509,"get_count":132,"put_bytes":89552,"put_count":131}} +{"i":53,"series":1,"stats":{"get_bytes":83973,"get_count":124,"put_bytes":86105,"put_count":124}} +{"i":54,"series":1,"stats":{"get_bytes":88890,"get_count":132,"put_bytes":90931,"put_count":131}} +{"i":55,"series":1,"stats":{"get_bytes":86483,"get_count":124,"put_bytes":88547,"put_count":123}} +{"i":56,"series":1,"stats":{"get_bytes":86737,"get_count":128,"put_bytes":88856,"put_count":128}} +{"i":57,"series":1,"stats":{"get_bytes":90763,"get_count":131,"put_bytes":92816,"put_count":130}} +{"i":58,"series":1,"stats":{"get_bytes":88326,"get_count":129,"put_bytes":90363,"put_count":128}} +{"i":59,"series":1,"stats":{"get_bytes":90383,"get_count":132,"put_bytes":92431,"put_count":131}} +{"i":60,"series":1,"stats":{"get_bytes":89174,"get_count":132,"put_bytes":91223,"put_count":131}} +{"i":61,"series":1,"stats":{"get_bytes":93524,"get_count":132,"put_bytes":95573,"put_count":131}} +{"i":62,"series":1,"stats":{"get_bytes":86253,"get_count":128,"put_bytes":88316,"put_count":127}} +{"i":63,"series":1,"stats":{"get_bytes":94435,"get_count":131,"put_bytes":96525,"put_count":131}} +{"i":64,"series":1,"stats":{"get_bytes":94384,"get_count":128,"put_bytes":96493,"put_count":128}} +{"i":65,"series":1,"stats":{"get_bytes":87052,"get_count":125,"put_bytes":89072,"put_count":124}} +{"i":66,"series":1,"stats":{"get_bytes":93194,"get_count":128,"put_bytes":95287,"put_count":128}} +{"i":67,"series":1,"stats":{"get_bytes":93462,"get_count":132,"put_bytes":95509,"put_count":131}} +{"i":68,"series":1,"stats":{"get_bytes":95363,"get_count":130,"put_bytes":97402,"put_count":129}} +{"i":69,"series":1,"stats":{"get_bytes":95468,"get_count":131,"put_bytes":97497,"put_count":130}} +{"i":70,"series":1,"stats":{"get_bytes":96314,"get_count":131,"put_bytes":98334,"put_count":130}} +{"i":71,"series":1,"stats":{"get_bytes":92797,"get_count":126,"put_bytes":94814,"put_count":125}} +{"i":72,"series":1,"stats":{"get_bytes":92589,"get_count":128,"put_bytes":94606,"put_count":127}} +{"i":73,"series":1,"stats":{"get_bytes":94086,"get_count":128,"put_bytes":96202,"put_count":128}} +{"i":74,"series":1,"stats":{"get_bytes":91593,"get_count":126,"put_bytes":93615,"put_count":125}} +{"i":75,"series":1,"stats":{"get_bytes":98705,"get_count":132,"put_bytes":100718,"put_count":131}} +{"i":76,"series":1,"stats":{"get_bytes":96803,"get_count":129,"put_bytes":98822,"put_count":128}} +{"i":77,"series":1,"stats":{"get_bytes":94873,"get_count":130,"put_bytes":96925,"put_count":129}} +{"i":78,"series":1,"stats":{"get_bytes":96562,"get_count":129,"put_bytes":98599,"put_count":128}} +{"i":79,"series":1,"stats":{"get_bytes":96338,"get_count":129,"put_bytes":98328,"put_count":128}} +{"i":80,"series":1,"stats":{"get_bytes":97218,"get_count":129,"put_bytes":99302,"put_count":129}} +{"i":81,"series":1,"stats":{"get_bytes":100293,"get_count":132,"put_bytes":102369,"put_count":132}} +{"i":82,"series":1,"stats":{"get_bytes":97092,"get_count":126,"put_bytes":99165,"put_count":126}} +{"i":83,"series":1,"stats":{"get_bytes":93984,"get_count":122,"put_bytes":96014,"put_count":121}} +{"i":84,"series":1,"stats":{"get_bytes":103254,"get_count":131,"put_bytes":105255,"put_count":130}} +{"i":85,"series":1,"stats":{"get_bytes":98209,"get_count":128,"put_bytes":100245,"put_count":127}} +{"i":86,"series":1,"stats":{"get_bytes":100671,"get_count":131,"put_bytes":102707,"put_count":130}} +{"i":87,"series":1,"stats":{"get_bytes":98575,"get_count":129,"put_bytes":100594,"put_count":128}} +{"i":88,"series":1,"stats":{"get_bytes":99376,"get_count":128,"put_bytes":101448,"put_count":128}} +{"i":89,"series":1,"stats":{"get_bytes":103166,"get_count":130,"put_bytes":105142,"put_count":129}} +{"i":90,"series":1,"stats":{"get_bytes":106255,"get_count":132,"put_bytes":108322,"put_count":132}} +{"i":91,"series":1,"stats":{"get_bytes":99795,"get_count":130,"put_bytes":101795,"put_count":129}} +{"i":92,"series":1,"stats":{"get_bytes":106360,"get_count":130,"put_bytes":108345,"put_count":129}} +{"i":93,"series":1,"stats":{"get_bytes":100558,"get_count":125,"put_bytes":102664,"put_count":126}} +{"i":94,"series":1,"stats":{"get_bytes":103544,"get_count":127,"put_bytes":105539,"put_count":126}} +{"i":95,"series":1,"stats":{"get_bytes":103653,"get_count":128,"put_bytes":105630,"put_count":127}} +{"i":96,"series":1,"stats":{"get_bytes":101148,"get_count":131,"put_bytes":103179,"put_count":130}} +{"i":97,"series":1,"stats":{"get_bytes":101806,"get_count":130,"put_bytes":103953,"put_count":131}} +{"i":98,"series":1,"stats":{"get_bytes":101247,"get_count":128,"put_bytes":103325,"put_count":128}} +{"i":99,"series":1,"stats":{"get_bytes":104300,"get_count":130,"put_bytes":106306,"put_count":129}} +{"i":0,"series":2,"stats":{"get_bytes":107175,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":105892,"get_count":128,"put_bytes":107885,"put_count":127}} +{"i":2,"series":2,"stats":{"get_bytes":105041,"get_count":129,"put_bytes":107015,"put_count":128}} +{"i":3,"series":2,"stats":{"get_bytes":107752,"get_count":132,"put_bytes":109733,"put_count":131}} +{"i":4,"series":2,"stats":{"get_bytes":100926,"get_count":127,"put_bytes":102985,"put_count":127}} +{"i":5,"series":2,"stats":{"get_bytes":106895,"get_count":130,"put_bytes":108873,"put_count":129}} +{"i":6,"series":2,"stats":{"get_bytes":110493,"get_count":131,"put_bytes":112556,"put_count":131}} +{"i":7,"series":2,"stats":{"get_bytes":109805,"get_count":128,"put_bytes":111762,"put_count":127}} +{"i":8,"series":2,"stats":{"get_bytes":112945,"get_count":133,"put_bytes":114994,"put_count":133}} +{"i":9,"series":2,"stats":{"get_bytes":111329,"get_count":131,"put_bytes":113313,"put_count":130}} +{"i":10,"series":2,"stats":{"get_bytes":107197,"get_count":128,"put_bytes":109184,"put_count":127}} +{"i":11,"series":2,"stats":{"get_bytes":109496,"get_count":128,"put_bytes":111556,"put_count":128}} +{"i":12,"series":2,"stats":{"get_bytes":108052,"get_count":126,"put_bytes":110027,"put_count":125}} +{"i":13,"series":2,"stats":{"get_bytes":108081,"get_count":128,"put_bytes":110057,"put_count":127}} +{"i":14,"series":2,"stats":{"get_bytes":111370,"get_count":130,"put_bytes":113411,"put_count":130}} +{"i":15,"series":2,"stats":{"get_bytes":99536,"get_count":123,"put_bytes":101541,"put_count":122}} +{"i":16,"series":2,"stats":{"get_bytes":111392,"get_count":129,"put_bytes":113462,"put_count":129}} +{"i":17,"series":2,"stats":{"get_bytes":112540,"get_count":129,"put_bytes":114770,"put_count":131}} +{"i":18,"series":2,"stats":{"get_bytes":113781,"get_count":129,"put_bytes":115734,"put_count":128}} +{"i":19,"series":2,"stats":{"get_bytes":110440,"get_count":125,"put_bytes":112451,"put_count":124}} +{"i":20,"series":2,"stats":{"get_bytes":111409,"get_count":128,"put_bytes":113470,"put_count":128}} +{"i":21,"series":2,"stats":{"get_bytes":117088,"get_count":132,"put_bytes":119128,"put_count":132}} +{"i":22,"series":2,"stats":{"get_bytes":113964,"get_count":129,"put_bytes":115930,"put_count":128}} +{"i":23,"series":2,"stats":{"get_bytes":111619,"get_count":125,"put_bytes":113596,"put_count":124}} +{"i":24,"series":2,"stats":{"get_bytes":114800,"get_count":129,"put_bytes":116776,"put_count":128}} +{"i":25,"series":2,"stats":{"get_bytes":115672,"get_count":126,"put_bytes":117736,"put_count":126}} +{"i":26,"series":2,"stats":{"get_bytes":117101,"get_count":131,"put_bytes":119167,"put_count":131}} +{"i":27,"series":2,"stats":{"get_bytes":119142,"get_count":133,"put_bytes":121117,"put_count":132}} +{"i":28,"series":2,"stats":{"get_bytes":117093,"get_count":133,"put_bytes":119136,"put_count":133}} +{"i":29,"series":2,"stats":{"get_bytes":110498,"get_count":126,"put_bytes":112481,"put_count":125}} +{"i":30,"series":2,"stats":{"get_bytes":115444,"get_count":129,"put_bytes":117557,"put_count":130}} +{"i":31,"series":2,"stats":{"get_bytes":118258,"get_count":130,"put_bytes":120205,"put_count":129}} +{"i":32,"series":2,"stats":{"get_bytes":118663,"get_count":132,"put_bytes":120756,"put_count":133}} +{"i":33,"series":2,"stats":{"get_bytes":112846,"get_count":128,"put_bytes":114802,"put_count":127}} +{"i":34,"series":2,"stats":{"get_bytes":119046,"get_count":129,"put_bytes":121005,"put_count":128}} +{"i":35,"series":2,"stats":{"get_bytes":120245,"get_count":128,"put_bytes":122209,"put_count":127}} +{"i":36,"series":2,"stats":{"get_bytes":119207,"get_count":129,"put_bytes":121295,"put_count":130}} +{"i":37,"series":2,"stats":{"get_bytes":117142,"get_count":126,"put_bytes":119110,"put_count":125}} +{"i":38,"series":2,"stats":{"get_bytes":116074,"get_count":127,"put_bytes":118006,"put_count":126}} +{"i":39,"series":2,"stats":{"get_bytes":113919,"get_count":127,"put_bytes":116078,"put_count":128}} +{"i":40,"series":2,"stats":{"get_bytes":114291,"get_count":126,"put_bytes":116256,"put_count":125}} +{"i":41,"series":2,"stats":{"get_bytes":118343,"get_count":130,"put_bytes":120291,"put_count":129}} +{"i":42,"series":2,"stats":{"get_bytes":114208,"get_count":125,"put_bytes":116166,"put_count":124}} +{"i":43,"series":2,"stats":{"get_bytes":120742,"get_count":129,"put_bytes":122742,"put_count":128}} +{"i":44,"series":2,"stats":{"get_bytes":120506,"get_count":128,"put_bytes":122549,"put_count":128}} +{"i":45,"series":2,"stats":{"get_bytes":124250,"get_count":132,"put_bytes":126269,"put_count":132}} +{"i":46,"series":2,"stats":{"get_bytes":124093,"get_count":132,"put_bytes":126231,"put_count":133}} +{"i":47,"series":2,"stats":{"get_bytes":121883,"get_count":128,"put_bytes":123981,"put_count":129}} +{"i":48,"series":2,"stats":{"get_bytes":122169,"get_count":130,"put_bytes":124105,"put_count":129}} +{"i":49,"series":2,"stats":{"get_bytes":123482,"get_count":131,"put_bytes":125524,"put_count":131}} +{"i":50,"series":2,"stats":{"get_bytes":120478,"get_count":128,"put_bytes":122608,"put_count":129}} +{"i":51,"series":2,"stats":{"get_bytes":123511,"get_count":132,"put_bytes":125502,"put_count":131}} +{"i":52,"series":2,"stats":{"get_bytes":124946,"get_count":132,"put_bytes":126852,"put_count":131}} +{"i":53,"series":2,"stats":{"get_bytes":119593,"get_count":125,"put_bytes":121563,"put_count":124}} +{"i":54,"series":2,"stats":{"get_bytes":121582,"get_count":130,"put_bytes":123677,"put_count":131}} +{"i":55,"series":2,"stats":{"get_bytes":122011,"get_count":127,"put_bytes":124065,"put_count":127}} +{"i":56,"series":2,"stats":{"get_bytes":123491,"get_count":131,"put_bytes":125503,"put_count":131}} +{"i":57,"series":2,"stats":{"get_bytes":123674,"get_count":129,"put_bytes":125689,"put_count":129}} +{"i":58,"series":2,"stats":{"get_bytes":130858,"get_count":134,"put_bytes":132959,"put_count":135}} +{"i":59,"series":2,"stats":{"get_bytes":130348,"get_count":129,"put_bytes":132252,"put_count":128}} +{"i":60,"series":2,"stats":{"get_bytes":131679,"get_count":132,"put_bytes":133614,"put_count":131}} +{"i":61,"series":2,"stats":{"get_bytes":131114,"get_count":129,"put_bytes":133197,"put_count":130}} +{"i":62,"series":2,"stats":{"get_bytes":124596,"get_count":127,"put_bytes":126633,"put_count":127}} +{"i":63,"series":2,"stats":{"get_bytes":121440,"get_count":126,"put_bytes":123589,"put_count":128}} +{"i":64,"series":2,"stats":{"get_bytes":128756,"get_count":127,"put_bytes":130916,"put_count":129}} +{"i":65,"series":2,"stats":{"get_bytes":130453,"get_count":130,"put_bytes":132548,"put_count":131}} +{"i":66,"series":2,"stats":{"get_bytes":129379,"get_count":129,"put_bytes":131478,"put_count":130}} +{"i":67,"series":2,"stats":{"get_bytes":132308,"get_count":130,"put_bytes":134306,"put_count":130}} +{"i":68,"series":2,"stats":{"get_bytes":132270,"get_count":131,"put_bytes":134288,"put_count":131}} +{"i":69,"series":2,"stats":{"get_bytes":129616,"get_count":130,"put_bytes":131616,"put_count":130}} +{"i":70,"series":2,"stats":{"get_bytes":132413,"get_count":131,"put_bytes":134364,"put_count":130}} +{"i":71,"series":2,"stats":{"get_bytes":130045,"get_count":129,"put_bytes":132073,"put_count":129}} +{"i":72,"series":2,"stats":{"get_bytes":128112,"get_count":128,"put_bytes":130052,"put_count":127}} +{"i":73,"series":2,"stats":{"get_bytes":129276,"get_count":130,"put_bytes":131338,"put_count":131}} +{"i":74,"series":2,"stats":{"get_bytes":126795,"get_count":127,"put_bytes":128724,"put_count":126}} +{"i":75,"series":2,"stats":{"get_bytes":128572,"get_count":128,"put_bytes":130582,"put_count":128}} +{"i":76,"series":2,"stats":{"get_bytes":131583,"get_count":132,"put_bytes":133656,"put_count":133}} +{"i":77,"series":2,"stats":{"get_bytes":136555,"get_count":129,"put_bytes":138485,"put_count":128}} +{"i":78,"series":2,"stats":{"get_bytes":134366,"get_count":129,"put_bytes":136294,"put_count":128}} +{"i":79,"series":2,"stats":{"get_bytes":133710,"get_count":129,"put_bytes":135638,"put_count":128}} +{"i":80,"series":2,"stats":{"get_bytes":136585,"get_count":133,"put_bytes":138739,"put_count":135}} +{"i":81,"series":2,"stats":{"get_bytes":130286,"get_count":128,"put_bytes":132385,"put_count":129}} +{"i":82,"series":2,"stats":{"get_bytes":137820,"get_count":132,"put_bytes":139812,"put_count":132}} +{"i":83,"series":2,"stats":{"get_bytes":134583,"get_count":128,"put_bytes":136567,"put_count":128}} +{"i":84,"series":2,"stats":{"get_bytes":133301,"get_count":128,"put_bytes":135324,"put_count":128}} +{"i":85,"series":2,"stats":{"get_bytes":133877,"get_count":127,"put_bytes":135875,"put_count":127}} +{"i":86,"series":2,"stats":{"get_bytes":129985,"get_count":126,"put_bytes":132035,"put_count":127}} +{"i":87,"series":2,"stats":{"get_bytes":140246,"get_count":131,"put_bytes":142162,"put_count":130}} +{"i":88,"series":2,"stats":{"get_bytes":133745,"get_count":130,"put_bytes":135755,"put_count":130}} +{"i":89,"series":2,"stats":{"get_bytes":131055,"get_count":126,"put_bytes":133307,"put_count":129}} +{"i":90,"series":2,"stats":{"get_bytes":133072,"get_count":131,"put_bytes":135260,"put_count":133}} +{"i":91,"series":2,"stats":{"get_bytes":139130,"get_count":131,"put_bytes":141062,"put_count":130}} +{"i":92,"series":2,"stats":{"get_bytes":134456,"get_count":127,"put_bytes":136540,"put_count":128}} +{"i":93,"series":2,"stats":{"get_bytes":134732,"get_count":127,"put_bytes":136868,"put_count":129}} +{"i":94,"series":2,"stats":{"get_bytes":137970,"get_count":131,"put_bytes":139898,"put_count":130}} +{"i":95,"series":2,"stats":{"get_bytes":135161,"get_count":127,"put_bytes":137247,"put_count":128}} +{"i":96,"series":2,"stats":{"get_bytes":139119,"get_count":131,"put_bytes":141119,"put_count":131}} +{"i":97,"series":2,"stats":{"get_bytes":139486,"get_count":130,"put_bytes":141641,"put_count":132}} +{"i":98,"series":2,"stats":{"get_bytes":142320,"get_count":130,"put_bytes":144500,"put_count":132}} +{"i":99,"series":2,"stats":{"get_bytes":132169,"get_count":126,"put_bytes":134240,"put_count":127}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.png b/actors/evm/tests/measurements/mapping_add_n100.png index fa4a4fa0d9f0127beeb34ce922379fb5fe75f830..c0006af9d4f96f4b26936be6fd071651681dcb09 100644 GIT binary patch literal 20965 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 URV;W@2lvYjP$uUqbn&GB1EfuG00000 diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index f04066520..56688786f 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":19141,"get_count":11,"put_bytes":17133,"put_count":10}} -{"i":1,"series":1,"stats":{"get_bytes":19314,"get_count":12,"put_bytes":17306,"put_count":11}} -{"i":2,"series":1,"stats":{"get_bytes":18760,"get_count":9,"put_bytes":16752,"put_count":8}} -{"i":3,"series":1,"stats":{"get_bytes":19326,"get_count":10,"put_bytes":17318,"put_count":9}} -{"i":4,"series":1,"stats":{"get_bytes":19969,"get_count":13,"put_bytes":17961,"put_count":12}} -{"i":5,"series":1,"stats":{"get_bytes":18613,"get_count":9,"put_bytes":16605,"put_count":8}} -{"i":6,"series":1,"stats":{"get_bytes":19962,"get_count":12,"put_bytes":17954,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":19036,"get_count":10,"put_bytes":17028,"put_count":9}} -{"i":8,"series":1,"stats":{"get_bytes":19611,"get_count":12,"put_bytes":17603,"put_count":11}} -{"i":9,"series":1,"stats":{"get_bytes":19690,"get_count":12,"put_bytes":17682,"put_count":11}} -{"i":10,"series":1,"stats":{"get_bytes":19134,"get_count":11,"put_bytes":17126,"put_count":10}} -{"i":11,"series":1,"stats":{"get_bytes":19525,"get_count":11,"put_bytes":17517,"put_count":10}} -{"i":12,"series":1,"stats":{"get_bytes":19003,"get_count":10,"put_bytes":16995,"put_count":9}} -{"i":13,"series":1,"stats":{"get_bytes":19594,"get_count":12,"put_bytes":17586,"put_count":11}} -{"i":14,"series":1,"stats":{"get_bytes":19832,"get_count":12,"put_bytes":17824,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":19631,"get_count":11,"put_bytes":17623,"put_count":10}} -{"i":16,"series":1,"stats":{"get_bytes":19169,"get_count":10,"put_bytes":17161,"put_count":9}} -{"i":17,"series":1,"stats":{"get_bytes":19166,"get_count":11,"put_bytes":17158,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":18689,"get_count":10,"put_bytes":16681,"put_count":9}} -{"i":19,"series":1,"stats":{"get_bytes":19361,"get_count":12,"put_bytes":17353,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":19702,"get_count":13,"put_bytes":17694,"put_count":12}} -{"i":21,"series":1,"stats":{"get_bytes":19114,"get_count":11,"put_bytes":17106,"put_count":10}} -{"i":22,"series":1,"stats":{"get_bytes":19420,"get_count":12,"put_bytes":17412,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":18927,"get_count":10,"put_bytes":16919,"put_count":9}} -{"i":24,"series":1,"stats":{"get_bytes":19251,"get_count":11,"put_bytes":17243,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":19533,"get_count":12,"put_bytes":17525,"put_count":11}} -{"i":26,"series":1,"stats":{"get_bytes":19845,"get_count":13,"put_bytes":17837,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":19548,"get_count":12,"put_bytes":17540,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":18847,"get_count":9,"put_bytes":16839,"put_count":8}} -{"i":29,"series":1,"stats":{"get_bytes":19487,"get_count":10,"put_bytes":17479,"put_count":9}} -{"i":30,"series":1,"stats":{"get_bytes":19051,"get_count":10,"put_bytes":17043,"put_count":9}} -{"i":31,"series":1,"stats":{"get_bytes":18654,"get_count":8,"put_bytes":16646,"put_count":7}} -{"i":32,"series":1,"stats":{"get_bytes":18681,"get_count":9,"put_bytes":16673,"put_count":8}} -{"i":33,"series":1,"stats":{"get_bytes":19974,"get_count":12,"put_bytes":17966,"put_count":11}} -{"i":34,"series":1,"stats":{"get_bytes":19252,"get_count":11,"put_bytes":17244,"put_count":10}} -{"i":35,"series":1,"stats":{"get_bytes":19150,"get_count":10,"put_bytes":17142,"put_count":9}} -{"i":36,"series":1,"stats":{"get_bytes":18471,"get_count":9,"put_bytes":16463,"put_count":8}} -{"i":37,"series":1,"stats":{"get_bytes":18515,"get_count":9,"put_bytes":16507,"put_count":8}} -{"i":38,"series":1,"stats":{"get_bytes":18782,"get_count":9,"put_bytes":16774,"put_count":8}} -{"i":39,"series":1,"stats":{"get_bytes":19146,"get_count":9,"put_bytes":17138,"put_count":8}} -{"i":40,"series":1,"stats":{"get_bytes":19458,"get_count":12,"put_bytes":17450,"put_count":11}} -{"i":41,"series":1,"stats":{"get_bytes":19549,"get_count":11,"put_bytes":17541,"put_count":10}} -{"i":42,"series":1,"stats":{"get_bytes":19356,"get_count":11,"put_bytes":17348,"put_count":10}} -{"i":43,"series":1,"stats":{"get_bytes":19541,"get_count":11,"put_bytes":17533,"put_count":10}} -{"i":44,"series":1,"stats":{"get_bytes":18546,"get_count":9,"put_bytes":16538,"put_count":8}} -{"i":45,"series":1,"stats":{"get_bytes":18279,"get_count":7,"put_bytes":16271,"put_count":6}} -{"i":46,"series":1,"stats":{"get_bytes":18720,"get_count":9,"put_bytes":16712,"put_count":8}} -{"i":47,"series":1,"stats":{"get_bytes":18886,"get_count":9,"put_bytes":16878,"put_count":8}} -{"i":48,"series":1,"stats":{"get_bytes":18717,"get_count":9,"put_bytes":16709,"put_count":8}} -{"i":49,"series":1,"stats":{"get_bytes":19251,"get_count":10,"put_bytes":17243,"put_count":9}} -{"i":50,"series":1,"stats":{"get_bytes":18082,"get_count":7,"put_bytes":16074,"put_count":6}} -{"i":51,"series":1,"stats":{"get_bytes":18994,"get_count":10,"put_bytes":16986,"put_count":9}} -{"i":52,"series":1,"stats":{"get_bytes":18801,"get_count":9,"put_bytes":16793,"put_count":8}} -{"i":53,"series":1,"stats":{"get_bytes":19366,"get_count":11,"put_bytes":17358,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":18154,"get_count":7,"put_bytes":16146,"put_count":6}} -{"i":55,"series":1,"stats":{"get_bytes":19354,"get_count":11,"put_bytes":17346,"put_count":10}} -{"i":56,"series":1,"stats":{"get_bytes":19352,"get_count":10,"put_bytes":17344,"put_count":9}} -{"i":57,"series":1,"stats":{"get_bytes":19232,"get_count":11,"put_bytes":17224,"put_count":10}} -{"i":58,"series":1,"stats":{"get_bytes":19399,"get_count":11,"put_bytes":17391,"put_count":10}} -{"i":59,"series":1,"stats":{"get_bytes":19526,"get_count":12,"put_bytes":17518,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":19654,"get_count":12,"put_bytes":17646,"put_count":11}} -{"i":61,"series":1,"stats":{"get_bytes":19033,"get_count":10,"put_bytes":17025,"put_count":9}} -{"i":62,"series":1,"stats":{"get_bytes":19058,"get_count":10,"put_bytes":17050,"put_count":9}} -{"i":63,"series":1,"stats":{"get_bytes":19128,"get_count":11,"put_bytes":17120,"put_count":10}} -{"i":64,"series":1,"stats":{"get_bytes":19272,"get_count":11,"put_bytes":17264,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":19574,"get_count":13,"put_bytes":17566,"put_count":12}} -{"i":66,"series":1,"stats":{"get_bytes":18992,"get_count":10,"put_bytes":16984,"put_count":9}} -{"i":67,"series":1,"stats":{"get_bytes":19151,"get_count":11,"put_bytes":17143,"put_count":10}} -{"i":68,"series":1,"stats":{"get_bytes":19530,"get_count":12,"put_bytes":17522,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":18921,"get_count":10,"put_bytes":16913,"put_count":9}} -{"i":70,"series":1,"stats":{"get_bytes":19254,"get_count":10,"put_bytes":17246,"put_count":9}} -{"i":71,"series":1,"stats":{"get_bytes":18503,"get_count":8,"put_bytes":16495,"put_count":7}} -{"i":72,"series":1,"stats":{"get_bytes":19942,"get_count":12,"put_bytes":17934,"put_count":11}} -{"i":73,"series":1,"stats":{"get_bytes":19120,"get_count":10,"put_bytes":17112,"put_count":9}} -{"i":74,"series":1,"stats":{"get_bytes":19361,"get_count":11,"put_bytes":17353,"put_count":10}} -{"i":75,"series":1,"stats":{"get_bytes":19275,"get_count":11,"put_bytes":17267,"put_count":10}} -{"i":76,"series":1,"stats":{"get_bytes":19310,"get_count":10,"put_bytes":17302,"put_count":9}} -{"i":77,"series":1,"stats":{"get_bytes":19225,"get_count":10,"put_bytes":17217,"put_count":9}} -{"i":78,"series":1,"stats":{"get_bytes":18852,"get_count":8,"put_bytes":16844,"put_count":7}} -{"i":79,"series":1,"stats":{"get_bytes":19177,"get_count":10,"put_bytes":17169,"put_count":9}} -{"i":80,"series":1,"stats":{"get_bytes":19759,"get_count":13,"put_bytes":17751,"put_count":12}} -{"i":81,"series":1,"stats":{"get_bytes":19454,"get_count":11,"put_bytes":17446,"put_count":10}} -{"i":82,"series":1,"stats":{"get_bytes":18784,"get_count":9,"put_bytes":16776,"put_count":8}} -{"i":83,"series":1,"stats":{"get_bytes":19238,"get_count":10,"put_bytes":17230,"put_count":9}} -{"i":84,"series":1,"stats":{"get_bytes":18230,"get_count":7,"put_bytes":16222,"put_count":6}} -{"i":85,"series":1,"stats":{"get_bytes":18544,"get_count":9,"put_bytes":16536,"put_count":8}} -{"i":86,"series":1,"stats":{"get_bytes":19755,"get_count":12,"put_bytes":17747,"put_count":11}} -{"i":87,"series":1,"stats":{"get_bytes":19513,"get_count":12,"put_bytes":17505,"put_count":11}} -{"i":88,"series":1,"stats":{"get_bytes":19301,"get_count":11,"put_bytes":17293,"put_count":10}} -{"i":89,"series":1,"stats":{"get_bytes":19120,"get_count":11,"put_bytes":17112,"put_count":10}} -{"i":90,"series":1,"stats":{"get_bytes":19559,"get_count":12,"put_bytes":17551,"put_count":11}} -{"i":91,"series":1,"stats":{"get_bytes":19126,"get_count":11,"put_bytes":17118,"put_count":10}} -{"i":92,"series":1,"stats":{"get_bytes":18852,"get_count":10,"put_bytes":16844,"put_count":9}} -{"i":93,"series":1,"stats":{"get_bytes":18976,"get_count":10,"put_bytes":16968,"put_count":9}} -{"i":94,"series":1,"stats":{"get_bytes":18713,"get_count":8,"put_bytes":16705,"put_count":7}} -{"i":95,"series":1,"stats":{"get_bytes":19068,"get_count":10,"put_bytes":17060,"put_count":9}} -{"i":96,"series":1,"stats":{"get_bytes":18402,"get_count":7,"put_bytes":16394,"put_count":6}} -{"i":97,"series":1,"stats":{"get_bytes":18463,"get_count":8,"put_bytes":16455,"put_count":7}} -{"i":98,"series":1,"stats":{"get_bytes":19156,"get_count":10,"put_bytes":17148,"put_count":9}} -{"i":99,"series":1,"stats":{"get_bytes":19143,"get_count":10,"put_bytes":17135,"put_count":9}} -{"i":0,"series":2,"stats":{"get_bytes":19141,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":19314,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":2,"series":2,"stats":{"get_bytes":18760,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":3,"series":2,"stats":{"get_bytes":19326,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":4,"series":2,"stats":{"get_bytes":19969,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":5,"series":2,"stats":{"get_bytes":18613,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":6,"series":2,"stats":{"get_bytes":19962,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":7,"series":2,"stats":{"get_bytes":19036,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":8,"series":2,"stats":{"get_bytes":19611,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":9,"series":2,"stats":{"get_bytes":19690,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":10,"series":2,"stats":{"get_bytes":19134,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":11,"series":2,"stats":{"get_bytes":19525,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":12,"series":2,"stats":{"get_bytes":19003,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":13,"series":2,"stats":{"get_bytes":19594,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":14,"series":2,"stats":{"get_bytes":19832,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":15,"series":2,"stats":{"get_bytes":19631,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":16,"series":2,"stats":{"get_bytes":19169,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":17,"series":2,"stats":{"get_bytes":19166,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":18,"series":2,"stats":{"get_bytes":18689,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":19,"series":2,"stats":{"get_bytes":19361,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":20,"series":2,"stats":{"get_bytes":19702,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":21,"series":2,"stats":{"get_bytes":19114,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":22,"series":2,"stats":{"get_bytes":19420,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":23,"series":2,"stats":{"get_bytes":18927,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":24,"series":2,"stats":{"get_bytes":19251,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":25,"series":2,"stats":{"get_bytes":19533,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":26,"series":2,"stats":{"get_bytes":19845,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":27,"series":2,"stats":{"get_bytes":19548,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":28,"series":2,"stats":{"get_bytes":18847,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":29,"series":2,"stats":{"get_bytes":19487,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":30,"series":2,"stats":{"get_bytes":19051,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":31,"series":2,"stats":{"get_bytes":18654,"get_count":8,"put_bytes":0,"put_count":0}} -{"i":32,"series":2,"stats":{"get_bytes":18681,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":33,"series":2,"stats":{"get_bytes":19974,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":34,"series":2,"stats":{"get_bytes":19252,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":35,"series":2,"stats":{"get_bytes":19150,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":36,"series":2,"stats":{"get_bytes":18471,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":37,"series":2,"stats":{"get_bytes":18515,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":38,"series":2,"stats":{"get_bytes":18782,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":39,"series":2,"stats":{"get_bytes":19146,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":40,"series":2,"stats":{"get_bytes":19458,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":41,"series":2,"stats":{"get_bytes":19549,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":42,"series":2,"stats":{"get_bytes":19356,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":43,"series":2,"stats":{"get_bytes":19541,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":44,"series":2,"stats":{"get_bytes":18546,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":45,"series":2,"stats":{"get_bytes":18279,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":46,"series":2,"stats":{"get_bytes":18720,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":47,"series":2,"stats":{"get_bytes":18886,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":48,"series":2,"stats":{"get_bytes":18717,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":49,"series":2,"stats":{"get_bytes":19251,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":50,"series":2,"stats":{"get_bytes":18082,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":51,"series":2,"stats":{"get_bytes":18994,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":52,"series":2,"stats":{"get_bytes":18801,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":53,"series":2,"stats":{"get_bytes":19366,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":54,"series":2,"stats":{"get_bytes":18154,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":55,"series":2,"stats":{"get_bytes":19354,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":56,"series":2,"stats":{"get_bytes":19352,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":57,"series":2,"stats":{"get_bytes":19232,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":58,"series":2,"stats":{"get_bytes":19399,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":59,"series":2,"stats":{"get_bytes":19526,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":60,"series":2,"stats":{"get_bytes":19654,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":61,"series":2,"stats":{"get_bytes":19033,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":62,"series":2,"stats":{"get_bytes":19058,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":63,"series":2,"stats":{"get_bytes":19128,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":64,"series":2,"stats":{"get_bytes":19272,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":65,"series":2,"stats":{"get_bytes":19574,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":66,"series":2,"stats":{"get_bytes":18992,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":67,"series":2,"stats":{"get_bytes":19151,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":68,"series":2,"stats":{"get_bytes":19530,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":69,"series":2,"stats":{"get_bytes":18921,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":70,"series":2,"stats":{"get_bytes":19254,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":71,"series":2,"stats":{"get_bytes":18503,"get_count":8,"put_bytes":0,"put_count":0}} -{"i":72,"series":2,"stats":{"get_bytes":19942,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":73,"series":2,"stats":{"get_bytes":19120,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":74,"series":2,"stats":{"get_bytes":19361,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":75,"series":2,"stats":{"get_bytes":19275,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":76,"series":2,"stats":{"get_bytes":19310,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":77,"series":2,"stats":{"get_bytes":19225,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":78,"series":2,"stats":{"get_bytes":18852,"get_count":8,"put_bytes":0,"put_count":0}} -{"i":79,"series":2,"stats":{"get_bytes":19177,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":80,"series":2,"stats":{"get_bytes":19759,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":81,"series":2,"stats":{"get_bytes":19454,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":82,"series":2,"stats":{"get_bytes":18784,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":83,"series":2,"stats":{"get_bytes":19238,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":84,"series":2,"stats":{"get_bytes":18230,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":85,"series":2,"stats":{"get_bytes":18544,"get_count":9,"put_bytes":0,"put_count":0}} -{"i":86,"series":2,"stats":{"get_bytes":19755,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":87,"series":2,"stats":{"get_bytes":19513,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":88,"series":2,"stats":{"get_bytes":19301,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":89,"series":2,"stats":{"get_bytes":19120,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":90,"series":2,"stats":{"get_bytes":19559,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":91,"series":2,"stats":{"get_bytes":19126,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":92,"series":2,"stats":{"get_bytes":18852,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":93,"series":2,"stats":{"get_bytes":18976,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":94,"series":2,"stats":{"get_bytes":18713,"get_count":8,"put_bytes":0,"put_count":0}} -{"i":95,"series":2,"stats":{"get_bytes":19068,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":96,"series":2,"stats":{"get_bytes":18402,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":97,"series":2,"stats":{"get_bytes":18463,"get_count":8,"put_bytes":0,"put_count":0}} -{"i":98,"series":2,"stats":{"get_bytes":19156,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":99,"series":2,"stats":{"get_bytes":19143,"get_count":10,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":15423,"get_count":21,"put_bytes":13415,"put_count":20}} +{"i":1,"series":1,"stats":{"get_bytes":16837,"get_count":22,"put_bytes":14829,"put_count":21}} +{"i":2,"series":1,"stats":{"get_bytes":14911,"get_count":21,"put_bytes":12903,"put_count":20}} +{"i":3,"series":1,"stats":{"get_bytes":16353,"get_count":21,"put_bytes":14345,"put_count":20}} +{"i":4,"series":1,"stats":{"get_bytes":15625,"get_count":21,"put_bytes":13617,"put_count":20}} +{"i":5,"series":1,"stats":{"get_bytes":17347,"get_count":23,"put_bytes":15339,"put_count":22}} +{"i":6,"series":1,"stats":{"get_bytes":16998,"get_count":22,"put_bytes":14990,"put_count":21}} +{"i":7,"series":1,"stats":{"get_bytes":16912,"get_count":22,"put_bytes":14904,"put_count":21}} +{"i":8,"series":1,"stats":{"get_bytes":16611,"get_count":21,"put_bytes":14603,"put_count":20}} +{"i":9,"series":1,"stats":{"get_bytes":16596,"get_count":22,"put_bytes":14588,"put_count":21}} +{"i":10,"series":1,"stats":{"get_bytes":18179,"get_count":23,"put_bytes":16171,"put_count":22}} +{"i":11,"series":1,"stats":{"get_bytes":15314,"get_count":21,"put_bytes":13306,"put_count":20}} +{"i":12,"series":1,"stats":{"get_bytes":15427,"get_count":21,"put_bytes":13419,"put_count":20}} +{"i":13,"series":1,"stats":{"get_bytes":17974,"get_count":23,"put_bytes":15966,"put_count":22}} +{"i":14,"series":1,"stats":{"get_bytes":16944,"get_count":23,"put_bytes":14936,"put_count":22}} +{"i":15,"series":1,"stats":{"get_bytes":15776,"get_count":21,"put_bytes":13768,"put_count":20}} +{"i":16,"series":1,"stats":{"get_bytes":13729,"get_count":20,"put_bytes":11721,"put_count":19}} +{"i":17,"series":1,"stats":{"get_bytes":14287,"get_count":20,"put_bytes":12279,"put_count":19}} +{"i":18,"series":1,"stats":{"get_bytes":17681,"get_count":22,"put_bytes":15673,"put_count":21}} +{"i":19,"series":1,"stats":{"get_bytes":16726,"get_count":22,"put_bytes":14718,"put_count":21}} +{"i":20,"series":1,"stats":{"get_bytes":17079,"get_count":22,"put_bytes":15071,"put_count":21}} +{"i":21,"series":1,"stats":{"get_bytes":15444,"get_count":21,"put_bytes":13436,"put_count":20}} +{"i":22,"series":1,"stats":{"get_bytes":18395,"get_count":23,"put_bytes":16387,"put_count":22}} +{"i":23,"series":1,"stats":{"get_bytes":14430,"get_count":20,"put_bytes":12422,"put_count":19}} +{"i":24,"series":1,"stats":{"get_bytes":17108,"get_count":22,"put_bytes":15100,"put_count":21}} +{"i":25,"series":1,"stats":{"get_bytes":17248,"get_count":22,"put_bytes":15240,"put_count":21}} +{"i":26,"series":1,"stats":{"get_bytes":15860,"get_count":21,"put_bytes":13852,"put_count":20}} +{"i":27,"series":1,"stats":{"get_bytes":15611,"get_count":21,"put_bytes":13603,"put_count":20}} +{"i":28,"series":1,"stats":{"get_bytes":17502,"get_count":23,"put_bytes":15494,"put_count":22}} +{"i":29,"series":1,"stats":{"get_bytes":17303,"get_count":22,"put_bytes":15295,"put_count":21}} +{"i":30,"series":1,"stats":{"get_bytes":18086,"get_count":23,"put_bytes":16078,"put_count":22}} +{"i":31,"series":1,"stats":{"get_bytes":15842,"get_count":21,"put_bytes":13834,"put_count":20}} +{"i":32,"series":1,"stats":{"get_bytes":17334,"get_count":22,"put_bytes":15326,"put_count":21}} +{"i":33,"series":1,"stats":{"get_bytes":16617,"get_count":22,"put_bytes":14609,"put_count":21}} +{"i":34,"series":1,"stats":{"get_bytes":16185,"get_count":21,"put_bytes":14177,"put_count":20}} +{"i":35,"series":1,"stats":{"get_bytes":16783,"get_count":22,"put_bytes":14775,"put_count":21}} +{"i":36,"series":1,"stats":{"get_bytes":18048,"get_count":23,"put_bytes":16040,"put_count":22}} +{"i":37,"series":1,"stats":{"get_bytes":15266,"get_count":21,"put_bytes":13258,"put_count":20}} +{"i":38,"series":1,"stats":{"get_bytes":14823,"get_count":21,"put_bytes":12815,"put_count":20}} +{"i":39,"series":1,"stats":{"get_bytes":16853,"get_count":22,"put_bytes":14845,"put_count":21}} +{"i":40,"series":1,"stats":{"get_bytes":16134,"get_count":22,"put_bytes":14126,"put_count":21}} +{"i":41,"series":1,"stats":{"get_bytes":18706,"get_count":23,"put_bytes":16698,"put_count":22}} +{"i":42,"series":1,"stats":{"get_bytes":16801,"get_count":21,"put_bytes":14793,"put_count":20}} +{"i":43,"series":1,"stats":{"get_bytes":17384,"get_count":22,"put_bytes":15376,"put_count":21}} +{"i":44,"series":1,"stats":{"get_bytes":18386,"get_count":23,"put_bytes":16378,"put_count":22}} +{"i":45,"series":1,"stats":{"get_bytes":15358,"get_count":21,"put_bytes":13350,"put_count":20}} +{"i":46,"series":1,"stats":{"get_bytes":16824,"get_count":22,"put_bytes":14816,"put_count":21}} +{"i":47,"series":1,"stats":{"get_bytes":18382,"get_count":23,"put_bytes":16374,"put_count":22}} +{"i":48,"series":1,"stats":{"get_bytes":16873,"get_count":22,"put_bytes":14865,"put_count":21}} +{"i":49,"series":1,"stats":{"get_bytes":16377,"get_count":22,"put_bytes":14369,"put_count":21}} +{"i":50,"series":1,"stats":{"get_bytes":18027,"get_count":23,"put_bytes":16019,"put_count":22}} +{"i":51,"series":1,"stats":{"get_bytes":15642,"get_count":21,"put_bytes":13634,"put_count":20}} +{"i":52,"series":1,"stats":{"get_bytes":15414,"get_count":21,"put_bytes":13406,"put_count":20}} +{"i":53,"series":1,"stats":{"get_bytes":17059,"get_count":22,"put_bytes":15051,"put_count":21}} +{"i":54,"series":1,"stats":{"get_bytes":17197,"get_count":22,"put_bytes":15189,"put_count":21}} +{"i":55,"series":1,"stats":{"get_bytes":14402,"get_count":20,"put_bytes":12394,"put_count":19}} +{"i":56,"series":1,"stats":{"get_bytes":16196,"get_count":22,"put_bytes":14188,"put_count":21}} +{"i":57,"series":1,"stats":{"get_bytes":17794,"get_count":23,"put_bytes":15786,"put_count":22}} +{"i":58,"series":1,"stats":{"get_bytes":17878,"get_count":21,"put_bytes":15870,"put_count":20}} +{"i":59,"series":1,"stats":{"get_bytes":17823,"get_count":23,"put_bytes":15815,"put_count":22}} +{"i":60,"series":1,"stats":{"get_bytes":16832,"get_count":22,"put_bytes":14824,"put_count":21}} +{"i":61,"series":1,"stats":{"get_bytes":18673,"get_count":23,"put_bytes":16665,"put_count":22}} +{"i":62,"series":1,"stats":{"get_bytes":16739,"get_count":22,"put_bytes":14731,"put_count":21}} +{"i":63,"series":1,"stats":{"get_bytes":17907,"get_count":23,"put_bytes":15899,"put_count":22}} +{"i":64,"series":1,"stats":{"get_bytes":15662,"get_count":21,"put_bytes":13654,"put_count":20}} +{"i":65,"series":1,"stats":{"get_bytes":16008,"get_count":21,"put_bytes":14000,"put_count":20}} +{"i":66,"series":1,"stats":{"get_bytes":14585,"get_count":20,"put_bytes":12577,"put_count":19}} +{"i":67,"series":1,"stats":{"get_bytes":16718,"get_count":22,"put_bytes":14710,"put_count":21}} +{"i":68,"series":1,"stats":{"get_bytes":16815,"get_count":22,"put_bytes":14807,"put_count":21}} +{"i":69,"series":1,"stats":{"get_bytes":14607,"get_count":21,"put_bytes":12599,"put_count":20}} +{"i":70,"series":1,"stats":{"get_bytes":18196,"get_count":23,"put_bytes":16188,"put_count":22}} +{"i":71,"series":1,"stats":{"get_bytes":13762,"get_count":20,"put_bytes":11754,"put_count":19}} +{"i":72,"series":1,"stats":{"get_bytes":16225,"get_count":21,"put_bytes":14217,"put_count":20}} +{"i":73,"series":1,"stats":{"get_bytes":17042,"get_count":22,"put_bytes":15034,"put_count":21}} +{"i":74,"series":1,"stats":{"get_bytes":17325,"get_count":23,"put_bytes":15317,"put_count":22}} +{"i":75,"series":1,"stats":{"get_bytes":15418,"get_count":20,"put_bytes":13410,"put_count":19}} +{"i":76,"series":1,"stats":{"get_bytes":16907,"get_count":22,"put_bytes":14899,"put_count":21}} +{"i":77,"series":1,"stats":{"get_bytes":17775,"get_count":22,"put_bytes":15767,"put_count":21}} +{"i":78,"series":1,"stats":{"get_bytes":16760,"get_count":22,"put_bytes":14752,"put_count":21}} +{"i":79,"series":1,"stats":{"get_bytes":17038,"get_count":22,"put_bytes":15030,"put_count":21}} +{"i":80,"series":1,"stats":{"get_bytes":16373,"get_count":22,"put_bytes":14365,"put_count":21}} +{"i":81,"series":1,"stats":{"get_bytes":15304,"get_count":21,"put_bytes":13296,"put_count":20}} +{"i":82,"series":1,"stats":{"get_bytes":15482,"get_count":21,"put_bytes":13474,"put_count":20}} +{"i":83,"series":1,"stats":{"get_bytes":18679,"get_count":23,"put_bytes":16671,"put_count":22}} +{"i":84,"series":1,"stats":{"get_bytes":16536,"get_count":22,"put_bytes":14528,"put_count":21}} +{"i":85,"series":1,"stats":{"get_bytes":16160,"get_count":21,"put_bytes":14152,"put_count":20}} +{"i":86,"series":1,"stats":{"get_bytes":16759,"get_count":22,"put_bytes":14751,"put_count":21}} +{"i":87,"series":1,"stats":{"get_bytes":16203,"get_count":21,"put_bytes":14195,"put_count":20}} +{"i":88,"series":1,"stats":{"get_bytes":17930,"get_count":23,"put_bytes":15922,"put_count":22}} +{"i":89,"series":1,"stats":{"get_bytes":18354,"get_count":23,"put_bytes":16346,"put_count":22}} +{"i":90,"series":1,"stats":{"get_bytes":17629,"get_count":23,"put_bytes":15621,"put_count":22}} +{"i":91,"series":1,"stats":{"get_bytes":17423,"get_count":22,"put_bytes":15415,"put_count":21}} +{"i":92,"series":1,"stats":{"get_bytes":15387,"get_count":20,"put_bytes":13379,"put_count":19}} +{"i":93,"series":1,"stats":{"get_bytes":17199,"get_count":22,"put_bytes":15191,"put_count":21}} +{"i":94,"series":1,"stats":{"get_bytes":15525,"get_count":21,"put_bytes":13517,"put_count":20}} +{"i":95,"series":1,"stats":{"get_bytes":15331,"get_count":21,"put_bytes":13323,"put_count":20}} +{"i":96,"series":1,"stats":{"get_bytes":15276,"get_count":21,"put_bytes":13268,"put_count":20}} +{"i":97,"series":1,"stats":{"get_bytes":15696,"get_count":21,"put_bytes":13688,"put_count":20}} +{"i":98,"series":1,"stats":{"get_bytes":16771,"get_count":22,"put_bytes":14763,"put_count":21}} +{"i":99,"series":1,"stats":{"get_bytes":16538,"get_count":22,"put_bytes":14530,"put_count":21}} +{"i":0,"series":2,"stats":{"get_bytes":15423,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":16837,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":2,"series":2,"stats":{"get_bytes":14911,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":3,"series":2,"stats":{"get_bytes":16353,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":4,"series":2,"stats":{"get_bytes":15625,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":5,"series":2,"stats":{"get_bytes":17347,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":6,"series":2,"stats":{"get_bytes":16998,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":7,"series":2,"stats":{"get_bytes":16912,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":8,"series":2,"stats":{"get_bytes":16611,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":9,"series":2,"stats":{"get_bytes":16596,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":10,"series":2,"stats":{"get_bytes":18179,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":11,"series":2,"stats":{"get_bytes":15314,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":12,"series":2,"stats":{"get_bytes":15427,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":13,"series":2,"stats":{"get_bytes":17974,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":14,"series":2,"stats":{"get_bytes":16944,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":15,"series":2,"stats":{"get_bytes":15776,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":16,"series":2,"stats":{"get_bytes":13729,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":17,"series":2,"stats":{"get_bytes":14287,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":18,"series":2,"stats":{"get_bytes":17681,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":19,"series":2,"stats":{"get_bytes":16726,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":20,"series":2,"stats":{"get_bytes":17079,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":21,"series":2,"stats":{"get_bytes":15444,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":22,"series":2,"stats":{"get_bytes":18395,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":23,"series":2,"stats":{"get_bytes":14430,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":24,"series":2,"stats":{"get_bytes":17108,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":25,"series":2,"stats":{"get_bytes":17248,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":26,"series":2,"stats":{"get_bytes":15860,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":27,"series":2,"stats":{"get_bytes":15611,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":28,"series":2,"stats":{"get_bytes":17502,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":29,"series":2,"stats":{"get_bytes":17303,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":30,"series":2,"stats":{"get_bytes":18086,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":31,"series":2,"stats":{"get_bytes":15842,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":32,"series":2,"stats":{"get_bytes":17334,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":33,"series":2,"stats":{"get_bytes":16617,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":34,"series":2,"stats":{"get_bytes":16185,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":35,"series":2,"stats":{"get_bytes":16783,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":36,"series":2,"stats":{"get_bytes":18048,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":37,"series":2,"stats":{"get_bytes":15266,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":38,"series":2,"stats":{"get_bytes":14823,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":39,"series":2,"stats":{"get_bytes":16853,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":40,"series":2,"stats":{"get_bytes":16134,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":41,"series":2,"stats":{"get_bytes":18706,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":42,"series":2,"stats":{"get_bytes":16801,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":43,"series":2,"stats":{"get_bytes":17384,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":44,"series":2,"stats":{"get_bytes":18386,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":45,"series":2,"stats":{"get_bytes":15358,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":46,"series":2,"stats":{"get_bytes":16824,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":47,"series":2,"stats":{"get_bytes":18382,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":48,"series":2,"stats":{"get_bytes":16873,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":49,"series":2,"stats":{"get_bytes":16377,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":50,"series":2,"stats":{"get_bytes":18027,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":51,"series":2,"stats":{"get_bytes":15642,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":52,"series":2,"stats":{"get_bytes":15414,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":53,"series":2,"stats":{"get_bytes":17059,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":54,"series":2,"stats":{"get_bytes":17197,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":55,"series":2,"stats":{"get_bytes":14402,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":56,"series":2,"stats":{"get_bytes":16196,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":57,"series":2,"stats":{"get_bytes":17794,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":58,"series":2,"stats":{"get_bytes":17878,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":59,"series":2,"stats":{"get_bytes":17823,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":60,"series":2,"stats":{"get_bytes":16832,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":61,"series":2,"stats":{"get_bytes":18673,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":62,"series":2,"stats":{"get_bytes":16739,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":63,"series":2,"stats":{"get_bytes":17907,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":64,"series":2,"stats":{"get_bytes":15662,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":65,"series":2,"stats":{"get_bytes":16008,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":66,"series":2,"stats":{"get_bytes":14585,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":67,"series":2,"stats":{"get_bytes":16718,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":68,"series":2,"stats":{"get_bytes":16815,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":69,"series":2,"stats":{"get_bytes":14607,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":70,"series":2,"stats":{"get_bytes":18196,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":71,"series":2,"stats":{"get_bytes":13762,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":72,"series":2,"stats":{"get_bytes":16225,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":73,"series":2,"stats":{"get_bytes":17042,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":74,"series":2,"stats":{"get_bytes":17325,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":75,"series":2,"stats":{"get_bytes":15418,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":76,"series":2,"stats":{"get_bytes":16907,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":77,"series":2,"stats":{"get_bytes":17775,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":78,"series":2,"stats":{"get_bytes":16760,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":79,"series":2,"stats":{"get_bytes":17038,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":80,"series":2,"stats":{"get_bytes":16373,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":81,"series":2,"stats":{"get_bytes":15304,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":82,"series":2,"stats":{"get_bytes":15482,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":83,"series":2,"stats":{"get_bytes":18679,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":84,"series":2,"stats":{"get_bytes":16536,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":85,"series":2,"stats":{"get_bytes":16160,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":86,"series":2,"stats":{"get_bytes":16759,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":87,"series":2,"stats":{"get_bytes":16203,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":88,"series":2,"stats":{"get_bytes":17930,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":89,"series":2,"stats":{"get_bytes":18354,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":90,"series":2,"stats":{"get_bytes":17629,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":91,"series":2,"stats":{"get_bytes":17423,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":92,"series":2,"stats":{"get_bytes":15387,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":93,"series":2,"stats":{"get_bytes":17199,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":94,"series":2,"stats":{"get_bytes":15525,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":95,"series":2,"stats":{"get_bytes":15331,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":96,"series":2,"stats":{"get_bytes":15276,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":97,"series":2,"stats":{"get_bytes":15696,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":98,"series":2,"stats":{"get_bytes":16771,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":99,"series":2,"stats":{"get_bytes":16538,"get_count":22,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.png b/actors/evm/tests/measurements/mapping_overwrite.png index 78319b77efce74c0026f0618e420b9d065202cd2..5e335b54283f79a4c05d2126dfb54ba0422b8cc9 100644 GIT binary patch literal 22313 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

##awwH)1e!#O10^>PtMr1J^CH@b^Y3&0`Zg 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 diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 91a8ebcec..0e8eca522 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":144643,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":140207,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":134295,"get_count":80,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":140720,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":146426,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":150587,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":138080,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":133102,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":141964,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":145117,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":134837,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":141358,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":136866,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":135863,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":144612,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":142398,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":147675,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":143998,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":139148,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":145959,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":136244,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":153101,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":137209,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":141682,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":141423,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":130679,"get_count":78,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":143941,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":144765,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":148809,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":139227,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":150582,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":144914,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":142435,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":139456,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":135586,"get_count":80,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":144920,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":133780,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":145967,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":143537,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":137860,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":140322,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":132747,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":135717,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":137142,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":143480,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":140360,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":131733,"get_count":79,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":138412,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":134600,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":142344,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":146504,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":140664,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":140964,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":143207,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":140089,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":141773,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":139081,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":150936,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":138088,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":146393,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":140315,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":137042,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":150801,"get_count":93,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":141625,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":142273,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":142659,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":147746,"get_count":89,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":146665,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":142109,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":121008,"get_count":74,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":129956,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":137093,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":134835,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":151063,"get_count":92,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":134319,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":134182,"get_count":81,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":137750,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":137632,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":141254,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":145112,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":141191,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":148027,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":138725,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":152527,"get_count":93,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":139126,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":145861,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":143454,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":133630,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":139991,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":134091,"get_count":83,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":136741,"get_count":85,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":141201,"get_count":86,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":139604,"get_count":84,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":148025,"get_count":90,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":142932,"get_count":88,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":140170,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":148164,"get_count":91,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":144790,"get_count":87,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":138350,"get_count":82,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":147557,"get_count":90,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":110993,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":110859,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":105817,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":104930,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":110951,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":110060,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":102935,"get_count":123,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":109072,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":114626,"get_count":134,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":109195,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":111854,"get_count":134,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":113445,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":113738,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":107639,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":109082,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":109859,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":108956,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":108186,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":102894,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":111344,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":103411,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":106200,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":103079,"get_count":126,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":112714,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":111918,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":108424,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":106920,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":107471,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":107851,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":106681,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":111721,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":104049,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":112677,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":106942,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":109227,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":106233,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":107717,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":109592,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":109310,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":104016,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":108077,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":107171,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":108960,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":107165,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":111201,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":110161,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":110424,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":109219,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":112424,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":110044,"get_count":134,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":105270,"get_count":124,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":106731,"get_count":124,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":109999,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":106049,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":110856,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":105904,"get_count":124,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":106804,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":110405,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":108677,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":110636,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":108615,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":112472,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":103791,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":112448,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":111491,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":103772,"get_count":125,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":109695,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":110005,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":110938,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":110266,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":112518,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":106393,"get_count":126,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":107572,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":107491,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":105711,"get_count":126,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":112059,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":108846,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":107899,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":108384,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":107965,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":109058,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":111748,"get_count":136,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":108225,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":104372,"get_count":123,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":112956,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":108464,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":110012,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":107368,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":107519,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":111354,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":114184,"get_count":133,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":106805,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":113853,"get_count":130,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":107288,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":109254,"get_count":127,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":109342,"get_count":128,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":106457,"get_count":131,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":107294,"get_count":132,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":106410,"get_count":129,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":108598,"get_count":130,"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 index d1145d77ef149b7d32bc2500c754056ea17bcf93..5edcadc3519b428b17fd65d13522da9b8725e4f4 100644 GIT binary patch literal 18833 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 literal 19729 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 VCiBJJ%G>||002ovPDHLkV1iZvU{U}8 diff --git a/actors/evm/tests/measurements/storage-footprint.sh b/actors/evm/tests/measurements/storage-footprint.sh index 0fb169fac..855f7399f 100755 --- a/actors/evm/tests/measurements/storage-footprint.sh +++ b/actors/evm/tests/measurements/storage-footprint.sh @@ -19,7 +19,7 @@ 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 + echo $'\n' >> $NAME.dat done gnuplot \ From c37c68a1b04c18caa5d670984f184f89c209d2d8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 7 Dec 2022 23:35:54 +0200 Subject: [PATCH 172/339] EVM: implement gas hocus pocus consensus logic (#920) * turn gasless value transfers and 2300s into sends * fix gas in callvariants * add test for send transformation when value and no gass * add test for send transformation when no value and gas is 2300 * clippy --- .../evm/src/interpreter/instructions/call.rs | 4 +- actors/evm/tests/call.rs | 174 ++++++++++++++++++ actors/evm/tests/contracts/callvariants.hex | 2 +- .../evm/tests/contracts/callvariants_body.eas | 20 +- 4 files changed, 187 insertions(+), 13 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 0adde4ed8..cd727aeb3 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -240,8 +240,8 @@ pub fn call_generic( let method = if !actor_exists || matches!(target_actor_type, Some(Type::Embryo | Type::Account)) // See https://github.com/filecoin-project/ref-fvm/issues/980 for this - // magic value - || (!value.is_zero() && !gas.is_zero() && gas <= U256::from(21_000)) + // hocus pocus + || (input_data.is_empty() && ((gas == 0 && value > 0) || (gas == 2300 && value == 0))) { // If the target actor doesn't exist or is an account or an embryo, // switch to a basic "send" so the call will still work even if the diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 7289a168d..22af9fc18 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -60,6 +60,98 @@ 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(); @@ -174,6 +266,88 @@ fn test_call_convert_to_send() { } } +// Make sure we do bare sends when calling with 0 gas and value +#[test] +fn test_call_convert_to_send2() { + 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); + + let proxy_call_contract_params = vec![]; + let proxy_call_input_data = RawBytes::serialize(BytesSer(&proxy_call_contract_params)) + .expect("failed to serialize input data"); + + // we don't expected return data + let return_data = vec![]; + + rt.expect_gas_available(10_000_000_000u64); + rt.expect_send( + target, + METHOD_SEND, + proxy_call_input_data, + TokenAmount::from_atto(0x42), + RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + rt.verify(); +} + +// Make sure we do bare sends when calling with 2300 gas and no value +#[test] +fn test_call_convert_to_send3() { + 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); + + let proxy_call_contract_params = vec![]; + let proxy_call_input_data = RawBytes::serialize(BytesSer(&proxy_call_contract_params)) + .expect("failed to serialize input data"); + + // we don't expected return data + let return_data = vec![]; + + rt.expect_gas_available(10_000_000_000u64); + rt.expect_send( + target, + METHOD_SEND, + proxy_call_input_data, + TokenAmount::zero(), + RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + rt.verify(); +} + #[allow(dead_code)] pub fn filecoin_fallback_contract() -> Vec { hex::decode(include_str!("contracts/FilecoinFallback.hex")).unwrap() diff --git a/actors/evm/tests/contracts/callvariants.hex b/actors/evm/tests/contracts/callvariants.hex index 73355bfa1..f21d03629 100644 --- a/actors/evm/tests/contracts/callvariants.hex +++ b/actors/evm/tests/contracts/callvariants.hex @@ -1 +1 @@ -306000556102108060106000396000f360003560e01c80600114607657806002146101e25780600314609757806004146101ee578060051460b8578060061460df57806007146101025780600814610129578060091461014c5780600a1461016d5780600b146101945780600c1461014c5780600d146101bb5780600e1461016d57600080fd5b60206000600260e01b600052600460006004356000fa156102025760206000f35b60206000600460e01b600052600460006004356000fa156102025760206000f35b60206000600660e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600260e01b6000526004600060006004356000f1156102025760206000f35b60206000600860e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600460e01b6000526004600060006004356000f1156102025760206000f35b60206000600260e01b600052600460006004356000f4156102025760206000f35b60206000600460e01b600052600460006004356000f4156102025760005460005260206000f35b60206000600c60e01b600052602435600452602460006004356000fa156102025760206000f35b60206000600e60e01b600052602435600452602460006004356000fa156102025760206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f35b63deadbeef6000526004601cfd \ No newline at end of file +306000556102058060106000396000f360003560e01c80600114607557806002146101d75780600314609557806004146101e3578060051460b5578060061460db578060071460fd578060081461012357806009146101455780600a146101655780600b1461018b5780600c146101455780600d146101b15780600e1461016557600080fd5b60206000600260e01b600052600460006004355afa156101f75760206000f35b60206000600460e01b600052600460006004355afa156101f75760206000f35b60206000600660e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600260e01b6000526004600060006004355af1156101f75760206000f35b60206000600860e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600460e01b6000526004600060006004355af1156101f75760206000f35b60206000600260e01b600052600460006004355af4156101f75760206000f35b60206000600460e01b600052600460006004355af4156101f75760005460005260206000f35b60206000600c60e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600e60e01b600052602435600452602460006004355afa156101f75760206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f35b63deadbeef6000526004601cfd \ No newline at end of file diff --git a/actors/evm/tests/contracts/callvariants_body.eas b/actors/evm/tests/contracts/callvariants_body.eas index b9e183d52..a5302d4d1 100644 --- a/actors/evm/tests/contracts/callvariants_body.eas +++ b/actors/evm/tests/contracts/callvariants_body.eas @@ -81,7 +81,7 @@ push1 0x04 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length @@ -102,7 +102,7 @@ push1 0x04 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length @@ -127,7 +127,7 @@ push1 0x24 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length @@ -149,7 +149,7 @@ push1 0x00 # call args offset push1 0x00 # value push1 0x04 # input (dest) offset calldataload # call dest -push1 0x00 # call gas (ignored) +gas call # do it! %check() push1 0x20 # ret length @@ -174,7 +174,7 @@ push1 0x24 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length @@ -196,7 +196,7 @@ push1 0x00 # call args offset push1 0x00 # value push1 0x04 # input (dest) offset calldataload # icall dest -push1 0x00 # call gas (ignored) +gas call # do it! %check() push1 0x20 # ret length @@ -217,7 +217,7 @@ push1 0x04 # delegate args length push1 0x00 # delegate args offset push1 0x04 # input (dest) offset calldataload # delegate dest -push1 0x00 # delegate gas (ignored) +gas delegatecall # do it! %check() push1 0x20 # ret length @@ -238,7 +238,7 @@ push1 0x04 # delegate args length push1 0x00 # delegate args offset push1 0x04 # input (dest) offset calldataload # delegate dest -push1 0x00 # delegate gas (ignored) +gas delegatecall # do it! %check() push1 0x00 # slot @@ -267,7 +267,7 @@ push1 0x24 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length @@ -292,7 +292,7 @@ push1 0x24 # staticcall args length push1 0x00 # staticcall args offset push1 0x04 # input (dest) offset calldataload # staticcall dest -push1 0x00 # staticcall gas (ignored) +gas staticcall # do it! %check() push1 0x20 # ret length From a6cf2f34b7c13b0fece8bff2aeac3e6c8e94f82e Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 7 Dec 2022 16:06:52 -0800 Subject: [PATCH 173/339] EVM: Call with flags through runtime (#917) * wip * add sending with flags to runtime * fix a few different issues - expectations of send flags - call test - ensure readonly is being passed through * replicate sticky readonly behavior of the FVM in the test VM * add value as a parameter to call_actor precompile * Update actors/evm/src/interpreter/instructions/call.rs Co-authored-by: Steven Allen * fix issues from review - static mode violations are handled by FVM now, remove internal logic around it - read_only in test vm is "context" which is/will probably be different than the _Context_ bits * restrict default impl of to just tests * oops * fmt Co-authored-by: Steven Allen --- .../evm/src/interpreter/instructions/call.rs | 23 ++++++++------- actors/evm/src/interpreter/precompiles/evm.rs | 8 ++++++ actors/evm/src/interpreter/precompiles/fvm.rs | 21 ++++++++++---- actors/evm/src/interpreter/precompiles/mod.rs | 8 +++--- actors/evm/src/interpreter/system.rs | 9 +++--- actors/evm/tests/call.rs | 25 ++++++++++++----- runtime/src/runtime/fvm.rs | 7 +++-- runtime/src/runtime/mod.rs | 7 +++-- runtime/src/test_utils.rs | 28 +++++++++++-------- test_vm/src/lib.rs | 28 +++++++++++-------- 10 files changed, 104 insertions(+), 60 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index cd727aeb3..d78a14c6c 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,7 +1,7 @@ #![allow(clippy::too_many_arguments)] use fvm_ipld_encoding::{BytesDe, BytesSer}; -use fvm_shared::{address::Address, METHOD_SEND}; +use fvm_shared::{address::Address, sys::SendFlags, METHOD_SEND}; use crate::interpreter::precompiles::PrecompileContext; @@ -197,11 +197,8 @@ pub fn call_generic( }; if precompiles::Precompiles::::is_precompile(&dst) { - let context = PrecompileContext { - is_static: matches!(kind, CallKind::StaticCall) || system.readonly, - gas_limit: effective_gas_limit(system, gas), - value, - }; + let context = + PrecompileContext { call_type: kind, gas_limit: effective_gas_limit(system, gas) }; match precompiles::Precompiles::call_precompile(system, dst, input_data, context) .map_err(StatusCode::from) @@ -255,8 +252,14 @@ pub fn call_generic( let params = RawBytes::serialize(BytesSer(input_data))?; let value = TokenAmount::from(&value); let gas_limit = effective_gas_limit(system, gas); - let read_only = kind == CallKind::StaticCall; - system.send_with_gas(&dst_addr, method, params, value, gas_limit, read_only) + let send_flags = if kind == CallKind::StaticCall { + SendFlags::READ_ONLY + } else { + SendFlags::default() + }; + system.send_generalized( + &dst_addr, method, params, value, gas_limit, send_flags, + ) } } CallKind::DelegateCall => match get_cid_type(system.rt, dst) { @@ -266,13 +269,13 @@ pub fn call_generic( // and then invoke self with delegate; readonly context is sticky let params = DelegateCallParams { code, input: input_data.into() }; - system.send_with_gas( + system.send_generalized( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, RawBytes::serialize(¶ms)?, TokenAmount::from(&value), effective_gas_limit(system, gas), - system.readonly, + SendFlags::default(), ) } // If we're calling an account or a non-existent actor, return nothing because diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 7596e440a..4e04537c3 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -326,10 +326,18 @@ pub(super) fn blake2f( #[cfg(test)] mod tests { + use crate::interpreter::instructions::call::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: None } + } + } + #[test] fn padding() { let input = b"foo bar boxy"; diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index ffd8e647a..1d6f6c923 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,9 +1,10 @@ use fil_actors_runtime::runtime::{builtins::Type, Runtime}; use fvm_ipld_encoding::RawBytes; -use fvm_shared::{address::Address, econ::TokenAmount}; +use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags}; use num_traits::FromPrimitive; use crate::interpreter::{ + instructions::call::CallKind, precompiles::{ parameter::{read_right_pad, Parameter}, NativeType, @@ -169,9 +170,19 @@ pub(super) fn call_actor( ) -> PrecompileResult { // ----- Input Parameters ------- + if ctx.call_type != CallKind::DelegateCall { + return Err(PrecompileError::CallForbidden); + } + let mut input_params = U256Reader::new(input); let method: u64 = input_params.next_param_padded()?; + + let value: U256 = input_params.next_padded().into(); + + let flags: u64 = input_params.next_param_padded()?; + let flags = SendFlags::from_bits(flags).ok_or(PrecompileError::InvalidInput)?; + let codec: u64 = input_params.next_param_padded()?; // TODO only CBOR for now if codec != fvm_ipld_encoding::DAG_CBOR { @@ -184,8 +195,6 @@ pub(super) fn call_actor( // ------ Begin Call ------- let result = { - // REMOVEME: closes https://github.com/filecoin-project/ref-fvm/issues/1018 - let start = input_params.remaining_slice(); let bytes = read_right_pad(start, send_data_size + address_size); @@ -193,13 +202,13 @@ pub(super) fn call_actor( let address = &bytes[send_data_size..send_data_size + address_size]; let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; - system.send_with_gas( + system.send_generalized( &address, method, RawBytes::from(input_data.to_vec()), - TokenAmount::from(&ctx.value), + TokenAmount::from(&value), ctx.gas_limit, - ctx.is_static, + flags, ) }; diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 693c4a647..1543e4c83 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use fil_actors_runtime::runtime::Runtime; use substrate_bn::{CurveError, GroupError}; -use super::{StatusCode, System, U256}; +use super::{instructions::call::CallKind, StatusCode, System, U256}; mod evm; mod fvm; @@ -87,6 +87,7 @@ pub enum PrecompileError { EcErr(CurveError), EcGroupErr(GroupError), InvalidInput, // TODO merge with below? + CallForbidden, IncorrectInputSize, OutOfGas, CallActorError(StatusCode), @@ -101,11 +102,10 @@ impl From for StatusCode { } } -#[derive(Debug, PartialEq, Eq, Default)] +#[derive(Debug, PartialEq, Eq)] pub struct PrecompileContext { - pub is_static: bool, + pub call_type: CallKind, pub gas_limit: Option, - pub value: U256, } /// Native Type of a given contract diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 26e47e47f..3c54267b3 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -10,6 +10,7 @@ use fvm_shared::{ address::{Address, Payload}, econ::TokenAmount, error::ExitCode, + sys::SendFlags, MethodNum, IPLD_RAW, }; use multihash::Code; @@ -163,18 +164,18 @@ impl<'r, RT: Runtime> System<'r, RT> { } /// Generalized send - pub fn send_with_gas( + pub fn send_generalized( &mut self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, gas_limit: Option, - read_only: bool, + send_flags: SendFlags, ) -> Result { self.flush()?; - let result = self.rt.send_with_gas(to, method, params, value, gas_limit, read_only)?; - if !read_only { + let result = self.rt.send_generalized(to, method, params, value, gas_limit, send_flags)?; + if !send_flags.read_only() { self.reload()?; } Ok(result) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 22af9fc18..eae92937a 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -10,6 +10,7 @@ use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::sys::SendFlags; use fvm_shared::METHOD_SEND; mod util; @@ -398,16 +399,14 @@ push1 0xa0 calldatasize push1 0x00 -# value -push1 0x00 - # dst (callactor precompile) push1 0x0e # gas push1 0x00 -call +# call_actor must be from delegatecall +delegatecall # copy result to mem 0x00 (overwrites input data) returndatasize @@ -439,6 +438,8 @@ fn test_callactor_revert() { fn test_callactor_inner(exit_code: ExitCode) { let contract = callactor_proxy_contract(); + const CALLACTOR_NUM_PARAMS: usize = 6; + // construct the proxy let mut rt = util::construct_and_verify(contract); // create a mock target and proxy a call through the proxy @@ -450,6 +451,8 @@ fn test_callactor_inner(exit_code: ExitCode) { let mut contract_params = Vec::new(); let method = U256::from(0x42); + let value = U256::from(0); + let send_flags = SendFlags::default(); let codec = U256::from(DAG_CBOR); let target_bytes = target.to_bytes(); @@ -459,26 +462,33 @@ fn test_callactor_inner(exit_code: ExitCode) { let data_size = U256::from(proxy_call_input_data.len()); 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(&target_size.to_bytes()); contract_params.extend_from_slice(&data_size.to_bytes()); contract_params.extend_from_slice(&target_bytes); contract_params.extend_from_slice(&proxy_call_input_data); - assert_eq!(32 * 4 + target_bytes.len() + proxy_call_input_data.len(), contract_params.len()); + assert_eq!( + 32 * CALLACTOR_NUM_PARAMS + target_bytes.len() + proxy_call_input_data.len(), + contract_params.len(), + "unexpected input length" + ); // 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( + rt.expect_send_generalized( target, 0x42, proxy_call_input_data, TokenAmount::zero(), RawBytes::from(return_data), exit_code, + send_flags, ); // invoke @@ -538,6 +548,7 @@ fn test_callactor_inner(exit_code: ExitCode) { data_size: 32, data: U256::from(0x42).to_bytes().to_vec(), }; - + rt.verify(); assert_eq!(result, expected); + rt.reset(); } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 2d572c552..b924798da 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -22,6 +22,7 @@ use fvm_shared::sector::{ AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo, }; +use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; use num_traits::FromPrimitive; @@ -385,14 +386,14 @@ where handle_send_result(to, method, fvm::send::send_read_only(to, method, params, None)) } - fn send_with_gas( + fn send_generalized( &self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, gas_limit: Option, - read_only: bool, + flags: SendFlags, ) -> Result { if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); @@ -400,7 +401,7 @@ where handle_send_result( to, method, - if read_only { + if flags.read_only() { fvm::send::send_read_only(to, method, params, gas_limit) } else { fvm::send::send(to, method, params, value, gas_limit) diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 853883866..cc01e0eaa 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -19,6 +19,7 @@ use fvm_shared::sector::{ AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo, }; +use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; use multihash::Code; @@ -204,15 +205,15 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { ) -> Result; /// Generailizes [`Runtime::send`] and [`Runtime::send_read_only`] to allow the caller to - /// specify a gas limit. - fn send_with_gas( + /// specify a gas limit and send flags. + fn send_generalized( &self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, gas_limit: Option, - read_only: bool, + flags: SendFlags, ) -> Result; /// Computes an address for a new actor. The returned address is intended to uniquely refer to diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index e9be430a4..6135b6364 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -28,6 +28,7 @@ use fvm_shared::sector::{ AggregateSealVerifyInfo, AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo, }; +use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum}; @@ -367,7 +368,7 @@ pub struct ExpectedMessage { pub method: MethodNum, pub params: RawBytes, pub value: TokenAmount, - pub read_only: bool, + pub send_flags: SendFlags, // returns from applying expectedMessage pub send_return: RawBytes, @@ -546,7 +547,7 @@ impl MockRuntime { params: RawBytes, value: TokenAmount, _gas_limit: Option, - read_only: bool, + send_flags: SendFlags, ) -> Result { // TODO gas_limit is currently ignored, what should we do about it? self.require_in_call(); @@ -570,7 +571,7 @@ impl MockRuntime { && expected_msg.method == method && expected_msg.params == params && expected_msg.value == value - && expected_msg.read_only == read_only, + && expected_msg.send_flags == send_flags, "message sent does not match expectation.\n\ message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", @@ -735,18 +736,21 @@ impl MockRuntime { value, send_return, exit_code, - read_only: false, + send_flags: SendFlags::default(), }) } #[allow(dead_code)] - pub fn expect_send_read_only( + #[allow(clippy::too_many_arguments)] + pub fn expect_send_generalized( &mut self, to: Address, method: MethodNum, params: RawBytes, + value: TokenAmount, send_return: RawBytes, exit_code: ExitCode, + send_flags: SendFlags, ) { self.expectations.borrow_mut().expect_sends.push_back(ExpectedMessage { to, @@ -754,8 +758,8 @@ impl MockRuntime { params, send_return, exit_code, - value: TokenAmount::default(), - read_only: true, + value, + send_flags, }) } @@ -1169,7 +1173,7 @@ impl Runtime for MockRuntime { params: RawBytes, value: TokenAmount, ) -> Result { - self.send_inner(to, method, params, value, None, false) + self.send_inner(to, method, params, value, None, SendFlags::default()) } fn send_read_only( @@ -1178,19 +1182,19 @@ impl Runtime for MockRuntime { method: MethodNum, params: RawBytes, ) -> Result { - self.send_inner(to, method, params, TokenAmount::default(), None, true) + self.send_inner(to, method, params, TokenAmount::default(), None, SendFlags::READ_ONLY) } - fn send_with_gas( + fn send_generalized( &self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, gas_limit: Option, - read_only: bool, + flags: SendFlags, ) -> Result { - self.send_inner(to, method, params, value, gas_limit, read_only) + self.send_inner(to, method, params, value, gas_limit, flags) } fn new_actor_address(&mut self) -> Result { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 07541685d..91ab12b95 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -56,6 +56,7 @@ use fvm_shared::sector::{ StoragePower, WindowPoStVerifyInfo, }; use fvm_shared::smooth::FilterEstimate; +use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum, IPLD_RAW, METHOD_CONSTRUCTOR, METHOD_SEND}; use regex::Regex; @@ -630,7 +631,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { }; // But only if we're not in read-only mode. - if self.read_only { + if self.read_only() { return Err(ActorError::unchecked( ExitCode::USR_READ_ONLY, format!("cannot create actor {target} in read-only mode"), @@ -735,7 +736,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { "insufficient balance to transfer".to_string(), )); } - if self.read_only { + if self.read_only() { return Err(ActorError::unchecked( ExitCode::USR_READ_ONLY, "cannot transfer value in read-only mode".to_string(), @@ -796,8 +797,13 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { params: RawBytes, value: TokenAmount, _gas_limit: Option, - read_only: bool, + mut send_flags: SendFlags, ) -> Result { + // replicate FVM by silently propagating read only flag to subcalls + if self.read_only() { + send_flags.set(SendFlags::READ_ONLY, true) + } + // TODO gas_limit is current ignored, what should we do about it? if !self.allow_side_effects { return Err(ActorError::unchecked( @@ -816,7 +822,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { policy: self.policy, subinvocations: RefCell::new(vec![]), actor_exit: RefCell::new(None), - read_only, + read_only: send_flags.read_only(), }; let res = new_ctx.invoke_actor(); let invoc = new_ctx.gather_trace(res.clone()); @@ -862,7 +868,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { } }; - if self.read_only { + if self.read_only() { return Err(ActorError::unchecked( ExitCode::USR_READ_ONLY, "cannot send value in read-only mode".into(), @@ -1013,7 +1019,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { params: RawBytes, value: TokenAmount, ) -> Result { - self.send_inner(to, method, params, value, None, self.read_only) + self.send_inner(to, method, params, value, None, SendFlags::default()) } fn send_read_only( @@ -1022,19 +1028,19 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { method: MethodNum, params: RawBytes, ) -> Result { - self.send_inner(to, method, params, TokenAmount::zero(), None, true) + self.send_inner(to, method, params, TokenAmount::zero(), None, SendFlags::READ_ONLY) } - fn send_with_gas( + fn send_generalized( &self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, gas_limit: Option, - read_only: bool, + flags: SendFlags, ) -> Result { - self.send_inner(to, method, params, value, gas_limit, read_only || self.read_only) + self.send_inner(to, method, params, value, gas_limit, flags) } fn get_randomness_from_tickets( @@ -1084,7 +1090,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { ExitCode::SYS_ASSERTION_FAILED, "actor does not exist".to_string(), )), - Some(mut act) if !self.read_only => { + Some(mut act) if !self.read_only() => { act.head = *root; self.v.set_actor(self.to(), act); Ok(()) From 46415d23e722d7fd8731c127b9d7d752cb6410ee Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 7 Dec 2022 20:35:24 -0800 Subject: [PATCH 174/339] chore: update sdk (#924) --- Cargo.lock | 18 +++++++++--------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 ++-- actors/evm/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 ++-- runtime/src/runtime/fvm.rs | 22 +++++++++++----------- state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- 20 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf2a29b38..bd76e2a74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1857,7 +1857,7 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#f5d851a33ce0f34eabb583165fdce53cb4c60f56" dependencies = [ "frc42_hasher", "frc42_macros", @@ -1870,7 +1870,7 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#f5d851a33ce0f34eabb583165fdce53cb4c60f56" dependencies = [ "fvm_sdk", "fvm_shared", @@ -1880,7 +1880,7 @@ dependencies = [ [[package]] name = "frc42_macros" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#f5d851a33ce0f34eabb583165fdce53cb4c60f56" dependencies = [ "blake2b_simd", "frc42_hasher", @@ -1892,7 +1892,7 @@ dependencies = [ [[package]] name = "frc46_token" version = "1.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#f5d851a33ce0f34eabb583165fdce53cb4c60f56" dependencies = [ "anyhow", "cid 0.8.6", @@ -2041,7 +2041,7 @@ dependencies = [ [[package]] name = "fvm_actor_utils" version = "0.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#8fd6f9ca24f2c190573dae2ef49b4cbe6fcf33b5" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#f5d851a33ce0f34eabb583165fdce53cb4c60f56" dependencies = [ "anyhow", "cid 0.8.6", @@ -2167,9 +2167,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.15" +version = "3.0.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820720405923ef64f9ec5b1c7bb523629763d888f34904c8ff3bdd69de652195" +checksum = "7016663436cc9e98b68bbdee40a2a333efa22eb984a1d2099a51d73b9cb2fd7a" dependencies = [ "cid 0.8.6", "fvm_ipld_encoding", @@ -2182,9 +2182,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.13" +version = "3.0.0-alpha.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c716adb0c618a2ce9c3e464cdbe1f00c65cc0a18e83d4aae7be38e52895e0a5" +checksum = "8f4c82720bd5a315560cb724b4806e371fede40a203955ca17690b5b668a1db5" dependencies = [ "anyhow", "bitflags", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index ae60a60d8..5564c9573 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 8f7067ab3..2c15d2347 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index 0c44fb720..469cb7ab6 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index a2a5ee19a..55e536452 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index c4b58eddb..a32c68132 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.15", optional = true } -fvm_shared = { version = "3.0.0-alpha.13", optional = true } +fvm_sdk = { version = "3.0.0-alpha.16", optional = true } +fvm_shared = { version = "3.0.0-alpha.14", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 16d2e9ab0..a375fa383 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } fvm_ipld_kamt = { version = "0.1.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 0d1ad1fd5..65dcf65c8 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 241af3d03..4c4889633 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 49144e8bf..f8dc6ade7 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 2ab3f6b08..cc8a1c163 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", 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 7f43948be..1947e5a11 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } frc42_dispatch = "1.0.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 666890dee..451ead215 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 4f3a6cbd6..d2be1bf35 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 4042c0462..2854ade66 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 89d124099..e1d378529 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 83ce3832e..4d9599db5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.15", optional = true } +fvm_sdk = { version = "3.0.0-alpha.16", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index b924798da..957808f11 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -371,7 +371,11 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - handle_send_result(to, method, fvm::send::send(to, method, params, value, None)) + handle_send_result( + to, + method, + fvm::send::send(to, method, params, value, None, SendFlags::default()), + ) } fn send_read_only( @@ -383,7 +387,11 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - handle_send_result(to, method, fvm::send::send_read_only(to, method, params, None)) + handle_send_result( + to, + method, + fvm::send::send(to, method, params, TokenAmount::default(), None, SendFlags::READ_ONLY), + ) } fn send_generalized( @@ -398,15 +406,7 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - handle_send_result( - to, - method, - if flags.read_only() { - fvm::send::send_read_only(to, method, params, gas_limit) - } else { - fvm::send::send(to, method, params, value, gas_limit) - }, - ) + handle_send_result(to, method, fvm::send::send(to, method, params, value, gas_limit, flags)) } fn new_actor_address(&mut self) -> Result { diff --git a/state/Cargo.toml b/state/Cargo.toml index 577641d6b..71322195a 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index be21623e9..1c572b1d8 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -37,7 +37,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.13", default-features = false } +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From b31f593c42b3c665044124a1ee6cd4d60088cd23 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Thu, 8 Dec 2022 13:26:12 +0000 Subject: [PATCH 175/339] EVM-859: Try allowing data in the root, but just 1 KV pair. (#926) --- actors/evm/src/interpreter/system.rs | 12 +- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 15619 -> 17263 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 25461 -> 26774 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 13369 -> 13584 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 14255 -> 14451 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 15071 -> 14440 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 14022 -> 14093 bytes .../measurements/mapping_add_n1.jsonline | 398 ++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 19902 -> 19532 bytes .../measurements/mapping_add_n100.jsonline | 398 ++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 20965 -> 22174 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22313 -> 22146 bytes .../measurements/mapping_read_n1.jsonline | 154 +++---- .../tests/measurements/mapping_read_n1.png | Bin 17467 -> 16915 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 18833 -> 19133 bytes 23 files changed, 1605 insertions(+), 1597 deletions(-) diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 3c54267b3..0e9aa337d 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -31,19 +31,27 @@ lazy_static::lazy_static! { // 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. static ref KAMT_CONFIG: KamtConfig = KamtConfig { - min_data_depth: 2, + min_data_depth: 0, bit_width: 5, - max_array_width: 3 + max_array_width: 1 }; } diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 4357decd7..a13e75cf1 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2515,"put_count":9}} -{"i":1,"series":1,"stats":{"get_bytes":2424,"get_count":7,"put_bytes":420,"put_count":6}} -{"i":2,"series":1,"stats":{"get_bytes":2428,"get_count":7,"put_bytes":424,"put_count":6}} -{"i":3,"series":1,"stats":{"get_bytes":2432,"get_count":7,"put_bytes":428,"put_count":6}} -{"i":4,"series":1,"stats":{"get_bytes":2436,"get_count":7,"put_bytes":432,"put_count":6}} -{"i":5,"series":1,"stats":{"get_bytes":2440,"get_count":7,"put_bytes":436,"put_count":6}} -{"i":6,"series":1,"stats":{"get_bytes":2444,"get_count":7,"put_bytes":441,"put_count":6}} -{"i":7,"series":1,"stats":{"get_bytes":2449,"get_count":7,"put_bytes":445,"put_count":6}} -{"i":8,"series":1,"stats":{"get_bytes":2453,"get_count":7,"put_bytes":482,"put_count":6}} -{"i":9,"series":1,"stats":{"get_bytes":2490,"get_count":7,"put_bytes":486,"put_count":6}} -{"i":10,"series":1,"stats":{"get_bytes":2494,"get_count":7,"put_bytes":490,"put_count":6}} -{"i":11,"series":1,"stats":{"get_bytes":2498,"get_count":7,"put_bytes":494,"put_count":6}} -{"i":12,"series":1,"stats":{"get_bytes":2502,"get_count":7,"put_bytes":498,"put_count":6}} -{"i":13,"series":1,"stats":{"get_bytes":2506,"get_count":7,"put_bytes":502,"put_count":6}} -{"i":14,"series":1,"stats":{"get_bytes":2510,"get_count":7,"put_bytes":507,"put_count":6}} -{"i":15,"series":1,"stats":{"get_bytes":2515,"get_count":7,"put_bytes":511,"put_count":6}} -{"i":16,"series":1,"stats":{"get_bytes":2519,"get_count":7,"put_bytes":548,"put_count":6}} -{"i":17,"series":1,"stats":{"get_bytes":2556,"get_count":7,"put_bytes":552,"put_count":6}} -{"i":18,"series":1,"stats":{"get_bytes":2560,"get_count":7,"put_bytes":556,"put_count":6}} -{"i":19,"series":1,"stats":{"get_bytes":2564,"get_count":7,"put_bytes":560,"put_count":6}} -{"i":20,"series":1,"stats":{"get_bytes":2568,"get_count":7,"put_bytes":564,"put_count":6}} -{"i":21,"series":1,"stats":{"get_bytes":2572,"get_count":7,"put_bytes":568,"put_count":6}} -{"i":22,"series":1,"stats":{"get_bytes":2576,"get_count":7,"put_bytes":573,"put_count":6}} -{"i":23,"series":1,"stats":{"get_bytes":2581,"get_count":7,"put_bytes":577,"put_count":6}} -{"i":24,"series":1,"stats":{"get_bytes":2585,"get_count":7,"put_bytes":746,"put_count":7}} -{"i":25,"series":1,"stats":{"get_bytes":2754,"get_count":8,"put_bytes":750,"put_count":7}} -{"i":26,"series":1,"stats":{"get_bytes":2758,"get_count":8,"put_bytes":754,"put_count":7}} -{"i":27,"series":1,"stats":{"get_bytes":2762,"get_count":8,"put_bytes":758,"put_count":7}} -{"i":28,"series":1,"stats":{"get_bytes":2766,"get_count":8,"put_bytes":762,"put_count":7}} -{"i":29,"series":1,"stats":{"get_bytes":2770,"get_count":8,"put_bytes":766,"put_count":7}} -{"i":30,"series":1,"stats":{"get_bytes":2774,"get_count":8,"put_bytes":771,"put_count":7}} -{"i":31,"series":1,"stats":{"get_bytes":2779,"get_count":8,"put_bytes":775,"put_count":7}} -{"i":32,"series":1,"stats":{"get_bytes":2783,"get_count":8,"put_bytes":816,"put_count":7}} -{"i":33,"series":1,"stats":{"get_bytes":2824,"get_count":8,"put_bytes":820,"put_count":7}} -{"i":34,"series":1,"stats":{"get_bytes":2828,"get_count":8,"put_bytes":824,"put_count":7}} -{"i":35,"series":1,"stats":{"get_bytes":2832,"get_count":8,"put_bytes":828,"put_count":7}} -{"i":36,"series":1,"stats":{"get_bytes":2836,"get_count":8,"put_bytes":832,"put_count":7}} -{"i":37,"series":1,"stats":{"get_bytes":2840,"get_count":8,"put_bytes":836,"put_count":7}} -{"i":38,"series":1,"stats":{"get_bytes":2844,"get_count":8,"put_bytes":841,"put_count":7}} -{"i":39,"series":1,"stats":{"get_bytes":2849,"get_count":8,"put_bytes":845,"put_count":7}} -{"i":40,"series":1,"stats":{"get_bytes":2853,"get_count":8,"put_bytes":882,"put_count":7}} -{"i":41,"series":1,"stats":{"get_bytes":2890,"get_count":8,"put_bytes":886,"put_count":7}} -{"i":42,"series":1,"stats":{"get_bytes":2894,"get_count":8,"put_bytes":890,"put_count":7}} -{"i":43,"series":1,"stats":{"get_bytes":2898,"get_count":8,"put_bytes":894,"put_count":7}} -{"i":44,"series":1,"stats":{"get_bytes":2902,"get_count":8,"put_bytes":898,"put_count":7}} -{"i":45,"series":1,"stats":{"get_bytes":2906,"get_count":8,"put_bytes":902,"put_count":7}} -{"i":46,"series":1,"stats":{"get_bytes":2910,"get_count":8,"put_bytes":907,"put_count":7}} -{"i":47,"series":1,"stats":{"get_bytes":2915,"get_count":8,"put_bytes":911,"put_count":7}} -{"i":48,"series":1,"stats":{"get_bytes":2919,"get_count":8,"put_bytes":952,"put_count":7}} -{"i":49,"series":1,"stats":{"get_bytes":2960,"get_count":8,"put_bytes":956,"put_count":7}} -{"i":50,"series":1,"stats":{"get_bytes":2964,"get_count":8,"put_bytes":960,"put_count":7}} -{"i":51,"series":1,"stats":{"get_bytes":2968,"get_count":8,"put_bytes":964,"put_count":7}} -{"i":52,"series":1,"stats":{"get_bytes":2972,"get_count":8,"put_bytes":968,"put_count":7}} -{"i":53,"series":1,"stats":{"get_bytes":2976,"get_count":8,"put_bytes":972,"put_count":7}} -{"i":54,"series":1,"stats":{"get_bytes":2980,"get_count":8,"put_bytes":977,"put_count":7}} -{"i":55,"series":1,"stats":{"get_bytes":2985,"get_count":8,"put_bytes":981,"put_count":7}} -{"i":56,"series":1,"stats":{"get_bytes":2989,"get_count":8,"put_bytes":1018,"put_count":7}} -{"i":57,"series":1,"stats":{"get_bytes":3026,"get_count":8,"put_bytes":1022,"put_count":7}} -{"i":58,"series":1,"stats":{"get_bytes":3030,"get_count":8,"put_bytes":1026,"put_count":7}} -{"i":59,"series":1,"stats":{"get_bytes":3034,"get_count":8,"put_bytes":1030,"put_count":7}} -{"i":60,"series":1,"stats":{"get_bytes":3038,"get_count":8,"put_bytes":1034,"put_count":7}} -{"i":61,"series":1,"stats":{"get_bytes":3042,"get_count":8,"put_bytes":1038,"put_count":7}} -{"i":62,"series":1,"stats":{"get_bytes":3046,"get_count":8,"put_bytes":1043,"put_count":7}} -{"i":63,"series":1,"stats":{"get_bytes":3051,"get_count":8,"put_bytes":1047,"put_count":7}} -{"i":64,"series":1,"stats":{"get_bytes":3055,"get_count":8,"put_bytes":1088,"put_count":7}} -{"i":65,"series":1,"stats":{"get_bytes":3096,"get_count":8,"put_bytes":1092,"put_count":7}} -{"i":66,"series":1,"stats":{"get_bytes":3100,"get_count":8,"put_bytes":1096,"put_count":7}} -{"i":67,"series":1,"stats":{"get_bytes":3104,"get_count":8,"put_bytes":1100,"put_count":7}} -{"i":68,"series":1,"stats":{"get_bytes":3108,"get_count":8,"put_bytes":1104,"put_count":7}} -{"i":69,"series":1,"stats":{"get_bytes":3112,"get_count":8,"put_bytes":1108,"put_count":7}} -{"i":70,"series":1,"stats":{"get_bytes":3116,"get_count":8,"put_bytes":1113,"put_count":7}} -{"i":71,"series":1,"stats":{"get_bytes":3121,"get_count":8,"put_bytes":1117,"put_count":7}} -{"i":72,"series":1,"stats":{"get_bytes":3125,"get_count":8,"put_bytes":1154,"put_count":7}} -{"i":73,"series":1,"stats":{"get_bytes":3162,"get_count":8,"put_bytes":1158,"put_count":7}} -{"i":74,"series":1,"stats":{"get_bytes":3166,"get_count":8,"put_bytes":1162,"put_count":7}} -{"i":75,"series":1,"stats":{"get_bytes":3170,"get_count":8,"put_bytes":1166,"put_count":7}} -{"i":76,"series":1,"stats":{"get_bytes":3174,"get_count":8,"put_bytes":1170,"put_count":7}} -{"i":77,"series":1,"stats":{"get_bytes":3178,"get_count":8,"put_bytes":1174,"put_count":7}} -{"i":78,"series":1,"stats":{"get_bytes":3182,"get_count":8,"put_bytes":1179,"put_count":7}} -{"i":79,"series":1,"stats":{"get_bytes":3187,"get_count":8,"put_bytes":1183,"put_count":7}} -{"i":80,"series":1,"stats":{"get_bytes":3191,"get_count":8,"put_bytes":605,"put_count":7}} -{"i":81,"series":1,"stats":{"get_bytes":2613,"get_count":8,"put_bytes":609,"put_count":7}} -{"i":82,"series":1,"stats":{"get_bytes":2617,"get_count":8,"put_bytes":613,"put_count":7}} -{"i":83,"series":1,"stats":{"get_bytes":2621,"get_count":8,"put_bytes":617,"put_count":7}} -{"i":84,"series":1,"stats":{"get_bytes":2625,"get_count":8,"put_bytes":621,"put_count":7}} -{"i":85,"series":1,"stats":{"get_bytes":2629,"get_count":8,"put_bytes":625,"put_count":7}} -{"i":86,"series":1,"stats":{"get_bytes":2633,"get_count":8,"put_bytes":630,"put_count":7}} -{"i":87,"series":1,"stats":{"get_bytes":2638,"get_count":8,"put_bytes":634,"put_count":7}} -{"i":88,"series":1,"stats":{"get_bytes":2642,"get_count":8,"put_bytes":671,"put_count":7}} -{"i":89,"series":1,"stats":{"get_bytes":2679,"get_count":8,"put_bytes":675,"put_count":7}} -{"i":90,"series":1,"stats":{"get_bytes":2683,"get_count":8,"put_bytes":679,"put_count":7}} -{"i":91,"series":1,"stats":{"get_bytes":2687,"get_count":8,"put_bytes":683,"put_count":7}} -{"i":92,"series":1,"stats":{"get_bytes":2691,"get_count":8,"put_bytes":687,"put_count":7}} -{"i":93,"series":1,"stats":{"get_bytes":2695,"get_count":8,"put_bytes":691,"put_count":7}} -{"i":94,"series":1,"stats":{"get_bytes":2699,"get_count":8,"put_bytes":696,"put_count":7}} -{"i":95,"series":1,"stats":{"get_bytes":2704,"get_count":8,"put_bytes":700,"put_count":7}} -{"i":96,"series":1,"stats":{"get_bytes":2708,"get_count":8,"put_bytes":737,"put_count":7}} -{"i":97,"series":1,"stats":{"get_bytes":2745,"get_count":8,"put_bytes":741,"put_count":7}} -{"i":98,"series":1,"stats":{"get_bytes":2749,"get_count":8,"put_bytes":745,"put_count":7}} -{"i":99,"series":1,"stats":{"get_bytes":2753,"get_count":8,"put_bytes":749,"put_count":7}} -{"i":0,"series":2,"stats":{"get_bytes":2311,"get_count":5,"put_bytes":486,"put_count":6}} -{"i":1,"series":2,"stats":{"get_bytes":2494,"get_count":7,"put_bytes":490,"put_count":6}} -{"i":2,"series":2,"stats":{"get_bytes":2498,"get_count":7,"put_bytes":494,"put_count":6}} -{"i":3,"series":2,"stats":{"get_bytes":2502,"get_count":7,"put_bytes":498,"put_count":6}} -{"i":4,"series":2,"stats":{"get_bytes":2506,"get_count":7,"put_bytes":502,"put_count":6}} -{"i":5,"series":2,"stats":{"get_bytes":2510,"get_count":7,"put_bytes":506,"put_count":6}} -{"i":6,"series":2,"stats":{"get_bytes":2514,"get_count":7,"put_bytes":511,"put_count":6}} -{"i":7,"series":2,"stats":{"get_bytes":2519,"get_count":7,"put_bytes":515,"put_count":6}} -{"i":8,"series":2,"stats":{"get_bytes":2523,"get_count":7,"put_bytes":552,"put_count":6}} -{"i":9,"series":2,"stats":{"get_bytes":2560,"get_count":7,"put_bytes":556,"put_count":6}} -{"i":10,"series":2,"stats":{"get_bytes":2564,"get_count":7,"put_bytes":560,"put_count":6}} -{"i":11,"series":2,"stats":{"get_bytes":2568,"get_count":7,"put_bytes":564,"put_count":6}} -{"i":12,"series":2,"stats":{"get_bytes":2572,"get_count":7,"put_bytes":568,"put_count":6}} -{"i":13,"series":2,"stats":{"get_bytes":2576,"get_count":7,"put_bytes":572,"put_count":6}} -{"i":14,"series":2,"stats":{"get_bytes":2580,"get_count":7,"put_bytes":577,"put_count":6}} -{"i":15,"series":2,"stats":{"get_bytes":2585,"get_count":7,"put_bytes":581,"put_count":6}} -{"i":16,"series":2,"stats":{"get_bytes":2589,"get_count":7,"put_bytes":618,"put_count":6}} -{"i":17,"series":2,"stats":{"get_bytes":2626,"get_count":7,"put_bytes":622,"put_count":6}} -{"i":18,"series":2,"stats":{"get_bytes":2630,"get_count":7,"put_bytes":626,"put_count":6}} -{"i":19,"series":2,"stats":{"get_bytes":2634,"get_count":7,"put_bytes":630,"put_count":6}} -{"i":20,"series":2,"stats":{"get_bytes":2638,"get_count":7,"put_bytes":634,"put_count":6}} -{"i":21,"series":2,"stats":{"get_bytes":2642,"get_count":7,"put_bytes":638,"put_count":6}} -{"i":22,"series":2,"stats":{"get_bytes":2646,"get_count":7,"put_bytes":643,"put_count":6}} -{"i":23,"series":2,"stats":{"get_bytes":2651,"get_count":7,"put_bytes":647,"put_count":6}} -{"i":24,"series":2,"stats":{"get_bytes":2655,"get_count":7,"put_bytes":816,"put_count":7}} -{"i":25,"series":2,"stats":{"get_bytes":2824,"get_count":8,"put_bytes":820,"put_count":7}} -{"i":26,"series":2,"stats":{"get_bytes":2828,"get_count":8,"put_bytes":824,"put_count":7}} -{"i":27,"series":2,"stats":{"get_bytes":2832,"get_count":8,"put_bytes":828,"put_count":7}} -{"i":28,"series":2,"stats":{"get_bytes":2836,"get_count":8,"put_bytes":832,"put_count":7}} -{"i":29,"series":2,"stats":{"get_bytes":2840,"get_count":8,"put_bytes":836,"put_count":7}} -{"i":30,"series":2,"stats":{"get_bytes":2844,"get_count":8,"put_bytes":841,"put_count":7}} -{"i":31,"series":2,"stats":{"get_bytes":2849,"get_count":8,"put_bytes":845,"put_count":7}} -{"i":32,"series":2,"stats":{"get_bytes":2853,"get_count":8,"put_bytes":886,"put_count":7}} -{"i":33,"series":2,"stats":{"get_bytes":2894,"get_count":8,"put_bytes":890,"put_count":7}} -{"i":34,"series":2,"stats":{"get_bytes":2898,"get_count":8,"put_bytes":894,"put_count":7}} -{"i":35,"series":2,"stats":{"get_bytes":2902,"get_count":8,"put_bytes":898,"put_count":7}} -{"i":36,"series":2,"stats":{"get_bytes":2906,"get_count":8,"put_bytes":902,"put_count":7}} -{"i":37,"series":2,"stats":{"get_bytes":2910,"get_count":8,"put_bytes":906,"put_count":7}} -{"i":38,"series":2,"stats":{"get_bytes":2914,"get_count":8,"put_bytes":911,"put_count":7}} -{"i":39,"series":2,"stats":{"get_bytes":2919,"get_count":8,"put_bytes":915,"put_count":7}} -{"i":40,"series":2,"stats":{"get_bytes":2923,"get_count":8,"put_bytes":952,"put_count":7}} -{"i":41,"series":2,"stats":{"get_bytes":2960,"get_count":8,"put_bytes":956,"put_count":7}} -{"i":42,"series":2,"stats":{"get_bytes":2964,"get_count":8,"put_bytes":960,"put_count":7}} -{"i":43,"series":2,"stats":{"get_bytes":2968,"get_count":8,"put_bytes":964,"put_count":7}} -{"i":44,"series":2,"stats":{"get_bytes":2972,"get_count":8,"put_bytes":968,"put_count":7}} -{"i":45,"series":2,"stats":{"get_bytes":2976,"get_count":8,"put_bytes":972,"put_count":7}} -{"i":46,"series":2,"stats":{"get_bytes":2980,"get_count":8,"put_bytes":977,"put_count":7}} -{"i":47,"series":2,"stats":{"get_bytes":2985,"get_count":8,"put_bytes":981,"put_count":7}} -{"i":48,"series":2,"stats":{"get_bytes":2989,"get_count":8,"put_bytes":1022,"put_count":7}} -{"i":49,"series":2,"stats":{"get_bytes":3030,"get_count":8,"put_bytes":1026,"put_count":7}} -{"i":50,"series":2,"stats":{"get_bytes":3034,"get_count":8,"put_bytes":1030,"put_count":7}} -{"i":51,"series":2,"stats":{"get_bytes":3038,"get_count":8,"put_bytes":1034,"put_count":7}} -{"i":52,"series":2,"stats":{"get_bytes":3042,"get_count":8,"put_bytes":1038,"put_count":7}} -{"i":53,"series":2,"stats":{"get_bytes":3046,"get_count":8,"put_bytes":1042,"put_count":7}} -{"i":54,"series":2,"stats":{"get_bytes":3050,"get_count":8,"put_bytes":1047,"put_count":7}} -{"i":55,"series":2,"stats":{"get_bytes":3055,"get_count":8,"put_bytes":1051,"put_count":7}} -{"i":56,"series":2,"stats":{"get_bytes":3059,"get_count":8,"put_bytes":1088,"put_count":7}} -{"i":57,"series":2,"stats":{"get_bytes":3096,"get_count":8,"put_bytes":1092,"put_count":7}} -{"i":58,"series":2,"stats":{"get_bytes":3100,"get_count":8,"put_bytes":1096,"put_count":7}} -{"i":59,"series":2,"stats":{"get_bytes":3104,"get_count":8,"put_bytes":1100,"put_count":7}} -{"i":60,"series":2,"stats":{"get_bytes":3108,"get_count":8,"put_bytes":1104,"put_count":7}} -{"i":61,"series":2,"stats":{"get_bytes":3112,"get_count":8,"put_bytes":1108,"put_count":7}} -{"i":62,"series":2,"stats":{"get_bytes":3116,"get_count":8,"put_bytes":1113,"put_count":7}} -{"i":63,"series":2,"stats":{"get_bytes":3121,"get_count":8,"put_bytes":1117,"put_count":7}} -{"i":64,"series":2,"stats":{"get_bytes":3125,"get_count":8,"put_bytes":1158,"put_count":7}} -{"i":65,"series":2,"stats":{"get_bytes":3166,"get_count":8,"put_bytes":1162,"put_count":7}} -{"i":66,"series":2,"stats":{"get_bytes":3170,"get_count":8,"put_bytes":1166,"put_count":7}} -{"i":67,"series":2,"stats":{"get_bytes":3174,"get_count":8,"put_bytes":1170,"put_count":7}} -{"i":68,"series":2,"stats":{"get_bytes":3178,"get_count":8,"put_bytes":1174,"put_count":7}} -{"i":69,"series":2,"stats":{"get_bytes":3182,"get_count":8,"put_bytes":1178,"put_count":7}} -{"i":70,"series":2,"stats":{"get_bytes":3186,"get_count":8,"put_bytes":1183,"put_count":7}} -{"i":71,"series":2,"stats":{"get_bytes":3191,"get_count":8,"put_bytes":1187,"put_count":7}} -{"i":72,"series":2,"stats":{"get_bytes":3195,"get_count":8,"put_bytes":1224,"put_count":7}} -{"i":73,"series":2,"stats":{"get_bytes":3232,"get_count":8,"put_bytes":1228,"put_count":7}} -{"i":74,"series":2,"stats":{"get_bytes":3236,"get_count":8,"put_bytes":1232,"put_count":7}} -{"i":75,"series":2,"stats":{"get_bytes":3240,"get_count":8,"put_bytes":1236,"put_count":7}} -{"i":76,"series":2,"stats":{"get_bytes":3244,"get_count":8,"put_bytes":1240,"put_count":7}} -{"i":77,"series":2,"stats":{"get_bytes":3248,"get_count":8,"put_bytes":1244,"put_count":7}} -{"i":78,"series":2,"stats":{"get_bytes":3252,"get_count":8,"put_bytes":1249,"put_count":7}} -{"i":79,"series":2,"stats":{"get_bytes":3257,"get_count":8,"put_bytes":1253,"put_count":7}} -{"i":80,"series":2,"stats":{"get_bytes":3261,"get_count":8,"put_bytes":1294,"put_count":7}} -{"i":81,"series":2,"stats":{"get_bytes":3302,"get_count":8,"put_bytes":1298,"put_count":7}} -{"i":82,"series":2,"stats":{"get_bytes":3306,"get_count":8,"put_bytes":1302,"put_count":7}} -{"i":83,"series":2,"stats":{"get_bytes":3310,"get_count":8,"put_bytes":1306,"put_count":7}} -{"i":84,"series":2,"stats":{"get_bytes":3314,"get_count":8,"put_bytes":1310,"put_count":7}} -{"i":85,"series":2,"stats":{"get_bytes":3318,"get_count":8,"put_bytes":1314,"put_count":7}} -{"i":86,"series":2,"stats":{"get_bytes":3322,"get_count":8,"put_bytes":1319,"put_count":7}} -{"i":87,"series":2,"stats":{"get_bytes":3327,"get_count":8,"put_bytes":1323,"put_count":7}} -{"i":88,"series":2,"stats":{"get_bytes":3331,"get_count":8,"put_bytes":1360,"put_count":7}} -{"i":89,"series":2,"stats":{"get_bytes":3368,"get_count":8,"put_bytes":1364,"put_count":7}} -{"i":90,"series":2,"stats":{"get_bytes":3372,"get_count":8,"put_bytes":1368,"put_count":7}} -{"i":91,"series":2,"stats":{"get_bytes":3376,"get_count":8,"put_bytes":1372,"put_count":7}} -{"i":92,"series":2,"stats":{"get_bytes":3380,"get_count":8,"put_bytes":1376,"put_count":7}} -{"i":93,"series":2,"stats":{"get_bytes":3384,"get_count":8,"put_bytes":1380,"put_count":7}} -{"i":94,"series":2,"stats":{"get_bytes":3388,"get_count":8,"put_bytes":1385,"put_count":7}} -{"i":95,"series":2,"stats":{"get_bytes":3393,"get_count":8,"put_bytes":1389,"put_count":7}} -{"i":96,"series":2,"stats":{"get_bytes":3397,"get_count":8,"put_bytes":1430,"put_count":7}} -{"i":97,"series":2,"stats":{"get_bytes":3438,"get_count":8,"put_bytes":1434,"put_count":7}} -{"i":98,"series":2,"stats":{"get_bytes":3442,"get_count":8,"put_bytes":1438,"put_count":7}} -{"i":99,"series":2,"stats":{"get_bytes":3446,"get_count":8,"put_bytes":1442,"put_count":7}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2243,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2152,"get_count":3,"put_bytes":148,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2156,"get_count":3,"put_bytes":152,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2160,"get_count":3,"put_bytes":156,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2164,"get_count":3,"put_bytes":160,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2168,"get_count":3,"put_bytes":164,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2172,"get_count":3,"put_bytes":169,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2177,"get_count":3,"put_bytes":173,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2181,"get_count":3,"put_bytes":347,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2355,"get_count":4,"put_bytes":351,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2359,"get_count":4,"put_bytes":355,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":363,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2371,"get_count":4,"put_bytes":367,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":372,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2380,"get_count":4,"put_bytes":376,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2384,"get_count":4,"put_bytes":342,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":346,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2354,"get_count":4,"put_bytes":350,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2358,"get_count":4,"put_bytes":354,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2362,"get_count":4,"put_bytes":358,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2366,"get_count":4,"put_bytes":362,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2370,"get_count":4,"put_bytes":367,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":371,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2379,"get_count":4,"put_bytes":480,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2488,"get_count":5,"put_bytes":484,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2492,"get_count":5,"put_bytes":488,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2496,"get_count":5,"put_bytes":492,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2500,"get_count":5,"put_bytes":496,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2504,"get_count":5,"put_bytes":500,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2508,"get_count":5,"put_bytes":505,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2513,"get_count":5,"put_bytes":509,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2373,"get_count":4,"put_bytes":406,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2414,"get_count":4,"put_bytes":410,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2418,"get_count":4,"put_bytes":414,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2422,"get_count":4,"put_bytes":418,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2426,"get_count":4,"put_bytes":422,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":426,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2434,"get_count":4,"put_bytes":431,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2439,"get_count":4,"put_bytes":435,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2443,"get_count":4,"put_bytes":544,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2552,"get_count":5,"put_bytes":548,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":552,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2560,"get_count":5,"put_bytes":556,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2564,"get_count":5,"put_bytes":560,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2568,"get_count":5,"put_bytes":564,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2572,"get_count":5,"put_bytes":569,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2577,"get_count":5,"put_bytes":573,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2437,"get_count":4,"put_bytes":470,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":474,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2482,"get_count":4,"put_bytes":478,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2486,"get_count":4,"put_bytes":482,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":486,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2494,"get_count":4,"put_bytes":490,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2498,"get_count":4,"put_bytes":495,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2503,"get_count":4,"put_bytes":499,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2507,"get_count":4,"put_bytes":608,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":620,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":628,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2636,"get_count":5,"put_bytes":633,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2641,"get_count":5,"put_bytes":637,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":534,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2542,"get_count":4,"put_bytes":538,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2546,"get_count":4,"put_bytes":542,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2550,"get_count":4,"put_bytes":546,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2554,"get_count":4,"put_bytes":550,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2558,"get_count":4,"put_bytes":554,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2562,"get_count":4,"put_bytes":559,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2567,"get_count":4,"put_bytes":563,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2571,"get_count":4,"put_bytes":672,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2680,"get_count":5,"put_bytes":676,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2684,"get_count":5,"put_bytes":680,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2688,"get_count":5,"put_bytes":684,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2692,"get_count":5,"put_bytes":688,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2696,"get_count":5,"put_bytes":692,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2700,"get_count":5,"put_bytes":697,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2565,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":343,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2351,"get_count":4,"put_bytes":347,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2355,"get_count":4,"put_bytes":351,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2359,"get_count":4,"put_bytes":355,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":364,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2372,"get_count":4,"put_bytes":368,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2376,"get_count":4,"put_bytes":478,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2486,"get_count":5,"put_bytes":482,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2490,"get_count":5,"put_bytes":486,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2494,"get_count":5,"put_bytes":490,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2498,"get_count":5,"put_bytes":494,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2502,"get_count":5,"put_bytes":498,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2506,"get_count":5,"put_bytes":503,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2511,"get_count":5,"put_bytes":507,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2515,"get_count":5,"put_bytes":471,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2479,"get_count":5,"put_bytes":475,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2483,"get_count":5,"put_bytes":479,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2487,"get_count":5,"put_bytes":483,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2236,"get_count":3,"put_bytes":379,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2387,"get_count":4,"put_bytes":383,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2391,"get_count":4,"put_bytes":387,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2395,"get_count":4,"put_bytes":391,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2399,"get_count":4,"put_bytes":395,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2403,"get_count":4,"put_bytes":399,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2407,"get_count":4,"put_bytes":404,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2412,"get_count":4,"put_bytes":408,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2416,"get_count":4,"put_bytes":580,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2588,"get_count":5,"put_bytes":584,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2592,"get_count":5,"put_bytes":588,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2596,"get_count":5,"put_bytes":592,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2600,"get_count":5,"put_bytes":596,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":605,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":573,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2581,"get_count":5,"put_bytes":577,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2585,"get_count":5,"put_bytes":581,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2589,"get_count":5,"put_bytes":585,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2593,"get_count":5,"put_bytes":589,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":598,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2606,"get_count":5,"put_bytes":602,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2610,"get_count":5,"put_bytes":711,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2719,"get_count":6,"put_bytes":715,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2723,"get_count":6,"put_bytes":719,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2727,"get_count":6,"put_bytes":723,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2731,"get_count":6,"put_bytes":727,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2735,"get_count":6,"put_bytes":731,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2739,"get_count":6,"put_bytes":736,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2744,"get_count":6,"put_bytes":740,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":637,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2645,"get_count":5,"put_bytes":641,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2649,"get_count":5,"put_bytes":645,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2653,"get_count":5,"put_bytes":649,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2657,"get_count":5,"put_bytes":653,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2661,"get_count":5,"put_bytes":657,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2665,"get_count":5,"put_bytes":662,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2670,"get_count":5,"put_bytes":666,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2674,"get_count":5,"put_bytes":775,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2783,"get_count":6,"put_bytes":779,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2787,"get_count":6,"put_bytes":783,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2791,"get_count":6,"put_bytes":787,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2795,"get_count":6,"put_bytes":791,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2799,"get_count":6,"put_bytes":795,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2803,"get_count":6,"put_bytes":800,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2808,"get_count":6,"put_bytes":804,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2668,"get_count":5,"put_bytes":701,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":705,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2713,"get_count":5,"put_bytes":709,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2717,"get_count":5,"put_bytes":713,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":717,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2725,"get_count":5,"put_bytes":721,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2729,"get_count":5,"put_bytes":726,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":730,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2738,"get_count":5,"put_bytes":839,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2847,"get_count":6,"put_bytes":843,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2851,"get_count":6,"put_bytes":847,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2855,"get_count":6,"put_bytes":851,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2859,"get_count":6,"put_bytes":855,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2863,"get_count":6,"put_bytes":859,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2867,"get_count":6,"put_bytes":864,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2872,"get_count":6,"put_bytes":868,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2732,"get_count":5,"put_bytes":765,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2773,"get_count":5,"put_bytes":769,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2777,"get_count":5,"put_bytes":773,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2781,"get_count":5,"put_bytes":777,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2785,"get_count":5,"put_bytes":781,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2789,"get_count":5,"put_bytes":785,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2793,"get_count":5,"put_bytes":790,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2798,"get_count":5,"put_bytes":794,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2802,"get_count":5,"put_bytes":903,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":2911,"get_count":6,"put_bytes":907,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":2915,"get_count":6,"put_bytes":911,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":2919,"get_count":6,"put_bytes":915,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":2923,"get_count":6,"put_bytes":919,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":2927,"get_count":6,"put_bytes":923,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":2931,"get_count":6,"put_bytes":928,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":2936,"get_count":6,"put_bytes":932,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2796,"get_count":5,"put_bytes":829,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2837,"get_count":5,"put_bytes":833,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2841,"get_count":5,"put_bytes":837,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2845,"get_count":5,"put_bytes":841,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2849,"get_count":5,"put_bytes":845,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2853,"get_count":5,"put_bytes":849,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2857,"get_count":5,"put_bytes":854,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2862,"get_count":5,"put_bytes":858,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2866,"get_count":5,"put_bytes":967,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":2975,"get_count":6,"put_bytes":971,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":2979,"get_count":6,"put_bytes":975,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":2983,"get_count":6,"put_bytes":979,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":2987,"get_count":6,"put_bytes":983,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":2991,"get_count":6,"put_bytes":987,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":2995,"get_count":6,"put_bytes":992,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":3000,"get_count":6,"put_bytes":996,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2860,"get_count":5,"put_bytes":893,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":2901,"get_count":5,"put_bytes":897,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":2905,"get_count":5,"put_bytes":901,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":2909,"get_count":5,"put_bytes":905,"put_count":4}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index 49d1432368931129ee8d40fea9194ac4ef73c286..f80e531e59c90529254a41e20103adabd5f4c02b 100644 GIT binary patch literal 17263 zcmcJ%1z42d`Yt?3gCN}qNJtDN(vlL=IWROMf&$W|NIOG^fTVsj}G4^jHM>SQELBp?uoOyhxyAqa$r z0)cSZiEx1!Jb7mk5C}&_U)xv}gTVkXZ{NPf1|1+!2&Mys0qyK`D1!R>KuV5Fm=1PO zh+;>ONNP>|iEAKy9E$!2iLwVr~h=nErW= zVPtd%ngRk1MhtC%uua5(hQ4BGk}<*hdm$khRnS?8=SHgk-db#Hy>ml2>Cw^b+Yok4 z0ca;|=QU__?(N(weMN|~a0m@3goY+W5%>)WvF!-4#e`tnuZVdI{MusPx??&zFhGAW z9fS2bdzihqb%P%<9|t>70T{)%81}ZdHa0dk8ylP4+}!r|b_E56g@uLA&Q4H{m&u2X zwOr8NiZaNv+E?u*8W;s)_XlQPAP{*M_8(4)068-V#17I>Q8Ip(^)nl%I=_?-)G0GN zjKsye8f&=}UL-C4szM#7uJp4aGpZkQmwtnkD54iTLH}nzBr2w23L=|Fw`+|wnP;Ag zGgFRkj~f;RB1bi4o0su%I|4H(Q^$88P&GzpW=C-e&uO)bPDzYp7|EM`lmnaFvV1Kg zi>yB`XpdP8UAom9gW9a4TGghQ;EgJZ(>@Dz^cBCa&IreM_iJ&Swuxi*HrjQ>?8y&}<(w1SvRFYFX;tv7QaHGLF3hpC52%NF=PFu4SaFX;qp zf*Lr7p4*7?d=WBs{EnGZuO*q-@K3#IZaPm;b%=I49N+$ttH8K>@{Bf>&7`4;2lnEN z5!C0y!|C38!Xj-Odc>gmg0F}PkNY>|qbsUgSsxzpHWX2(-e7uudVD*u`^l;6EZ1S{ z#m!+|znR>M{0W!#08itS<-I}$ZqRIM4}L#`o*SMV-qt+ZbF*&FeFiA2Xk610M z!<0c@2NvGtLmp08SOim=ObHl|h2@`>MWHu!i9wR@_mxZg?;*y6Y39vo$*9#>RElDq zpi9rF&)UsZ3s@D1%93AWgpj8wRAc1&xc7MZGhl!?s$xDRI6*Jf<9ugr!Ac7ew0-L$K~gkQlF!mjIMQ%IUX*Sd$~e8H|0s*y!@n%IUN2W zGSML}R5DWpA4NOyTb7a};GDzA22)F1T;2sw7s1`KcX(JHWk=%*Twu=1h!aT9fpS<& zYCYqihi`9`o&ONM2`*^%fX^G(tP&;+}<~Mn1gxI+k@<^VJ9)#8y`O zvy!I;J2QKq+^?T+$0~tBE>3+@wXeZ&6dT8eHzaW2`2Smr4ugvuJ~U2lvH-6J;(~%Y zWK{du&0;HDSn0TnA=#;zzy(kU+urmklbyzQJ6e8t>)7RVwi`2O54UO$dbJX>+@$)| zS-c7d)ULSdbOR|qUD&;WF74c9&Zu-Fykg#mp795qjaSkuU{>4OOlu8xwY`;2r)%<;{oi6nYlL|EHJ&AE~ZO@R>dZ9!(JMw2*{c#K1Py%YAW^d~%@G{_X zG7eERLPbv|C`8U89zz&^>)+BGtlo2bXTE=AQ!&hMcVox9GTU$(uDmsEQu;i-;$voE zIg;rqrH}J>zv5_yq>aI*66rJwle(1$mBlM+8<#E_vmR%o2^;w2xtVrMs?%!b62_`e zyA%Qr+I>C5_Iy>3F8X4=y zF8Xbyrrw}Fw?v|0KG`zML*JN9fz;tZY6MrkqVZ+LfZ=CBu-uc{Knj!5(mQQGb0ma5 zu)16iqhNEAaI>1cF~(QwGLcgi4LN1C@+IbIeVC+GYMwPO4PTA8w6{tYX=NtSGLQ6cRg2W;=2I1Bht~NpdW%OVi_kAblXOE08xVX=@XbMm_O%pmEDvuZM|v zE0KTz-g=*vpGbheWZ#JC^O*Q$Q@%MYyGkd$d4v>|jkq!kRH4K!{k1o6m{`MYyksH_ z6YQJsmx3EdmFpcL70qR|6O2Wz>|iMcdRh~znEJXaUgX^@ar}EkmnH^KV$9yS?Ndcl zyDfzH&AWS#1!V(&>a7e>g$yE3#x>yM8lQwqVd9(BOVLJT1B4@A3(GVNI5WVRwBb)Fr1~WMgL#n$ zbgA>qg(Y>Xsh%MM|5d0>fDaw4e0i%S&!A;Ytd2-8jT=_Acn*|#~1~mw+E;~ z{3uM`o9w)ZbNu20QTq^Ho5imM;!%W3hJi!#&1CLBcl11GRombtuf~h#(lJzn?r}aM zX6Y0;B}Ip^acJ4v@ZfH0Fl~*Xr#V$TQ^Kg|M>ec|IFdTmSVVlEL_P}18RUqj&wkXq zT%8s#r_99+nc>$S;(G|aA1n^3fyLXvud)ACCOzoJCvxr&ZPe>6 zSZ@@$G6Ct<4sxw193Ab-Jj%DCq$5H-JBn}(ZWP;n{>T^hljhcMgW5a1Z*@+eRPtC= z{VLHMxAbclou-F{g34++9KaqhGUq5&BIgK-e#jjd2hPRFc>0&_3Q9VbkN9x$@ppq( zGQK2`IndCTmLPzF^F)v>8v=I-MUj~98jhh3G%{dCrL2exE?YefV^t~@?5Aznrin2rX- z(0qw0kwytOW807+M&!&2e?V}m3)HB!x?6vD2qHt2cpU{dBHc2gD&oh!gyNyY^4Ya) z1=`K-cR^K?DS>+ie9dZsOgsz(*>i$lc<@}>HcnD?{vLQ&J5loS2zTCV5@ za~~X-hA~Y$_p4TKaOA6&=&^Oz?;wAfxx5EF>m_S99vpeS*Z&w*UqI@t0bjvC6ir?( z4qscmyBq-{)2Cw7I%Y(0GjP~0i9Zrzjk(1o{5ByYYmI7xj|2}_J&%YEqV$W@dCp^` z)VxP)!`&r+`*Kho9Q|sR@cvF}u0f#oVN-OCl_mXgrKGWJ(OE164ENzMF;*Jl1poFQ z_q0aXZ`W{3_}7pQ+~Q((2_$;{%DOBM)nxWHJN+zi&jlUBqkz$RdLi_X9T|hFHm0Y@ zKb?`GO;Y+Ev{K+5Tew+nPQG_LRrP_ZpQdk2V$us(qdHrdcYv_~Qq=_={1QV7f?0(f zt;z)Bwd!$1i^Y`YUd^hm1Q4j2D{%Dy1pUN*MiV0e&$FbjcIikG|3}p<(RdrgViI;JH z*~fbC-aDUVFFYUrUD8EQp|tB9`F%E4E8>RO`9p;l1q5!oT~Gs6V^k)c%b9q1>eKBH zcH>zCFHyeV3%t7s%(L+G3EYOdP?M#xPso~H6lUCX-TZj_67b4=aC8NcVS%a}Qjv>a zb)m?MfWRRzGU-pm7pd|OWCLw=gC=E9f%TgL?pLJbh$bca9fe|lWl~5UN?cZ%5UK7J;vM$Xhwt8<<-D+f2 z&{r#ZQEZ0Tog6&8n&!F?DWq3PzplV;W_W`>bqZ_`KgzID<~qS8j%JwHD{fj0lepU5 z@E%tG$`KuGC97PGTj6fAD#UsYf3TxYN+at2!PQwmkW9epz1Pb)4kG8#mJdSwybXxj>a^wchxKzmExohKJ@y)D+w1K7ewoj>)FC6xg*>^#4@H8f@Cs zsnr^)H1}KISk_B32uhCwITKyVS&upf_9SvH=orteDYQ%pdJ8Eu#eIkyc<QS5@5vrq0!B&W=svNQlLu}FLb^}_?Qkc?jPWBD2OFD{~0s32l{9u^!Un9k~2_} z#_>qi{m7M%ieXeeuyXDfsg_}0;k@!nvEM&imV3Wuxb>XdbRY@*C{~@D0~{dUJFcl7 z%8@kbSqtt*o8kUQVat~iOA}ySMLe(`SgqzFT&JUJ%S4PAusn(a6=6R|U_bZ!-ch?y zfi^2|@WwMcM4h_tjE3_HDX4ikxI$$#*Gea=IK!|V!(U(&Q27r5-Ag$pvpUu zy7B!!4YhtHsKQW&q|nA2OuB?i~I%o-c5Y*A9W?o z9vodmD;sxvq>LEIxXeZt7rSYVvjzLSa6eApj4XX1WGJIqk*l~91MO4QRR8836JJ|;Ho?V$h?W;z zr<)KAI4a_HU2jWAWVIM)g{^lW4i)$4vUj?Sg)_Mt&du`~jo;5b69n{@rsSnF&Gdz=+WNBDx7k;897DRh=&}fRwzG=VB8o}h+L7OD@6%GyU4%!XuCyM#xRm@}wRcpxw_)0DeR1&^bGBP>pRwqbiFE}B@)<2= zI;|HyS&rUJuz5az1v%YXzlX_!F1oNhY8)snpgEFd3Q{ ze{{5Q+7DG}L87gbOvp*54MXE_uqteD5QB>Dg)=r*p(J8TUknPz&aSt=L!otnO;Qai ztpsiVS8NpOUCDJFbe83P=)g^_@&N4*4gC$1cBRR=m~9(Ox%V@BaQ03{V@EFOZ&mil zi^g~xHAwbDt_iITl!j<4$>A82YorYQ5}r-2Ror85-05#~32fxA0?bC@$7gTS2F`q) z+#BCqU7(zxqL>ghK4TPV??}m2_p+@f+44x)|G7olo-RZ|_q00$#h+&YD_^H!|H?n( z7V9f_N9*b;JF}u5<}i3nEKVfQS<)>Glcbp7FpkS?8L6#=jGL5XZbE>Q;3OUVTdML| ztyp3A33dHZO1hxL`0v;c@gPT|S{LjnF4FaLA2NiMe?|3&=x>g64g&I_zDffgJ6Oq>G`-$_?_Xv0O zA_@a1-L5BoPFgrmExbXwUDhPQh->Jgfe%x%<7OsG5wN>obgqP}E*Wj_4Sr|duYrKH zRZ&dLPt~b^Vv`eBe<(%!?u-&%OHEWJMM6nuwP~RUl?_Ej=hMJG?mmj$trjGM<2zG+ zq6(C5S>QdsTdD9wPlD|Ma z!gW^LcAF>|VM}GbpP2d_;kC)7CYDNSb*bcyp8kfJB&l&9D^?6?G**w%GIo*Q-PCtX zD$N7%zb)}CsWF)$et#w^yIVoC?-kKjTAQX%VxbiwsjJQZOb{l=(nnQzU{RM<=n{f#fnmjMZZ{FG->w9iYNzj_XdI?W*?X zae#@j=hgG45^k23P;+Vjc!wX~sSH(T<;wcR z7J3&?=q;2XqBgNE9J2M>ia+}nXEOu8^as|H8;P-~c$o)Mw7>Nw6|9u1v|q=!)g6Lu zMYIa|%bu36&vdDc*O55uH8e0G6dqBMztSn{3JIe|oz` zIzJ2K5K^DIN{WX*IC_e{rLupp_ZgC9S>&evDMel%BuVl42le0pPs#H~xySUuqgB@-< z=y*5D)DBjlEUu-gLaJLJ%Sfs%$JC%N z5Ug1A0)}p~j{NR>()qHT<)f?3la0!)QK97#n0X9YX6Vsl*xbz%pUbGG1IX;xVg^I$ z>qLxCuQ#Tmgc#mQ>1CBJQ((-?g00w&S<=Yaikpb82jZN^V(u7kP2Y|8%e8}TNM#L% z<8VqG5%0*mf1t1_7Z~+iRRM?0&m;&7mXbF%*eYO})^23*vQ}w8OO$ZVyCyx7(|Vca zE(+IzXD7@rJDchmrDb&n`IMo~Zew8xpBn}~L~^oIhg(dnvKMo~6ccYl21kj?>uMWo ztHNqh<1x?? z{9(`^r|_H2)fz>A$!iidQ)%Wl@=oW=^Q;b=#z@q}LY1NKE63p|46}VcO(?nZ)VtOnuEUc_7p(z!}`tiWq3O8<2k_cwKM3E~TbX5@vykJUx zD#RN8)(riW-h{aA6N9mpGF0+7r7um`6nevLi}^;^FZp|%-fZFG$TcSP*=l@BoN*=V_Crlc}}#<)#plePUI}#=c;n=)4DCq>M#$U*b0Hw zhb_lRsio>r=7hHT$_fGS0=}b&q?Mj{tg_Y;7)hr^17i(d+y5$gGVX&ICp5CA&N|eD zhrb<%X^F5;al?=7-ljf%e3LaE<#}6VxNu}YtKMrgafiq`UW{~Wr>&_1l_NJO87F04 zh$7j8NWiSvRXl=R*^$i?D0JrJzCs*IZG|HB^Q;1#TZB@7g`ek}v5cjHwXxI9xqf%t zE~?frR8WguaO+oYJN`w141H4BsuJ(9xoMJ@s&uvjM9#mj>Z6T(SWIarI(bdUeF`!% zIs4)efU9+<0f|!X(moPb6G2{s1y17_uy*($XgfQ!EPGQNo!(wb)qV(Cs^Cb{+b$a> zd{>&^P>ot-R+F2|FMl1h-AdGrk5FyhgmM+y?ckxOv!TtJ2?njv$+%~F1PXv_oa44p z_6hJ9O%ao<^oEzrQ|>Jk?F-eN6y1^h^h8_7T^!DFN1h>7jl6Vkm&OmG(WZ?`P*l1#lyOK-`vJfns{t;AjkcGGn`QySmjh!m3qzL0zQFZ zYTxOm-^bV+O2&S6{AF4Vq(I9a5IZlIAFXGQylZ01_YlI5EVWaHI>PAt5a}ohHtSD1 zB)y~Y971Zwth;DU(q=^oz_Y-8=Q^0HjInGbF0-1#uQrHAEe^frE$euL8bJ%aN=WC% za0dllru7}4ac1ZRpB$`TosmAHHEBkXuH09HR)vkvDvS0n+XQnRZ0rVI&DsRx2%|UY zd_x1+kuxNmC%?UdFY#N5$}Fbve}$r41pvQ!`0_|Wr1h>=J5U5JY|aGmuyfD1gl)md zAXu859&xS`w4PRN=TA)xq+5+B(mn&ng0inYC+RzH5Zs3?>kbJeu7A}<%{k{t_35Fp zOPRi-cvGFv)M=X&c|HnMs0eMQ7J&4~1t7H$u*SLD!@w<@)Rvq_f$cuVw4<>%EG3tc zu7ieXivj&CvA5Ga|7T~9I7xZ+WzQ2+IKBuWS_aNvp1=9Jw3#1b)QY`R-2#V51EycXY-L;33@(CW*J$IRx@0XopYqT zrX7azEpiP^aWaG)Fi++Y&NqGpuncTH)2uE}--;TsNIB76XHkWMlG%~1RX;DZF?$Xh zV=O=tV!(y#zONj@2uv_Lwhh2M6Or0{6jy?FhoV4hivJnp{qQ18$AQrjqOYan!e}Ex zP1FN?TAAV46`@;S-oG* z1|<{|1gqfhnahmao2{T_(6N6)b@TJNFbf2S%UUxc23K9@b*gF4$VSk;PH>(zzQ{_g z#*^t1>AY0?xvnnCsW(De`m`MacTc192dN(6?-BfS-uUBR1Jk+4`uFUMYVFZ>-~k&6 z1ziUV5GbcE3WV_e)5IpO?k%sp@tyCePv1Euv84j0Rjn4*jr7%k+L!`&cdV7pMb!!{ zGs%r7a3QafgX|*FrXVqPq+PLhKot6{>6aK_eFQMpms$pv`6$B{(LabBWJv(9W8i}3 z!qCNwG%j!|H3b%&&0h;Htkh!4E)f;qDiK1hv>^o=6>v02-n-ur81mwN$yDyg4upO7 z4oHNoz|L<|iL8`$g2I2X^K2Rb1*`FF$u)biGp{pP>WFJHA;cg>0!r4GIqs>GGn5CH zW=pi`4&sDp@UnI*rGh>y{UI=bJXlm4j{nZC1zLOSO+#pQ zO@t5&b5n&#{~uJF2cFx!`2I?jm*nra&i+^{X3$DHCG2QOV7{;GJ3y1l zvO$|NJGAk8N?z3bl_@HcXf)JzlQuI!{2zwg#j2Bq7*3mC6r z38nejfl$rf!EkdqLqTcWnV)WfH8v{cB7xD2GQJ5PSOL4O)bkQV&Dx%u3})5_hVvC@+7 zk^;y`Okxd|13-=1*^{`@+sa6tl5NyHiG%w1te}I$^;4Po-A5lEUVD>%J@bOu=kmcf z@awhpjv?pg1ogD%pRBu~jY&533sX7P+8m$1P<{R`^}?E`^;sE_gSz%GW8xt;_X5XL zn9P%3!(C75bi5cg%|PpT$zBjIpNAVhXnyflMc!&<;1iYRkY7y_xA3mkQxB<}C?Ye9 z$Zz1xHIcD{i4TVE7F8=u7WgoYf+ForFDlL9vgu-q?E50J4;yJUE2GT}1_I~a(+Izw zKh>oZ;@K0U7JX1pQDb&z(4cs|JgQ)^&afXr6NY*}GAiWrjksVvr6N!;&6>R~>ivkA zW`PhmW5qB|=Xu^?k6*RBXlO1U;NHK7-Q;U1H}=eHS22Vp7V2>FfH^a**$KqxHHCef zoTYO!zX(=PPIU7%Yn)PTKKJrZatSv-k9d+S^02#NSiQNg;N-`rN^SqP1PB%KaI>mWJGC0?6))N?}jW$+VoL{`ZfY+f@hfRoo=6t4^3dpYyTGUtYIUMBaUPriQK)KLMAGOa#1W@bJ9*2a-IxT=}^kz0du z#-a7jqi+wdwR1|{8j=)WccRIB&b*tKBl(rEp7!F?M?&R|=i>oZA3dt$^5XUszxm3^ zmuwRSLOs91yq^()#deM_bsZL;i1rOiyH|Tj7AniREiulT>tOc=0j=9s<<5^pAK&Od}u<@g19jIflB#p_% ziX&(8vFPBs!w=q4D)`H@ z+)hT14y>q53Z~g=(VlFG@oZrUc9&pLK=q>rYT(AIe)zu#jr~jY)AX0>r{>>PKk3vH zy-X&o1?j$$!&@Vh#X<2~1QRD(7JGfa^R@a@*B z&-FUwGg@lXnD_xbBAOm!m zNZ_iaF?_untMv? zFR|ywd{bedL@~^V4Kb+XQW$*p*qh<$B2&iLPb0J=Oq^B-Vs?J@TEs6DkXqX4lhwEbk%V+VtH)6S&lctQY`;s54GF`nY} z9XxT3DuMgPpPDSh3mksage*Ky<$HxLo;HCQTzmY~ys^N<$DFVMtNHmcKogmFPAgm1 zua^@OaIJ$svbTX6CjD|J?M><&a@-3L3*o?isz4KT8H>)i573*PmpxwX!BLiJjTsoK zxvhbj%1U&R&87eTfz9rgpFPQ=sIo8rhC~gsV#ux(I0dx&ertU$847N#Tu9-gDDW3L zu;7FH6i|7U9@rdinR=J@JtWX{$_!Da|jGIzAg)RPQ55~OSGg5{lP*=bmq|)D&QSF{MG5mfhRrsUn#{|rY*=iakF z-~o#XzjKFLDI}qEf1kcR1Q)_Br2&_vk^$B@jJ{*exUd7We4;kZfXA^tZ9M#X>nv18 zwRUh?%%nY-$Dh^nOJK2006< zWY^v>(H(SzWlF86Bs*)_xtDoiAKuM; zw*3%NNuVdutns21)st`!1BhwdkUi1V=S7ndA#GS4fSc0@!yJ z&%gPPp+G{~$0PeRCeEchFY{2!a{{{k6_zYtDb7*TP00!BKD$3UuQiB#)9N*X-YDX? zXl|=G1Xku)`uBwF1D*S*V+oVm%_SrBqJ*1Evhh@65gcTC>%|5Gkw83hQ{9*hUNG$e zz1%U`x=St)0Bw)!^t3vmTLx4@+zF8+svr`MxmWU1LB?;w`Nr-x*szZ!4x zfi6VGX|-)KonX#HRmB-PSOP@q|1TX_&KDa~+$x?7?;u|{$lb;Nq0Si>A{;;wglJ(& zaLQZKJ8V1~BJMPchL40ux#v*})c zg_%Zwh0Om;55Lb!CROEuwfeTVq-ZKGaJQJe<+lN);&_fToZj!TQtWF0z3vQZpaV2H zNvDCN&w8}Ud&hmEQ@cA1LYzenWF9uOD3U;G-MH-mDT_RVXETE?b93c4o5?}-AqB(F zD~d(MTu396D(n?&w;)Z9c|xA$i&k8q1wE z1?ls`^k2FRe9J2K_j+*N3}f=wqNlm*!^G1%|UdJ5GC1Oc+c60}pkINc|4IM`7lX6<$`EGzQ+ljqLn!-hyC1DQ`6b;)gC&xVo%+sIFf~`3k*BKq6Vq#=h03;PagZ0p~kTJFen`X~aI%yhu_tzZIg6EaS5Au&|n ztTEh8lWFxfHVD1;v!n0p3g?r-Eu>{twCGO*jR@Mx?=~lAzmB)c{+1ggNWz}mQ<^wX zm^5;)t^AdNcp!ri{pmWIzrHK$;^H>ujDL^8&^ZC+`4SZ^DEe3d^R4}Axi;M1_?^F^ z#J^`G8NlM%VNbIxpj$V!vVV$>_YVgjS7OpxHguCkgkwAZfO%L_oL_xs3g`#|Wod9L~6v4t1&G9xvUz8}` zU(rPsY#Pv}H%2!lW0{{`2Ou9{IdC$N4)n@E%5#{PvjMxhPP9dognI44|6rh$g1i$; z%KNE7u_zGd6K7~e9i%3s)L7@Jbb(LQVmP(W|6CjjImKm)GTLMB}E@_B+a z83;gP3DBh}MWd{X>Hy5j1?%n`E&Ls~CGuH4yS|;Pbz9|DR~+zeU=Wp{7Mu=7Z+RQn8C@iXVCWtg_y^ z&oOEg$5}Xy9T;l6H>kfU@6bko@@_aIz^;)QID~nXs@{dFiC>o(IeTJ7k6kEJ(_F6w zif(gjH#%NDZ!Dt607Upczz-}z|{ zEfE0Hwm?9f{dVP%6e4%8yL@@bhsCY`#Y(3%b@jqHz#$se17-!wPMe7|v6dC(UK-O( zCgt6RD!?qa5Dom8o|xnJADRlEIVlY;G)#fiO?bLI#4aBwR97M2c}mYOD<|79uVB5%$}CTF{#0zE z?7SjZj5YD|%d*I9B7TDgD?bv+iD1X~X$7och`9`~Gc%9Ut&XH5i9e~?QvZ!Hl>`5k zl9u7!1wbAFhBY6%U)Q#;2;S`!Q|<*^GR}to;6eY`YQT&Sx7kN2YPHD}&!x0Ql{A3= z1SLPEO5F{+oBpPvh)FH4AS2BrhC3QtCKpoZ^t}4XCwi{Cc;)04VK^WE*xjdZUhkH4 z)B%J& zXtrj552{%lYoIQIjQN(3$rJ~tcy{b_oRx4bxfn6R4sRhVotuHwHf%7kdw#*|^ADz` z5Tp;i=UJc~2s@=Yos{I=PHAvR*|h0W|K?w3B|w&BHw6JZ!v<*S`M=_1!g#@KWrnqx zQw+N-SNEYHYGD6~1o$l{3;PAh9@z*C&@DV*!+D1?)F~~2v;w;nIR>yxVqlvAZn2a= zfOMuJxVnqEW69gGe?bPGO*8bs^KQaz*{|nrHS@iWmI?EP-cRqKanNW7d;@Im29{tO z28_EI6F@=)j2@P4uRJp#h({xS^A?}9(icPl`)apt$rr2cF_s3I0sJH1m3OUw&~+(6 z0q)u>=pQ6rcYqEiD8qo=u)}Jq`%Q_qvVbofq_W9mtr<>Vy8lUYZ2xN`8(t+*v@eJA zCjxGEr$tc_I+Lsjy^RO^s5U_svPnK2G(E@{pm*(0QDhghY(9!FTE?+tQc?Z!P+RB&(*h3eT(7t zuS^O#$CYE1!8mT{>WL?^wYOP5I>5S(uk8P%+x59p?Yw^Fs(_g=*VSA+cf9I$``R9T zc}Bm-11_Q=9bU(Ut=?-L`yI?gNE@4C0WB9#BhfsYY!8Xz$&2$4;UFavSl{3t!o2Vs z^Mzgd&ff+E)ufw>u#)(w+;I;VygnTCr|15m2X0&S^WInORP9%guunFf(S_AS0?gRF zVx(n8y1KB-pfeV<)-vyGy|@R;i=hIhHjGa3QCshdW$2c{~YJ)UNwq&IHKe+&!gB__v9P zXiv@@r@FZRuyhjpFO-BN*~jAWkNz-gwTP@%#fNd4c;cj3rluAVi%udX;!gVV7q=(z zO6)y#jx-26?asK@1sbFPqQ;dAydR&L)v6CzG+2d0cy16G4#Vl2iH`R&7Tde zLJ#PGmip1rEx)b%br9&4#7qvO?ae^cdcjlQ$hDs1!|@5d9>O$k%&O=+|Gun~?k9@? zikL@FQ-tic^pU}pq3(CrX(wFpYEt@({HUbcxZ}KLJ%E}iCRU+$TNK%QCAmG~WwE6? zA`q1_6S2WeEVY*{-^k^!12wCYXnoLPhT#Y8#!qLh&QK{r_o!cxY%SZ>v%T&XYcaiZ zd}~$if*s4Bk~pt6gy74H3@wT=TG$hjdZHlEH}GF38_N1+eZ^Uy1wXw6MytvUI-?t` zu9PA|?sTw3L|Tn9fchnVri*4FtmVLAT)G8jSTgRoYf)>`6m(q@a5g=3Mai91N!rQY zypr~FpqTKGvSSX&=YyPUe*4(-$^}pwBqYbbJbTq8A7<{oIid*d%`009_W%^&E~yvK z&S$NmMXZG450)8}baI>Bf&AX+Usbx0vh^M!z&H-#Uv3&o>*~7|4SCOtvm=-FdrH^^ z%Q78c2kk#4dzuT<7It!)&o{f4-~RFn7Y;$i-){!K8Rk^>mk;(c93|nt=Ar*hx>HgS zN@&CfALm>!@WRIQPtaH;UJ`BDW;!g^^T56j3!51=RLcAEECjI_`;u?X=CTmfZ*Kz$ zoL2p|y@hq4&N;qwz`LMhCgG~>$=V;lJ1J$KO<3ka1Nlq!Y`|mu#*VxdaO9=Yb}m(X z@gPp|a(`01eqDqT?!#cs$SgZYPsSowj*^%?v=|up)~jT%u6+6?$SSz)>dXcKaU4%@ z3()1pAx6Gt>_{_SsU(v%DQ*v6=&q}!5x!Os$R@#~_rHZ#{ z*pY56M9z0j-&Z<)Oq_*6CD}|~iLFKNqOEPm#Clb;7U5!2o*W&)Ng! zM7}h;g}h^0qa;#vdl|K052zw;o|WcmUQ@rrjl6^15@h5aQ>Vt;sk&@8`Rj6rauS<) zk}I`_R1OXAK7XdalYp?Fi5Fx$pUwr^t!PX&me+0bzLyA=otsQdN@8oFePXXI?|ZIf zcCWdaLBaleVq*d-+78h3ve54x)!7An>^2j>F4SuU=vwY8LlqVbD-9GW#|G0seNWoC z1}q>8MD2bm|Jnqc1r8S0&>5KDpq`}%K0L!-5&r)(yZ_AkzUKYjA;~|8QVwLxnEA%pYRyPZI+f=tHopF_v=9%1 zUJsFW!fpWj5s#bgKWWAvMr*q69s+c3fyjhXS36lxoWi|tf<{o!_ol|4FN!41!fbB` zAT7s*pS%AHmL=Dy96{d>bwvK!$9TLEap-UF+MFc*H=b6fa6NWU!#2~n{#P>E|2<~1 zO;`U^5PG^gCkVi`Zv3tMS91}-Nc`$gv^MBxePvx>omP}Z} zzb17&fRdeDbup&cjwEkIxNhcw(brCO<0|7Hc`iOt+2iBrum@CCo;E!$>6ripJAiWO zrAr{j`49ZxHuj$+Y)yiiKyr?|Cr~9v9vn4KdfPAam*4KD&j%uBo^!-WD zZBjbwDY$LlWK+=lr*xV%dTLy9Y#7DxYqyV6T1on&g64wkUigb5M50tgVwSg`XB>Vn zzm?7^+Dc)j$KHA(o6*;Y*y9N56vmRUH6!mfq;IEZl_?`FOAcjVDFClYW0nVeBIZs@ zDtY~5^~jN!p87OngUC#Qy2kA8;F?<@)IU$vZZ*mIDJ`5kCFf4bum-POrBlALH~Giy z$i%u^_kcgX(1`uJ0IMdvt^5wEf+s%7hAsO3%d%z-=^ew`z3i76>4Qm*;ZHt1+>oYy zsH(|LZ%Bkr($s4nv%md1$F%9V*3v(8iYG~Pm^Y;f^(aY8Tw)b5!o1Lr?#2r$bcaEb z!k@$EjX6^-e+Vbin7nr!jU*n3EToUD3Pp^>Wbc>{74dk-IKzEBKDkmAKKYO|$vf## zTpJuILCk5geHQQ`#?n8b;^Nc$b=luhR@O##VxAjcxxmTqWannq`wZu*m)h5cs5OT^ zEGO}QwSfux1_mZ3H@6+W*5;9$xfJ9-q$JDULEK@x`RrDJ*DTE% z4SWOdnr9qZ0>UVjc2CP|P#(F;%t`?%-8#_^ls9>2gxt7OSo3aZ3CYHi-w3tunQL3< zzs@2^&?rr)>$?+9n?w3N?qV$KgF~aX{{*ofZen)kC?vXTBsnK@^ik!(oeiDYegIGV g56k`Uk0H$m!TDv>7o&|E4cNCDs=6u_%C=$u1EXFkD*ylh literal 15619 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Μ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 z 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 QsvCQm8550)4bI;9A6{w&K>z>% literal 25461 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 CzE|@A diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 05f93f611..cdbc266d3 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":4161,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":5408,"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 index 0ba8006fcc16dcac465eaec78cb6c39659061650..024f3dc8a4787619968d947089c99955e99540d0 100644 GIT binary patch literal 13584 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 literal 13369 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 diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index 9d730a988..bb0d53250 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":8564,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":7877,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":12301,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":12322,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":7898,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":12838,"get_count":12,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":11999,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":7575,"get_count":10,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":9236,"get_count":11,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":4812,"get_count":10,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6599,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6272,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6279,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":8858,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6736,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4680,"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 index 6e6f97b69868c05d170da605f53aea2c5821da8a..673b69c05e3a28bd747aca7167794ebd825e6229 100644 GIT binary patch literal 14451 zcmeIYcU05cw=WuclOiY}9TC{{qI3Z}B2A?QX~6>0dnZ6ZVbk4IkrLcU4-o00B{Y$) zAVCNnLJ2qx z^B4r8M1nvR0<;uB4W)*oEC@uV@le<3E|EwCYI1UNNdGV(P$&@tB7z76%ni`z&!C(3 zGenF4DD(ygnm(+S-PMgrujZ zS5{VHFqq!nUOXNTkXV5?133JBB}*}noJu}B2B8EI%4-F;W1s#TZET;ynFT^(0I@U!n?Cgx^ zgbEPzK!h+tGH7TbXX4Gn8^(?@p%*}*7cPX}06w9i5KJh97)s**1~CWtK!`bRL=1)q z$V0^R)n=~~*K=z6T8J%u7-SIfMh;P+xw)C2pMPp<%G%mmQ&SU-M#JIowzf7ze z@&X(rhkD@z$WP~X-^>#PVrnP-lBGy6af3htAniLhjr=lKCl~^XyICV_0(pAv;&RK6 zx{kGwN92FfE52fnS#MhW`RdAt8)`c;H!wIxV5LgOY?4#QVVz%CleW^E=#b(M>)K+lzK>lHw4b&pvn#Pw5d zx%16!U2oA3-kV_nfmaofx9Uz^04l4FL6kK{iCpqmA zE2ZY3>aw)WAL)wXk$%>mtWqD0c6qyOn>je4j`aG|v#!xSnWAUDuONrOR;$mrr9afO z+{H13;j(+678h)R+P*t(X%yXbcUnI4s=tD-|! ze;z!%Y{z=N=6MSLoB&bMuD4G8qMf|T7ZG%t;-B3nkFu5Ky`nQr=i2Lxa`PjnBf|;7 z;6nl4lC^{$<|#c*ed}b0`HOC19}jfyjE7&w-|Wsh;xju;U)x*hRW>~6f1W0rXOxDv z-~hij5K~I6Q>QkuaNzJ&>$`%q;0N0W#`E5qRvpx)creO(4L`SD+UT8k`#2f>Ky&NF z_RS?b@tKPhj`AbdkhVT_T6lr91hZWYOp?8mn7_jBw5C>cZ;q-xWOlqHp`4oONsj6U}W=ycJAn|GH5Xq5LA8*(7BP zd3^Gsb=jV=DAxBxB8fPWwi3JX@k%)>`N;Vy4>|Mgc~0zlDQ?czAYF*}+JnVQu~+e1 zNYxWr)8j!#ng%PGJ`{}!>gU47VCHdKq$ypee7!sYO?^;nxf7$iS2yhsKj>m66< z0|`7_lKDvXUoDByfd_l_y-I08sMmNXIVPZHTn;_Hx`U6?L~0d;)jI5o%*;`S4mbKh zA!gQ-TePe51z5(lptQ-UEpKv+gW$O}r}ivlhJ>r*NKv{{eTILh8c2c#4440q=ns{c zN#DEh>F)74VrO|W=CNYa&ez%zd_tE&sd2uOcE@o**8c3+j85<-ab4$xJ(Z!^@QR6{ zBCpv9B|G2A@f%aLl6F!2GjV6LJg46L>V{YJnB27dca*7%yj3`zHCXdzPT*XK%$+p0 zCJ%n;T3?2hF(ElR#0`kT#mC}#Lg5F_y;&ILY zCV{TiX3*KC(NnK^BQ-ACeUmf^*WyVI|V|;T=)-0M{Xz$vEIIhwsPl6u`ADBcC>qp9eLzK zbsv^b_zj-uKwjfZA_vdcb)_Hio39P148!9K9g>qC>-nL0Z=uhp{N1sEvWhGeG z&nSOkd2|xIdP(Wy^8^LwO{JbFD-{*ki+q~i2+P*Z;cG}QyP)Ix#^6h}U5}Z(#osnU z*>iin+VYc!@t)STL;NAMeO-KxWAm=5fIbt+9X;Bel$WenR|JtR_#o^x-?4}XFyk`6 zOZu){J|_`VKPXmWk6t~creV3IYmkhg7tYVjK7GjrwwHflP|v1wC_|mfUnZuHv)*H) zh-kBfLb5h0IOM{^2M+gEFDf&usQ(q-;m1EoZSqb`*oeN=Yh0WLyFQq|EF$B~WbX6~ zqTal4;94?p{YUNmIVO#0%o9C*17%B%3}bV!Tz$}!y^7Z_5;t)^-xHrQFxcIMl!_FH z2EirQYLSB_;Ro-2_T#T|0n0NO*EN5it9h1_y*~_?;-P_8H#I+^V$YP$j;TJ)lx~RK zaPqlFe>-mrBxv#*{g5f0VGW}lVM|<)QV401^o{M{=V7FdXZM&Mxz5g_%|wLd{c>*= zW(nxk#>_~CY}9~1_)QGv$!eqeqb9^d1EC+Wfv-gQc${uQN`0`|vTgqFd-&vk#BzfR zczK-e0F{H;v?rF#lka$HSUqYZ;`CLOzXP(xWwXS#plHb*6#{O_QZHVnMGP;|wQy!S z?svbqAbQt;_S3Wl1*|$dCmZY@Ij`3(=liJk{^b2H)6?O!h@JWE_IGMm%4H|7s=$Xa zQi+07noLhiMe;>WCItOoZ)A5JjMUwgpBF-6*@oCk2yd=Wo>PJ9-!vjjq)m>ueGo)+pZka!e1@g#4VS|X zj4;wo2wB7OjZFg3aW;o+kfQyF_=~Tc;DhY0TAP>$-=q>FIoJy@u;VWm8or6H>li>_)JKRx*AW4&L{AsA^T zTWArv-RHr;YNNXc;WduOs4|f2$da!VdQf7`SioWD!CEOoNMNC{#$r^9d0^h`l&KI( z>?1raqEY|}EU0*KEl3s>OqdhqZG>{AVUMb2hY5cl!;I~YG;iK|Q#}%cun1Vm3&!7c zstORMLkt5nv5fs#iLYb3hqbH=63kD9$V=4Eh~@x9?azqP07Uh}&xjTRM7hq0-UNsa zoe^as5v4vO`cQ>@ zYre(NB6evJ6nTi~BJT!#pXADd2;YLWaSBG&sfhbLc=bG(hKsysUau&}R)lZy-y42Z zq5Ut;hKK~?Q$n)*-oi1eh2y3aD^vSq-jvwC5RdUJCWn_@jGOeY_v%A>&zH(<+$sh7u9v^SN0P_aK*ReuT8K}=x zb5oQU*26upTDDV(Zk`96sm{T(OZl2%DKdYTLrbCfL7dDE1M|~dMhR_5>CQJc!SM); zym;2^+hsxoyM~uN6RaA|0~1D!hmB-&lJ|IL5zu!bP#T!G1waFgqKbiC9CA1(`6BCb zReY^D603;+bkHH%%fZ)vj}n{h!H`NW(d6Vk)#}mW8{MwT1@qQLB~hj>jYx5!0|)=k z!+7laFe4Ci=l;w+5AIABLe8fI)&%K4WkTfic|uY=x!ncGSKgkkq|6)W|LQEX#3r82FKsboGp^y zJP4jaUTkj|bGWy@e3F^S7J(rLBt5f&Fo4n$3dc77mTulR)kk6yX{S+ne5V46V^xWi z*av4AhT&-?AK_Dffjm_Rc_+pE*4fen$75KHUFgm*4FRSzLXoDU#KQ3HG+4B;XKB_d z1)fXV`x4BXCpSW;XU%oH?#R;f%nAl?WFs~I1hCDEX9p1cV2!Xelu-}|^dLg%=n!qG z_2*&Lx#O3~S0D#^%bm3X-E3Grk$hUj?O&R`jATl^t)1X%l2KC>R&Q`!0Lk=ACx2p8 zO|L0ZLg>T#5n4EJo6T)YhFuw!gEe`Q_3(D2X(&zJbk(av* z;eGrc(|FLXoZd^wR2I)UvmhYS{d~=Z;E>$Tdua=ZQ=$F6ZT~4J$F$S*JicQAMTcgu zQM%g@7)G@U=wAq^faKgh11QV!h&eN?P!CiTdD^7gm|^JB$^r&Nm(qgJi35z0JYGfS zI_9v}p{%+R`oW?yulbjoG>gZKna5-5mCvTaP6BSL18|gpId_3yX_t;^gmZV?7W}de zQ!oqQR@AzsgwBBThZdw7a9x&wOS^LhnHOg^J`aGxAD?>u%-#Od!o4$4{8HbgGf@1m z2h<26O-|1rXQUsVkTg(~$9MQo4b%Y|$RC$K)4&%%18+Tg&NOhl=REl7Zw{HHL{*We z>d;9gTx7SxlOJk;JJKb7+Oj1~GzA|hT2q=d0K$?4o|;VYKgr#%#(&h zN|OYP>Raui@idwqBh7rj_+*V)W>a>W1u9nD8j}cW#@M!=({%C%Jn=vJ*fXZ7k7>w%x~9(TtOF@M z6xy$qJ!u%lN}qV@sIT92`WCcY!L|?!%bS9SdXc#cX{Tp6?_FmBcLGp+1;}j)vGlb@ zj)-wTg{a^Vmto#c0P^mml*Xt`z7WPbk%NMTUJl_dCVyd6YXF3N80TgLw9+S@t*g3M z>BEQp6g{j$i%10^Du|z)zK*axltScW7+*ogONlBYPy2LFf@=~|94o%n0+FiN9Zsm{ z)_;H3H{qF> z5iBmZJ1?cUEHq5IRYvsid7<%v0=1Gr7zY*`fER{_SI;0QY0FEs=}k@$h34=X1HMf7 z-qjPGtI5Rja|ng4@=WK|!3>!lZc>TA!#dzc87&zR_RrY7|4F^z z5TGuG96T7qk(XpDj4j7Za+DnKo|}7Elr|HvE7P}IIqUl3Ep+%76AL;gp8zIK9`x%E z^eQJi%HcY@70iB2>|bbZNPoeEcvE(6oE@B(>Eb6g(RIzhp$6E8b+Vh}dRXIHE1;Q{ zyR?Tgb=^k>PSvxV*fAkt?nRD}FUx1qUk9F%o$=Q*aMvwJ`UhM91@)XvgTHr=;M(Lk zgJTK&Vma%n`N5NKpAK;SA`Ro%0=K*!9e7rD`o>k66$R;$=S9AAhJJe&z@Gw#8AaKg zuP;p=H+Btem`DW9pCC<$O|BW}Ri}y`XDGd?L(_K1RYNo!$cGQ6#DGAZqTW*RzG@?< z;YS$r?qF1kyiT>6w~ryDbRTfs3niGYw97zX$hT#@3y~U1hmEhqech zllK@07G6rML%wTE>Y`qD)bM*l={15B`p*7a!&(o6*eY*tILUPa!U0P`680h@Pl;y%p~$bhpdXBIfm^;lDLai99zO&$GHcP^I-JbI86P7^)!9GUPqD{2iSh4m^+wH zlopYtBa95b$_?{w@EyCmrd%(LY<2$5D9KiBc`NUkER^?UR?N~|%EvIWbz%C{i@p_drO2Y0!PM+2bQDV^}D%@15+yflW-n=CsE@fvqry&Zyb#w2cXnZrT8KCFG4kt>JF$*_K z+N_1aOy39{&&EL1WxM4;9`Y58hZYTXEaeLFULVt6oe_r4Yv*I1KuS*orxb}%tP4n* zk7s+MISV*FB8Y$Ar^9W9#tzT_?MTHgq*<2OO>(rAI_gU|owghD*ub9X4HV{&+am^KOCc44|vX;@i|>etbcNg8J{_csXnLH9`*h^kSt&e?WOV_fQ|+o-bZG~ zpO1Xy`eBRN0mog)s*Dz7RchoqGJE05*$zRPR0ehkKsx0`5+2BIS|eK0nMTn?oukCz zacm#jqo!Jixb!$7fQ9?SVVM&9Q-%dxAV-b$keFT*K-z`?n;k~##mBUWQELGpiE2Sz zAa~s508%H2oAhhUyAvuPVdBix5&XBVfQ$d#o&|{Wd;G7;vKOviEZ}`{>cuCt2-NR7 zV4{p)9e-D@U7B2QT$pG|om^lT+29~|3_&ekqD7R;jlIjUo6_{&h%<8M)zLdXK6!m$ z(!V?7p_Zn)wH2wd97Qp@ZL+PA|9G~{4M4%u};UY=O~=C1F#00@gx9>k%R3>iSv3TjJDtO?IjV@ zg+OC;3Z%RaeRQ-Yw@k~~4A1pf|AeyfoyYG=?|A!=ub4MhXz+S1wm*U>kgqt_U84a` zepDH#Y0eZ+cBWGhN!>g^#>e}k`^foA% z;hUqx`m%sAW0=j!AE$hC-KuwA@+M%&vDB%C4t1S%KEG2pD#VoVcEDjPMC*tr&W`5~ zSoLv!Ym!t^j!5~rfR3J5YH)URL-4U)qp+!qJFpAKeLrQj!e$=IC~v-#w8lc9t<6*c|q{lvHw$`58tl0k!h z0iR_bDHhEx3@dNQYKBvc1E}LNV2)y=WPhjyfqvZ|bj&PVWtGkjB)6c2?cAMP7IY5_ zC*0tL?eGHM=k7G83RphVAR!ME2LNMZ`^}O6r8teC9wja!f`^8bGdrgg{7Tgy_0)>9KPwFl%tUTHmpNa!6uD%iYNi~+JE2`{BA zq>Ibb=oT_opaf+6{)Xa(=}|Q2nBK%i-8JC3Z&vX5rT;}=hB+;w>V`5=$%OSyB<7HM zHEhjj#8tbaw0I;c^57DWfDp1d-;k0W$q3s~a9?xXk)E06UZMMZ69UzH7!gjuh)`mc zK9m+_#2KLUMH5t&l;k?5L6=}VRQJr_%=MnLe<0~#JB*W3)_=9-sgPJZ2Pg*6TxbiK z{QrwWwBSe5;TKO1u+)Gl=*d?o|F;X%=g+Kh{cHh-YHh!Q#L;MZseG02E%Nx=6gVDu zhusM8alHYbRNMJnd%$W5oYh+vXD+1#O4$u@L+@ES<&>>fu)t07=Ctgf&zXQ|{}q3y zd_1o^$_lmUVb#D2Hlg9a5HQA5GF^{o$UYGJ&7n|cH zAPpbgnm*npS|5&=^zB|dTHom&-IZ>VpOQ%nSPw4@(%3QlW96n~UvMA?c+z~;Iyjnl z(8p+$bTG3KC7ofk0R-2!;}>qh-L%rdgkjsjchLO+yi;JBRr*?*j{5#hBMtbWSn`fQ zs%;1dSifk|v=o_8S<4DGE5K5j97irLt}f#0N3(7Do2^uElX<2KNBGCg!7M2Q1;O+$ z)c?#Iog%OXI0JO27{+udBnZ^N*(lld0Q-_IIm8*9)k|l9h`4wIdXxkP6Ws?_^NPYz?FZYl9M7CmuyK zntygL8fw0-;A#DCkQ)q4XT^DzQFJS4uf8MfT zG9&D48EgM?HuPw`Gzypefs=Mx~ z1JEgl#yF>pM3dJAL)*T_+%C?zpBt$S4{OEt0%TS!V{1f~Y?&2HGYou3ovyJ??)wOR z?a*X9I=viUZAA>lDbPGgZ?fMkKL?OC+B|X2QmUUCs~MilntWZ~_l#H6C#{ES@b#5d zY9(vMsSg&X-1bVWD2w{{gQp(lA(+){&1;*xr;HgXM^;LbqsyuIOh?nw@z?c_P-%f? zG2&?-%A5^dXc3CTy>1E~Z7Jryo@_xK$GjE6CFsAKEW7p%>`FZF*qYMf9pCpZMg+VS zwxRgaMZ|+W{7Zn2H!gW)^N&}y3DwV|?F|VV%yrJ5Ij;`_-<+E8twjho>~jniTcE0) za~;|&3jv*=(sue&hFV_P;`!N^R6!v20n%H5#76UPg_%-&4vt2pPDrt#{P8({H{8Mv z1|ZpXKz*mHeCd7dm4(mM)P0Y z4gYV|m$m;$Un`OhVS2K>E-U9IKGo0eBnxG?qUdonC6_3KsNzN0 zgbJYZgL?$ni`m+zH~d{6@V;mg+oDuEn23}CL7GM|^fM;pIt2;9k>kCx<5Zs^O!pM3 z;F0y{r!}1YJu@KS^F8Z{f5)W113aKeVYwuf-}`mv$h%EfX)c*SfaTrJY{Qr4@_7ABk(4P9R|-enx+;q0NPMcyY--w5*YkBui@KAKhNPX_=M&m$!ycfn^5}?b zuFPtfKb$!Yv`6=QJz?IdBE-tTk|=r>FIyeB&k7?aU2Ldc^vO14AzV){pGpSXxPYXu zP~VBl)L#FdXdK)deq70OYi`#lv*=Fm{NCfugcKEVd-1t89fO8 z_KqSkksHk&lHL4A$7hM@Cr|g%V(Ucj(!WYp+q10LzB;uwlJ1_rnbjq2^8iq-88hP8 z0W$f~f(cRgMVjFK&K`nd1>1X)-b>sXH2cSUiHmdXY2||hK_T6+|MXtsz_>LL`V4~S zUs)9TA+n}8*42%d(cKHmrwrAU6uCL-^ZUg_0`6{p`vW>j*Tf^;Id<&xGJ8lLj~Bm+ zy8dp)OB+Gt=D?QKG;-X=vj^O9kKOIOe?#YWC!4sb=$1JVJLY+nPeUq^=~nSJ>Qp9l z@G3F|{n+t+^V^SircgAg%L5vEnr=Q7rye9F^`&V< z-#UJu)thW3TvZOcs8naPcrqT9J5bJ=x~%N$b~2k^pzuL_l2JxCzDLh`AN-do9gKnQ z3F@s>fX6w+E$l>Kk)NGoI^7owp`+g?~zO_xon7N#_e&)rk z@@oDx^XBR6lAv7H#~^|1^oIOPAWHfZVa5Lv^lsNm>d-u2$eXs^t<a&tpM+3*cU6$JAR`&GOTfx2@B$kcxQiz}IZ&Oy7OiQZ@~sGR>z&fQ^yLUWG4pOl*zodzW|-o9f@I&u z9z(SCO+na23eiSJggJd-rm;V_IF(8Khqngz@=8l=MqKwxHIV(42KzfOkpA=v^fA*` z0klC0mLg)f3-;|Hc3?JD3I-KT$4~3g2o)Z}4~mixqnCo90-1K7q77Ndm(jxy0cP*B?|t*%r8Q7u!6;>x81%YX zEmcIust!-m=1J317jbA>n+b1eP z0|i2_?)$)%p(T@oC}O-A%^>hbdc7Zgw=v5ajl_Qodt6#sKLNwTyQiYFtAPU zuQgbTkgfs6imB52(ZPX_6%?>CX&yw~?M-Bg(d1Rp4r~Ud7;jw=5lu zblL1+AZI8H{E>7q>vf;*si?sAfC7x_82b#4SeA5JNa^D5NiC_+Z_=nH4g4H_KG>27!35^h5t`F zaB+q2Z+7iZIC{7PHm|KUIGj=nSc}d~jUpaEc2fnBq4iMc)6$A`th32tP@{ZpPV!ho zlr~_Gqr)p;d~^?iwVvQJw2MV$dDBYf0Mq?k9U6+^2HOkuTpO#UUZ;ju7@jN9pQ4qV zrVTu52NzVbOL*QZ+fpT5r1qhM4d9w|3Z2sw%re+EA1qgya{6FNG$#V(f8U-dGsgiI z-`$Oxgpv=AQk5p7Mw2~T+M4(sGnEi%lCC~=c&NFDv;CLojnR34|} zmur}1sn*IX8gNtw}xDqj}j%xk>ywnYX5K5f}t1DCbOa{1C zUxR>8)n%CtHVt_GgqvT=`1hXvObC?K6MVhWIxd)|ZB+C?D`jKjSa+1gqw0H6G;PrF89P*ojP=OP~QRo{BM8H`4S&-{el zszLUnL3~{^=3pFi{o)tHfW17gf}NNT*kyUXz4aKLHdP|I-xatuX2qVdyy;c#`0gIu zPknS1c;jk78o0>e=IAk7xREx|7>#)BvXVz;@s8rSpP=KZ2v<5-^eaih2ZJ$WK2uir zyjy4jm)jqt=j~>z>HrFBSi?S4?5wdkI@){NFIKQO(enl{vd`3Il-DvrDNaS^>{@+-g8A79v1v<5`ki5hPEb{E(`sD%j^`%JOKlDOXKIw@|8 z$zIlnkVTI9R?PV={rKCLM;IA;TY(-?;$oAx*Vxa04Mr6-S*GDWckuU7S+1It(#OgR zBo2QhB%SC+hlXBGk}=cq`%32e{G{QF-z zl1@nBv@JJ4-rG?ufEfQ>_%C$OB^PF*x;>;Xyz5R)OmiC1YCLBT~0|qj}{)<3SGdN4WrO2Y8b9 zdmM0C-XoFc^v;h_667rrK-b5))}+mLy$d6~J@^-pK`Soh8N?733;T~;%cZ9^4U^Q}29p@d?(`(gN*66^ThSm(@NQE1qTnm;Ojm&2nf6d-f5_815X@e4$$j*WQN5FEP9MqDQd6ges2S#2)9P>oHU#{vE$T%gFDpePO70dJgWj z649cFhtm%!BHyvE&RXAZ2-EMFd}%6^avwH~$nn7!T9$;}TZY_f3)7wybTIiMZp>t( zP;9C=nrv^pzF|=B-3H;_fDJ1!zBN~kb-)elL_T;&ZNk21H%67}3^06q5CPFxD&rO@ z*3;@xfbA%0PYL(R>{VIzLKJ$h_nXx(!p8TyE%5}Rzfd^MQ!!O*QhCh}= zodRYMf1N5!-@Dtk&TN(u7VJk>G)({QgHDE7DW7}15zd8CC#vfFcbA+a4nEuko?7JC z$=44z^;f<9zLPW5tLn zXJ{Ehe9>QtJIU)@$;?}DmKD8=7h!b;eoZt!*sM*0(PFMdF=(adM(hbDPeld!)K|sfa6n5=P7eMTl7Ju-ha|uetgIlF2)=$L zPN6F*7rZiHT`xY1!G?g@lBp zrKMF=R3MSa@87@A&dvfutci*ytRxf<&8-=%{lwu`Fwb%D=F*iF+#mrk8o^`WXUO&F z>w?inr_a$$Ozwav1O#1?-B<#=h&Y1oKHTNkxR8fyp`kccf`d?xg%tm_xtPXkrUh}7CT1Nu|A-!pzjKtTN&|4o?8N6kV&aE;*pU1fdWx62qR|3VqY2rDt_ zxr+8ziruLF_@4=DoFA_|S2mofMQm18n&&^L8>f2dTTck^`j4N4wnfGXJ*}fL$Gp+D z7ISP;KRU9_m8aYr^zcrx4`%tT;z~DhP0BoSV`LkGDnyHS^f1^->7fq=D_f*8cV3BL zA_a5m@`7_pzU+YpMuuG&EeVizJrESUS^?&{_XOiSX(8r(w>ClERUxdYpij7R`N6VW zEdCkQQlOqn&SuC*inc13OvYWP4?DiKi*!tuu!+jVjX&%^ zh8}i|5tyl`(ge+Xf8M@hJX;|BQZbrI%tgmb_2#*eC2y-2A18SKsB+OPaNTq?c@tcu zyLo4ATG>bOWJg!pP?0s29l;XJ+Vo4Evous=AsrF_GT>pD9C_`t2~JAI&DP+|v_B43 zzk5^Mc_6Xg=>kDf^UUURxOO5JgPjJJz0-}swH&aP9ah;eq*vJMb4levy<6<gGJ=Rm)6i znM`!Az%aVnaC_cr|8O8|bAY);c$}(7^TY?&*nMS!>RSb1gG!THLDQ|JE9kxAQICp( zQgX7`7Qb_Px^gr{ebI$08d7Xa{=p92^00$c48LVMT}X{@ihJR@$>G9ogwT{mfHV!I z(uWLBDgtt_MPoh_0f}Q|>d?Q93nXO8-C@MhiP+AEN4)$&@F?o&EmGcvK0+>7Hgn|_2!EknAMUAlHSa_QYE%!)4j-qtac&L&jC^qp8^Nb?nI(T!LE@#oAU=)!vU9|IG8N4+q`udzl*a zP}*aZ-~JRjp$Qlxj`s1ELk4a8yHN0Not%6v%FK^*^u;a_f0qQC+QL^Z@e(vUsvDoY zQdB|sPv<+OYMIj1rOKZZ@)d9PS?=vqz_iq@-H4gDdSw*(vZ#6@^t_uF+eGJBtr2Ck zX>ucthdN#0U>xi6v<-+cD@*}qvav{zKRWFcSZ!x6_wW<7K2v>6v7#XUE4i-*>yT)_7$>{`?6~%zDnmh`_un<`>myPb$Ocf{)FI?# ziE;~D52E_Wp<-%>p=sIqGHjl!bWXIO>J!hP2~A#j)0c%8!fnckH(Fnl`sICPXp=4y*gMG1;_h3cC5rO}_E{O96iUu+#264&!k*t8ja=RuQP_^b`}- z`V};6`LXqlSj(puDCs$1aM~XfvE{{X#pS#96qc->tJz!^Of{dr{&ss{nH>J5U8~sp zgx~5beBpag7FXC460~HZOtb9qTL0UV@h&0_s(CBS7xR0iULkCTdv=GOZ0IeME8VTn z@h@(Bt8CbY`?KkKtMoQ8_~9a;=Y&}yD4^l$Rn)xj=zx%D9q2T%WX({mZCR{F$>t@bfh^Xq=qHu(;w z@y<&hut~GSb%_*Etr-$@3f~PkVRNzDSt$21jT`nSeRQDuj*W{T)hM9f=-xQE&d#G@ zxJ~etCO9aUp9W%(^aeTN89uXM{k3Z?U+lQ*u%&*#3RC^IwsyTdL{G(h;nFt}wA3k! z`xH- zk_5TS+Y1b(Nphqq^;)Ud%*T1L8=~w8;?;lw4v8X@iLkbEw+|2gCJYq$z>H&DPJoEd;+gx) z@%ixCVmOud-vj}y;t~E>EWF3}0^gU!U?$L|cq_~tguWzp1enXL4jNz$D0nl)Z%r@J zfig-GoFl6>GqboZP-fBxQ{aq&1{2F9DNmqU&X{WjWkUD%VtH z7e4csjy-Wqb}UvQl0O}gMQmTvRrg!{-Hj`tY{dv#KX!Jd!C z_{LF(H#7&y>+JWu;f)xkeG_(Ymp7m{^Grb}`{6$(sGzoY?++uHiHZz1E37jEs$ey! z{IcKjdZ!fcVUQ-QRhK-fA{G)%snM z2kO6fkQmaF^WV1hlfsDuWFm$V9!ANaTny{5i6a2*Ops--Axp1iRe%S6wua zstRX74^koxZR4zR2$x@DpuGSc$lw}m29KJZ9t$e)9*I zlsW_R$te>yA}7mXK+*FgDR1%8H z-u;vNCB2j=Lnf)P~u=`Dq0k>ZQwGBFgo7b&u>-7S_pin1zF?6@vy^?7BnHbbbcpWYb?nT z6&*es3y=97)_{5FA=@s|$c;(NVtxFHpOs&(04E)2;#Oxx5SJKJ#zoKsoE#dgQ8-%Y zZvh&A()smwl%KIx1Eb9Gsu{umKpwz@>cet<&Ztj)q6!XTA;Vw=&pfpKgCd?=(%bB) z8WX%`>RwF_&7dwq;S({73)AgYx5z%^50 zVT_>V^zl~hOjPb)J+s|Qp~EeCuCDf7Ot6hX+WNC^sdIqPx6b^wJx0-Ko)U_bxeDRR zJ98K+1^Ecgqk`2ZNdck@4+8@rS(ss2RIM{#A`>5-`PfkdtV+eL^!Cq|oyW(TpOcY` zI!`{|jyn&1{1<<7x=yE0}0Krgg2;jzspTnR2hCU-*c5l%l@D`N|9)cM9n|ejW6xov7qBJoL|o z<1>=307 zLcZsWK(>63aOhZ)CUP<|zWH~;gfnB|3Ca06Y}3f0LAF?ODDOp(N{1*d05dcIGl?e5 zbcR&t@P5f{JWl>>7jFdGt2xIGo5KFN-6oLkI(A$Tfa~A+STYcIeiry>*k}qG=am=6 z&nl;$@&W>*4Y@ec*UE>pv+^t2LLL(F=?dPK7Jt`cm<3Qp%We18+-F@PzEb}98TIS6OH@SIjQr|=%bWeI ze{ICiItk@+X^4z(_c&pN59MH@0W%r4I17Hji=N*XUtbX+VVLV5NE!xmddZo#z}SFw zMbE4_e(LgXXSfFB#805Pr-%lI#B-1NC!)VO6Uc%8<$HM7{cGlW$26cZlbf(YcQbIK zK#%%)bAL5TEH3BVtkZ zNynzfDGyV40vf+|QWH@I+$i9wm?npspW&|5tshvr_C3j)4xnN1&k!QQve>4@nFMWI zk3b$gjdg4k*jFq36KEC_AJoB_*PE;wE2=Z7DbbB4pdSV2Zory!Ph)-O;HEqg_QFk9 zWeG>n6H5X9i=gXgHlM4O7KBA)UYVXqo%GIl^lfhqzAaUHwAnIsjwI#fnZ5S~;wYh3 zH*%w6K^bqvi-#XE`%n~%9;fK1TJ}(O9{dRXQw)F`FAQoR*i~vA1c; zTCGv@NhicORh+}+`<%hZ6Y4rd@g<{Er6dt;Pa$4@<{&THN&(nN!h^br8`$!3K=qhR z<^(*3^AFh97M%7cY^-XLqUeQ>Lobt`#?sLKa2@_pITBDr#U0m#XZGoYEbzcYK^XQH zuBGq%xx+WlbBHf6(6~#(kdHB2XD079)?lhb6om`zvFOBQou|w~yAS6s)7gAio_|M+ zx8zxu`ni;GPojPytYmCb?Jy_06$07LH`}n>9xkqg;3LP`*b4+U_px;E4Wpyi3=9W9 z#oPD{Q?@882+GlC*#N&sBrNs~*~`WsL6ZLxg|?_e zp#0O8!>nk3#p1y)&+)F{dvun|KKHumK$p&NlA^jwT;y`dmmQNW7Q^UNvk{TDOO_d< z2EPA04Kd_-QxxQk)P4dBv4A4Yo7aT22PWzhLxPNP6L4gD#{|4TML9afCGbW;lI=*7 zg@qu3X}P*{MP&goYZ<(btXm;HimQJ{xxDLnh3e}|o1{`=-Y&HX0>dbZYORk5sfLhCpn8byr8xt`A^dsHi51Rl#Lqr4L2$zHHD<~ z`j#@U7WCYvmUAn=H123iQ^$rrq&V4~E7JE%%S?Xs+XR9Y7I7w>IE%{)Bq`YVyTCfa zzxRG_I=b2@L^Zy8@ui+U1wVyt8t|I-1t`sh)NL29GFraJ%mr@+Lj>+5zX6}dCD6Ti z0c!PZDWRz7M{{Kf;4H%z0w-Y{5UzLp1GZ?5Oi1403#mvmM9F!K>y|q~QQ(tP6q(yK z{$K*dx;CtLd08IG1^fZqdmV0-KHhbf`?eK-W>Z7gDud@=@}=W))M&`&s}Ws;x{xXp z!Tr*@=g35SPLoezWdKP{k5Xp8wAJ*D>vfSWZeDbC5|?|IzNuE|mcKVKD&%9Bt-G`5 zav#8cF{APDQ&;eDfPpXO&WtTnaSN$>)j-C0wn)~Il^4!?d;Qw+X3Kkpz@eJzZTJQ% z9@y5MO~3KQSj@rq_r{$!m=~VW0Rh-u_y-VWWpK`XD%6@`UFis6EN{I)D;+L)J?m ztJQp;K6{Eg$SvKf<>z2{2)!PH3*}-UFT@`i&rSi=>pWdtu<=n-Ko18@iUq~2ZAFRs zfNDf}Ceq)MtMDv0mQ5uf>vjiKOQq8pL2BWltN3};YIUMx=+Ai>U_lKna3PQlbh3IS= zV50|tUjz+BIn&W?ORc^KQE=m?!@be36)d&)4Jb$C9!1`rT;b1;o zRpgY}jh6vI_ylVXKnEx-4XT3g=d_4HNEi}Sz!be?19RpreY>l-+(n5rLQnjZsOyM% zNKy#am%>+GFDppzI$Fg!w0Hqt`*`x$Tbq^i5;=4;jRWTEK%JljHUQKG`cQzn<|Kw* zfF&3q23Jo9!FPpsU5eKJNHh>e$q9386`yP&hmHVZ3KJDcYSIbDAPcGf>#hJskm0sb zN`~dk=eDBF5w|gkhP&X&7Bq3E(;yjoO4Oq}LW);%qAtK!<+QoT#VrUdFdpJt^Hxav z4$w5t*7^seDv*za2owP|h1)pX(;Gi;vmCPYQ7xy255tk;{ulpe^yQdSjNUw8D2&wi#; zYsk)2*k}-LuWL`n&|%-v#q1WYZ-&gXpj=#ydz5$If0;Kc5Ez~hCncgZf-{4TqupD& zCwy;ku{!Opv{}(F+3PzQ486>ZqgN2(cU1yUb{G;mK%1T}DGZ0a0A1}!KVaGx!Sp)KG(CWrx3K82AQ*dW58CHX&g zIp|sUQ5b;J-5p2_%>i;|hyf!I2f#iz+@U3p7UTtR{=e-)*O~=R?60MKYUpB2xC_qg zG@cM?c*P4Ng?N>mXz90;;&<(UQsP8dGaGPP4G#*aNKpc~0&2Ud<;SL|0Ms&rH#;yX zm!HJQQruGpXKK`=r3#tgAL@z=f$)$Y2yHm=A-I6#3o7S3nefN{+s{j<(id);o#^*HFfX5LpHK356rCu?)Ebmr2 zdX7}$!GyLyr$KWrMU&GY6F?Qf+J%p?PPp=IHDgRrzm`JLqqSN6`i<#ikNdWK@i!{?%Ph_pH@TI}iegniFCH$%Zvo)}h?N%>a#01mjl1D# z2_&+`Lx%%wfyWU*{CKvVZ#J>RknO&mv328Rlb{K+PFr;JOH^uY3VZObBe>C(C24BO zMIG#gyMElIs2F(Lcz^vt?T7MY^+e+okWjecgw&u%+V*wpZ5Mu1V<8eWkBN(`Uws(C z^8!2T)HPpk(0&*C#R@STM&f)O7Vb62fEJ^!WA7o+8v5RkX`1W;Dlt7BY+-)#zdqSY zu!h}_x$fKe5)_o?v_I*C5Pge=jZ5C*T)N~c|5W^-K30zv7qGWcwzU3lf+L znc*|b_DNT@=MJwdg_XFaU1b5nw7c#Sbm)GAZ7H{t0X9R}BB~GS8f5AS`#E|3N47iK zNAq1@3_Kr|$L3IDuV7XW|9-=FI$BF*;5cY#w-htAgG~AJrYR8jwQD@a;teWRC2>$bl@|zc;xot02>hx1a z+`(papj1wuRAB`&{k4`A^V(h;B;`1D=Tb3|&6!)gU za_rdHtoKp<|m2d2bSpX^v|miAUs_sB3@aT#gh-P)au3^1~;j65~D zaaqMo__)X+0RaOg{w_d=g^pqSCJ$xo+*DL9{O1dEz@WrxUNn_MOy%a1qwfp87M_Hy zioU)WXO)~zQq)R^;OedpI;o%itlRgXk@_8gxS&3x@GGVVdADhda!x{hR4p$)+Q_>- zZKwOLucJx4Amfq!gP5JE)f6D3{7*i}WYF`C2?f5j ztxY3A@UZ0>*l+m&>1g1@JZn!$R}j=5zKS94^qg2E>IfK!&xJ|_X*tT3M6l|q@}Gm_X_Byt6tS&le^Q8hu8zt+G>adm)Q_xYkGAV@eAH?IUpM8 zC2cSgWop*hL+_9Z5 z)Wt@|E5m4@<#@2py-4ush!ljL1~ux-DHT+G=(qQtJ*I)EBoBL{z`1gtmWX4fJ1CX^ z=sV4!mmTjSLTr@l0uSo5&;W!!n<_>&1q(qP_vepd#W#^2LE!MN+ur05N(y_bTegQe z3yN_Qf0!DXzuh}2S`$-z>jBPxuh3A3OgHEv#b!|n(TZ*U_ZLC=zIaFF-i_*!RnbUEiXWw@nrb{ zxGlTA5xKq_mtp#aSF$`b`sJv+U0Jw9P!T1Ylx)B?L(PI`ajH7)FyNGlCL2a5A)qKl!;AB0mU&_Ur1Vg?-+vu#Tt;`57Q?&<{v~kSgbX588ec23i84WP0u5v@$ zNBwV8A@pQyJ)cbEG=6<4plCz~A2c2_zKtXW_eWkPcxnINxkFHG+_pU%dg)hQtKTx{ zn-bwxKV#)`Z1RQ6N;z_h^)MzOn{Zs@&&>rQg#lBC8b ziJO-4?$p983(a@=GO?@A9o@XgtX!t6J+&hw4w!M=1)JL#|4ebz@u!z8@r1LnM$MtMZ{xBnGsp~& zy8m(#-_(S&+X_}Y`QFcpP|Vqry>$~dc8DxDF&5JYF~W(1s#gmn%8)}(HU1Fb{~4J%&aEALmN3DJ!z1qP#MH;@qiXa&pY;e;_cuy-)@I#lN#6aVBS?c=o z0bwXD68DI@gU>PZ;U%Vz_2pSW$v{h7;lT$3!NxXmS!b>-M=Bu$30ma+UvwOA$gjmJ z&_0hM=>La@?v>X9M)$foahJXcWy;ekQJpo6{|xSWEcoieQAx$&=Y_q6 zjT^)W?2^LMI^32d>13y;zCzYria(f+W}6-OvbW zjA!pu|8tn>{zdx4;TED^*?OI~Y^jE&3C{UzOz~E2`u(NE(KaCDstnN4BvfL~F-+~2 zCL^zLg<(k)Hn!${IY98$_lhtjuI?4Uot;ib_p-VX%!(|7LDa_Lq=pT@HQsp^)BSM-D#r(HopPCUQ=a;JKsE^>!rq~BZ+oQcD|)_t4Nk= z9u2wG@?w8%7IMKtD{Zr#7x*7zaiqenjwoQa?{8KLu!AqX766DlQOpwYgsNadhgBjz z|3&`Crmi*JO%l0kmW}pGS1oqi0fz9|cg-Jz&O_bCIdUGYQrD^LWQGUIy!}2G!EHHx zP`Px8XrE2T>7Eb6hE_qfm+X)lu-*3or&iIa56V)?#t2;_S0`!AXiNvI0K+&5bs4Yn zvpz=>{bRWNL+t$jhku$({*ytL2Nc$ixu+GIxEa*}BWLe?a}UT>Y~)0VVsJrD3Rfcl zb1Pu!oRVWJ1)upWPAAt^J@}*}B|^b}ZMj0h;r9{JocF*Ex`w8m13-<=;mm+Su|+k6 zUb0;CU62!jTQ4{JVoPD46cOcKgwEk-P(kh`pM15F^}10$=V30AxZQQZcfhgF%((tn zzPwfotHViYJZ#sqzT3vbbz($FyYFzGGQJxUNc3pgq!>8b(HSI$kbVdn;U2^=vWj=W z&B3>SlR0Epj+h@_NpU^YW7&$Fz6eqpX$Ni$;DT5<6!BvQDc}? zgaFjg4ROE7WI#>!l@$;^`SZrrYM#)rwg$jRv?wUDRRPg>>?Z+nKN zF78tL-q;9+jH!DBT!1n*R>n3ugJ&(9VYpo+zu|_Z6m3X#u&alH{e;q((9c(24@o`P zzuABr7xT1A0tgXDd{>&xzJ(NK$0HC8|G@4MFYHK_rg^FM`DBrK3qlZfa&mIKEg90? z1gWt^a=zN7{H&1ZTPcmwpg1h z3vH4;YK5qN=G{grup`8|4;#K^PZt}eV66x(*Vbok^#=~n7BXcjcAs=~GVwD^d?~uu zEpE19XLt9_7oGye`V19r=cG5A1Pe0^9%E##js3n2|e*>lTk zN0tcsx9I}FFI0hep#-37S%d2@Zo)#RgMM(k%2;ky4E7mxb_Ggb#;g5<>Weulz$W zW+_@7Z|Kvksz8UMcoLbc1tyf0-})BOt)lQ}wj4OkmXQj>y~bY$ag^W)RG~SRWgBLJ z2j@E-Nl%6i0VAg$a!^0~oWla@v$5xhrs=5=lroE470UkpFn4uaKIeSTaamPGLR;9p zacbnb+#L~ElYeQb4Ce5$lf4z$Rc*Uxt*=X;2AOzMK%8|7B=DkZo@UMb(S+F?Sy=mG zRBlmbxcnE!i(#D4$Ga@Fgz@l2rOrXU8L08o+W&?X&;No&+drw$#;tOM-+hvjT@E3m zfIb7EzK{AZDciXTbJ zk*sesVcSHr5%DFEC2(sI@#juf<=;Itb@JsogM6v^2^?7bDcY{AHd0VRAOGas>X#}@ zgoysi%k{!Ze*AJ+v3l;IJg|#4LrA%HH?b=%|Kj>-P*1myFQ^tfRP) zNcAFcom~njQ%abN!GGioNZXzhU$^|y>paU=rEAqt9`9>%OBl?0y&?F+%v}}(SL7a} zo`?2SXm<+J+P+TRjQ?HuUcumTk*;HdbiFN^i{~(5Cg1HWr@BKUSELARhWbiauzfRGa!dr6Z4L;d+()OaX3jGmU|O5V5yP6rn=Yh z8DG7|m3@!h-p}ql(m!3C$5n{aMUFc#&91Mz7+4d`Rqlted$MOK3VJI^2L3vkc@S$K z`8qMZyr@jTho-=r&zI(~;Olv+U+p2UJ}c<8D*NcR5Du?>Hs)x3h73RF(jM})fgl&iRwFmt1W5Sg2r@v}32jV#2w+bK->opSg6BmsLhm6pVm>Y4KyscNV^OH{zm{}0pX^cesE diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index a43700cf0..3b75a8bc2 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2311,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":2,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":3,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":4,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":5,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":6,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":7,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":8,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":9,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":11,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":12,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":13,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":14,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":15,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":16,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":17,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":18,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":19,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":20,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":21,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":22,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":23,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":24,"series":1,"stats":{"get_bytes":2315,"get_count":5,"put_bytes":307,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":34,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":35,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":36,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":37,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":38,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":39,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":40,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":49,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":50,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":51,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":52,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":53,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":54,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":55,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":56,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":65,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":66,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":67,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":68,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":69,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":70,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":71,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":72,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":81,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":82,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":83,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":84,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":85,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":86,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":87,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":88,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2316,"get_count":5,"put_bytes":308,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2615,"get_count":3,"put_bytes":751,"put_count":4}} -{"i":1,"series":2,"stats":{"get_bytes":3143,"get_count":5,"put_bytes":1135,"put_count":4}} -{"i":2,"series":2,"stats":{"get_bytes":3399,"get_count":5,"put_bytes":1391,"put_count":4}} -{"i":3,"series":2,"stats":{"get_bytes":3720,"get_count":5,"put_bytes":1712,"put_count":4}} -{"i":4,"series":2,"stats":{"get_bytes":3848,"get_count":5,"put_bytes":1840,"put_count":4}} -{"i":5,"series":2,"stats":{"get_bytes":4104,"get_count":5,"put_bytes":2096,"put_count":4}} -{"i":6,"series":2,"stats":{"get_bytes":4232,"get_count":5,"put_bytes":2224,"put_count":4}} -{"i":7,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} -{"i":8,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":4296,"get_count":5,"put_bytes":2288,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":4360,"get_count":5,"put_bytes":2352,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":2483,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":25,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":26,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":27,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":28,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":29,"series":2,"stats":{"get_bytes":4555,"get_count":5,"put_bytes":2547,"put_count":4}} -{"i":30,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":31,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":32,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":41,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":42,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":43,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":44,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":45,"series":2,"stats":{"get_bytes":4619,"get_count":5,"put_bytes":2611,"put_count":4}} -{"i":46,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":47,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":48,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":4683,"get_count":5,"put_bytes":2675,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":4747,"get_count":5,"put_bytes":2739,"put_count":4}} -{"i":57,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} -{"i":58,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} -{"i":59,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} -{"i":60,"series":2,"stats":{"get_bytes":4811,"get_count":5,"put_bytes":2803,"put_count":4}} -{"i":61,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":62,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":63,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":64,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":4875,"get_count":5,"put_bytes":2867,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":4939,"get_count":5,"put_bytes":2931,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":73,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":74,"series":2,"stats":{"get_bytes":5003,"get_count":5,"put_bytes":2995,"put_count":4}} -{"i":75,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} -{"i":76,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} -{"i":77,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} -{"i":78,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} -{"i":79,"series":2,"stats":{"get_bytes":5067,"get_count":5,"put_bytes":3059,"put_count":4}} -{"i":80,"series":2,"stats":{"get_bytes":5107,"get_count":5,"put_bytes":3099,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":5107,"get_count":5,"put_bytes":3099,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":5171,"get_count":5,"put_bytes":3163,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} -{"i":89,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} -{"i":90,"series":2,"stats":{"get_bytes":5235,"get_count":5,"put_bytes":3227,"put_count":4}} -{"i":91,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} -{"i":92,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} -{"i":93,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} -{"i":94,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} -{"i":95,"series":2,"stats":{"get_bytes":5299,"get_count":5,"put_bytes":3291,"put_count":4}} -{"i":96,"series":2,"stats":{"get_bytes":5339,"get_count":5,"put_bytes":3331,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":5339,"get_count":5,"put_bytes":3331,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":5403,"get_count":5,"put_bytes":3395,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":5403,"get_count":5,"put_bytes":3395,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2240,"get_count":3,"put_bytes":342,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":2,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":3,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":4,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":5,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":6,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":8,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":30,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":31,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":32,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":45,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":57,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":58,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":61,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":62,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":63,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":74,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":2477,"get_count":3,"put_bytes":477,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2825,"get_count":3,"put_bytes":817,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1119,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3404,"get_count":3,"put_bytes":1396,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1522,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3717,"get_count":3,"put_bytes":1709,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3865,"get_count":4,"put_bytes":1857,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":3975,"get_count":4,"put_bytes":1967,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4067,"get_count":4,"put_bytes":2059,"put_count":3}} +{"i":9,"series":2,"stats":{"get_bytes":4113,"get_count":4,"put_bytes":2105,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4159,"get_count":4,"put_bytes":2151,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2214,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2214,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":28,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":38,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":43,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":52,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":55,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":56,"series":2,"stats":{"get_bytes":4494,"get_count":4,"put_bytes":2486,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} +{"i":58,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} +{"i":60,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":63,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":64,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":65,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":67,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4617,"get_count":4,"put_bytes":2609,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4704,"get_count":4,"put_bytes":2696,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4768,"get_count":4,"put_bytes":2760,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4768,"get_count":4,"put_bytes":2760,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4876,"get_count":5,"put_bytes":2868,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":4876,"get_count":5,"put_bytes":2868,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":5039,"get_count":5,"put_bytes":3031,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":5039,"get_count":5,"put_bytes":3031,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":5102,"get_count":5,"put_bytes":3094,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":5125,"get_count":5,"put_bytes":3117,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_after_fill.png b/actors/evm/tests/measurements/inc_after_fill.png index b85b41766c5f0d6f6ca340a3f4669fa199e8b51f..0bee338e4c6110eb5bcb48966decaf8907df11fe 100644 GIT binary patch literal 14440 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 DP_FDw diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index a45975d3a..4c72076a1 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2335,"put_count":7}} -{"i":0,"series":2,"stats":{"get_bytes":2244,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":1,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":1,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":2,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":2,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":3,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":3,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":4,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":4,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":5,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":5,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":6,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":6,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":7,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":7,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":8,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":8,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":9,"series":1,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2256,"get_count":5,"put_bytes":248,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2199,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2108,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"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 index 6d395dc339c31c6fe8b1bee144bcbbfe9b483ee1..1499bd0684ac15f085800e924b8809300b8729b0 100644 GIT binary patch literal 14093 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 AMF0Q* literal 14022 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 diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 1a0aef16a..5c27c992e 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ {"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":272,"put_count":4}} -{"i":2,"series":1,"stats":{"get_bytes":2166,"get_count":3,"put_bytes":339,"put_count":4}} -{"i":3,"series":1,"stats":{"get_bytes":2231,"get_count":3,"put_bytes":401,"put_count":4}} -{"i":4,"series":1,"stats":{"get_bytes":2295,"get_count":3,"put_bytes":467,"put_count":4}} -{"i":5,"series":1,"stats":{"get_bytes":2359,"get_count":3,"put_bytes":529,"put_count":4}} -{"i":6,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":596,"put_count":4}} -{"i":7,"series":1,"stats":{"get_bytes":2423,"get_count":3,"put_bytes":593,"put_count":4}} -{"i":8,"series":1,"stats":{"get_bytes":2556,"get_count":4,"put_bytes":660,"put_count":4}} -{"i":9,"series":1,"stats":{"get_bytes":2487,"get_count":3,"put_bytes":657,"put_count":4}} -{"i":10,"series":1,"stats":{"get_bytes":2551,"get_count":3,"put_bytes":722,"put_count":4}} -{"i":11,"series":1,"stats":{"get_bytes":2615,"get_count":3,"put_bytes":789,"put_count":4}} -{"i":12,"series":1,"stats":{"get_bytes":2679,"get_count":3,"put_bytes":852,"put_count":4}} -{"i":13,"series":1,"stats":{"get_bytes":2743,"get_count":3,"put_bytes":915,"put_count":4}} -{"i":14,"series":1,"stats":{"get_bytes":2877,"get_count":4,"put_bytes":981,"put_count":4}} -{"i":15,"series":1,"stats":{"get_bytes":2876,"get_count":4,"put_bytes":979,"put_count":4}} -{"i":16,"series":1,"stats":{"get_bytes":2875,"get_count":4,"put_bytes":980,"put_count":4}} -{"i":17,"series":1,"stats":{"get_bytes":2807,"get_count":3,"put_bytes":979,"put_count":4}} -{"i":18,"series":1,"stats":{"get_bytes":2871,"get_count":3,"put_bytes":1043,"put_count":4}} -{"i":19,"series":1,"stats":{"get_bytes":3049,"get_count":5,"put_bytes":1082,"put_count":4}} -{"i":20,"series":1,"stats":{"get_bytes":2935,"get_count":3,"put_bytes":1110,"put_count":4}} -{"i":21,"series":1,"stats":{"get_bytes":2999,"get_count":3,"put_bytes":1173,"put_count":4}} -{"i":22,"series":1,"stats":{"get_bytes":3063,"get_count":3,"put_bytes":1235,"put_count":4}} -{"i":23,"series":1,"stats":{"get_bytes":3198,"get_count":4,"put_bytes":1299,"put_count":4}} -{"i":24,"series":1,"stats":{"get_bytes":3195,"get_count":4,"put_bytes":1298,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":3195,"get_count":4,"put_bytes":1299,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":3196,"get_count":4,"put_bytes":1300,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":3197,"get_count":4,"put_bytes":1299,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1299,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":3191,"get_count":3,"put_bytes":1363,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":3255,"get_count":3,"put_bytes":1425,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":3319,"get_count":3,"put_bytes":1492,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":3383,"get_count":3,"put_bytes":1552,"put_count":4}} -{"i":33,"series":1,"stats":{"get_bytes":3447,"get_count":3,"put_bytes":1620,"put_count":4}} -{"i":34,"series":1,"stats":{"get_bytes":3581,"get_count":4,"put_bytes":1683,"put_count":4}} -{"i":35,"series":1,"stats":{"get_bytes":3580,"get_count":4,"put_bytes":1683,"put_count":4}} -{"i":36,"series":1,"stats":{"get_bytes":3511,"get_count":3,"put_bytes":1683,"put_count":4}} -{"i":37,"series":1,"stats":{"get_bytes":3646,"get_count":4,"put_bytes":1749,"put_count":4}} -{"i":38,"series":1,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1811,"put_count":4}} -{"i":39,"series":1,"stats":{"get_bytes":3691,"get_count":5,"put_bytes":1724,"put_count":4}} -{"i":40,"series":1,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1811,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1812,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":3645,"get_count":4,"put_bytes":1748,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1814,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1746,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":3839,"get_count":4,"put_bytes":1943,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":2005,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":3709,"get_count":4,"put_bytes":1813,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":3640,"get_count":3,"put_bytes":1815,"put_count":4}} -{"i":49,"series":1,"stats":{"get_bytes":3774,"get_count":4,"put_bytes":1877,"put_count":4}} -{"i":50,"series":1,"stats":{"get_bytes":3704,"get_count":3,"put_bytes":1876,"put_count":4}} -{"i":51,"series":1,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":2004,"put_count":4}} -{"i":52,"series":1,"stats":{"get_bytes":3768,"get_count":3,"put_bytes":1938,"put_count":4}} -{"i":53,"series":1,"stats":{"get_bytes":4031,"get_count":4,"put_bytes":2132,"put_count":4}} -{"i":54,"series":1,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":2004,"put_count":4}} -{"i":55,"series":1,"stats":{"get_bytes":3966,"get_count":4,"put_bytes":2070,"put_count":4}} -{"i":56,"series":1,"stats":{"get_bytes":3832,"get_count":3,"put_bytes":2002,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":3896,"get_count":3,"put_bytes":2068,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":4095,"get_count":4,"put_bytes":2198,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":4095,"get_count":4,"put_bytes":2196,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":3960,"get_count":3,"put_bytes":2129,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":4397,"get_count":5,"put_bytes":2432,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":4024,"get_count":3,"put_bytes":2197,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":4156,"get_count":4,"put_bytes":2263,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2326,"put_count":4}} -{"i":65,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2324,"put_count":4}} -{"i":66,"series":1,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2323,"put_count":4}} -{"i":67,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":68,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2327,"put_count":4}} -{"i":69,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2323,"put_count":4}} -{"i":70,"series":1,"stats":{"get_bytes":4157,"get_count":4,"put_bytes":2261,"put_count":4}} -{"i":71,"series":1,"stats":{"get_bytes":4157,"get_count":4,"put_bytes":2262,"put_count":4}} -{"i":72,"series":1,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2324,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":4088,"get_count":3,"put_bytes":2259,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2324,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2325,"put_count":4}} -{"i":81,"series":1,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":82,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2324,"put_count":4}} -{"i":83,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2324,"put_count":4}} -{"i":84,"series":1,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":85,"series":1,"stats":{"get_bytes":4285,"get_count":4,"put_bytes":2386,"put_count":4}} -{"i":86,"series":1,"stats":{"get_bytes":4396,"get_count":5,"put_bytes":2429,"put_count":4}} -{"i":87,"series":1,"stats":{"get_bytes":4527,"get_count":5,"put_bytes":2560,"put_count":4}} -{"i":88,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2323,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":4349,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2516,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2327,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2322,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":2,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":3,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":4,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":5,"series":2,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2323,"put_count":4}} -{"i":6,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":7,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} -{"i":8,"series":2,"stats":{"get_bytes":4524,"get_count":5,"put_bytes":2557,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2390,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2391,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2391,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2326,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2515,"put_count":4}} -{"i":25,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":26,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2582,"put_count":4}} -{"i":27,"series":2,"stats":{"get_bytes":4396,"get_count":5,"put_bytes":2429,"put_count":4}} -{"i":28,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} -{"i":29,"series":2,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2327,"put_count":4}} -{"i":30,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2515,"put_count":4}} -{"i":31,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":32,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2582,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2389,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":4463,"get_count":5,"put_bytes":2496,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2516,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":41,"series":2,"stats":{"get_bytes":4350,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":42,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} -{"i":43,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} -{"i":44,"series":2,"stats":{"get_bytes":4285,"get_count":4,"put_bytes":2390,"put_count":4}} -{"i":45,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} -{"i":46,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} -{"i":47,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} -{"i":48,"series":2,"stats":{"get_bytes":4349,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":4286,"get_count":4,"put_bytes":2389,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2391,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":4398,"get_count":5,"put_bytes":2431,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":4526,"get_count":5,"put_bytes":2559,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} -{"i":57,"series":2,"stats":{"get_bytes":4332,"get_count":5,"put_bytes":2365,"put_count":4}} -{"i":58,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} -{"i":59,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} -{"i":60,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} -{"i":61,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} -{"i":62,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} -{"i":63,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} -{"i":64,"series":2,"stats":{"get_bytes":4525,"get_count":5,"put_bytes":2560,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":4461,"get_count":5,"put_bytes":2494,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2580,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2517,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2710,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":73,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2582,"put_count":4}} -{"i":74,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2708,"put_count":4}} -{"i":75,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":76,"series":2,"stats":{"get_bytes":4223,"get_count":4,"put_bytes":2326,"put_count":4}} -{"i":77,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2711,"put_count":4}} -{"i":78,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} -{"i":79,"series":2,"stats":{"get_bytes":4478,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":80,"series":2,"stats":{"get_bytes":4607,"get_count":4,"put_bytes":2708,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2453,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":4654,"get_count":5,"put_bytes":2687,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2455,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2581,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2644,"put_count":4}} -{"i":89,"series":2,"stats":{"get_bytes":4287,"get_count":4,"put_bytes":2388,"put_count":4}} -{"i":90,"series":2,"stats":{"get_bytes":4414,"get_count":4,"put_bytes":2519,"put_count":4}} -{"i":91,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2516,"put_count":4}} -{"i":92,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2647,"put_count":4}} -{"i":93,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2454,"put_count":4}} -{"i":94,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2646,"put_count":4}} -{"i":95,"series":2,"stats":{"get_bytes":4415,"get_count":4,"put_bytes":2518,"put_count":4}} -{"i":96,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2325,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":4479,"get_count":4,"put_bytes":2583,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":4543,"get_count":4,"put_bytes":2645,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":4351,"get_count":4,"put_bytes":2452,"put_count":4}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":135,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2143,"get_count":3,"put_bytes":177,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2185,"get_count":3,"put_bytes":218,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2226,"get_count":3,"put_bytes":259,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2267,"get_count":3,"put_bytes":300,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2308,"get_count":3,"put_bytes":412,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2331,"get_count":3,"put_bytes":364,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2372,"get_count":3,"put_bytes":475,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2395,"get_count":3,"put_bytes":428,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2436,"get_count":3,"put_bytes":469,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2477,"get_count":3,"put_bytes":510,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2518,"get_count":3,"put_bytes":551,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2559,"get_count":3,"put_bytes":592,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2600,"get_count":3,"put_bytes":704,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2623,"get_count":3,"put_bytes":727,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2646,"get_count":3,"put_bytes":750,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2669,"get_count":3,"put_bytes":702,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2710,"get_count":3,"put_bytes":743,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2751,"get_count":3,"put_bytes":854,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2776,"get_count":3,"put_bytes":809,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2817,"get_count":3,"put_bytes":850,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2858,"get_count":3,"put_bytes":891,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2899,"get_count":3,"put_bytes":1003,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2922,"get_count":3,"put_bytes":1024,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2945,"get_count":3,"put_bytes":1048,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2968,"get_count":3,"put_bytes":1070,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2991,"get_count":3,"put_bytes":1094,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":3014,"get_count":3,"put_bytes":1047,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":3055,"get_count":3,"put_bytes":1088,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":3096,"get_count":3,"put_bytes":1129,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3137,"get_count":3,"put_bytes":1170,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3178,"get_count":3,"put_bytes":1211,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3219,"get_count":3,"put_bytes":1252,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1364,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":3283,"get_count":3,"put_bytes":1387,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":3306,"get_count":3,"put_bytes":1339,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3347,"get_count":3,"put_bytes":1451,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":3457,"get_count":4,"put_bytes":1492,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":3370,"get_count":3,"put_bytes":1476,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":3484,"get_count":4,"put_bytes":1517,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":3482,"get_count":4,"put_bytes":1517,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":3395,"get_count":3,"put_bytes":1498,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":3507,"get_count":4,"put_bytes":1540,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":3418,"get_count":3,"put_bytes":1452,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3590,"get_count":4,"put_bytes":1623,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":3631,"get_count":4,"put_bytes":1664,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":3546,"get_count":4,"put_bytes":1560,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":3458,"get_count":3,"put_bytes":1491,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3499,"get_count":3,"put_bytes":1602,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":3522,"get_count":3,"put_bytes":1555,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3652,"get_count":4,"put_bytes":1685,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":3563,"get_count":3,"put_bytes":1596,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3734,"get_count":4,"put_bytes":1767,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":3604,"get_count":3,"put_bytes":1707,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":3715,"get_count":4,"put_bytes":1748,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":3627,"get_count":3,"put_bytes":1660,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3668,"get_count":3,"put_bytes":1701,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1831,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1831,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":3709,"get_count":3,"put_bytes":1742,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3962,"get_count":4,"put_bytes":2066,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":3750,"get_count":3,"put_bytes":1783,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3880,"get_count":4,"put_bytes":1893,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1934,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1911,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":3877,"get_count":4,"put_bytes":1910,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1911,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":3899,"get_count":4,"put_bytes":1932,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":3789,"get_count":3,"put_bytes":1892,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":3812,"get_count":3,"put_bytes":1916,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":3923,"get_count":4,"put_bytes":1956,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":3835,"get_count":3,"put_bytes":1868,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2039,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":3876,"get_count":3,"put_bytes":1980,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":3987,"get_count":4,"put_bytes":2020,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":3899,"get_count":3,"put_bytes":2003,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":4011,"get_count":4,"put_bytes":2044,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":3922,"get_count":3,"put_bytes":2024,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":3945,"get_count":3,"put_bytes":2049,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":4097,"get_count":4,"put_bytes":2130,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":4055,"get_count":4,"put_bytes":2088,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":4098,"get_count":4,"put_bytes":2199,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":4203,"get_count":4,"put_bytes":2307,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":3968,"get_count":3,"put_bytes":2070,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":4119,"get_count":4,"put_bytes":2154,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":4121,"get_count":4,"put_bytes":2154,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":4120,"get_count":4,"put_bytes":2153,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":4080,"get_count":4,"put_bytes":2113,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":4203,"get_count":4,"put_bytes":2236,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":3991,"get_count":3,"put_bytes":2095,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":4102,"get_count":4,"put_bytes":2135,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":4014,"get_count":3,"put_bytes":2115,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":4125,"get_count":4,"put_bytes":2158,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":4166,"get_count":4,"put_bytes":2199,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":4166,"get_count":4,"put_bytes":2200,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":4125,"get_count":4,"put_bytes":2158,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":4037,"get_count":3,"put_bytes":2140,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":2223,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4272,"get_count":4,"put_bytes":2373,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":2223,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4149,"get_count":4,"put_bytes":2182,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4272,"get_count":4,"put_bytes":2305,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4149,"get_count":4,"put_bytes":2182,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2182,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4231,"get_count":4,"put_bytes":2264,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4189,"get_count":4,"put_bytes":2222,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4060,"get_count":3,"put_bytes":2164,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4254,"get_count":4,"put_bytes":2287,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4212,"get_count":4,"put_bytes":2245,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2287,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2287,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2286,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4295,"get_count":4,"put_bytes":2328,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4212,"get_count":4,"put_bytes":2314,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":4336,"get_count":4,"put_bytes":2369,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4083,"get_count":3,"put_bytes":2187,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2309,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2350,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2381,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2309,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4258,"get_count":4,"put_bytes":2291,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2379,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4193,"get_count":4,"put_bytes":2228,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2379,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":4194,"get_count":4,"put_bytes":2228,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2339,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2421,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2296,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2422,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4299,"get_count":4,"put_bytes":2402,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2456,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2433,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2269,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4106,"get_count":3,"put_bytes":2210,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2456,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4363,"get_count":4,"put_bytes":2396,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4446,"get_count":4,"put_bytes":2479,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2526,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":4218,"get_count":4,"put_bytes":2251,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":4345,"get_count":4,"put_bytes":2379,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":4382,"get_count":4,"put_bytes":2415,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":4129,"get_count":3,"put_bytes":2231,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":4428,"get_count":4,"put_bytes":2461,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":4282,"get_count":4,"put_bytes":2315,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png index f0d1cf63d9b224e02c25753df729c4c4aa6b4d6b..8db808d1100d382069533e991b546c69c9d825da 100644 GIT binary patch literal 19532 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 L{M?(|K4<<0Q8+Hy diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index a8fda0223..3bb020620 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ {"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":12985,"put_count":128}} -{"i":2,"series":1,"stats":{"get_bytes":10211,"get_count":39,"put_bytes":18447,"put_count":126}} -{"i":3,"series":1,"stats":{"get_bytes":15154,"get_count":44,"put_bytes":23084,"put_count":127}} -{"i":4,"series":1,"stats":{"get_bytes":22384,"get_count":65,"put_bytes":29015,"put_count":129}} -{"i":5,"series":1,"stats":{"get_bytes":25985,"get_count":63,"put_bytes":32809,"put_count":130}} -{"i":6,"series":1,"stats":{"get_bytes":28428,"get_count":64,"put_bytes":34638,"put_count":122}} -{"i":7,"series":1,"stats":{"get_bytes":34408,"get_count":79,"put_bytes":40062,"put_count":129}} -{"i":8,"series":1,"stats":{"get_bytes":39961,"get_count":82,"put_bytes":45673,"put_count":133}} -{"i":9,"series":1,"stats":{"get_bytes":42029,"get_count":92,"put_bytes":46762,"put_count":129}} -{"i":10,"series":1,"stats":{"get_bytes":45715,"get_count":81,"put_bytes":51469,"put_count":132}} -{"i":11,"series":1,"stats":{"get_bytes":49696,"get_count":93,"put_bytes":54515,"put_count":131}} -{"i":12,"series":1,"stats":{"get_bytes":52164,"get_count":106,"put_bytes":56010,"put_count":130}} -{"i":13,"series":1,"stats":{"get_bytes":50580,"get_count":99,"put_bytes":54787,"put_count":128}} -{"i":14,"series":1,"stats":{"get_bytes":55870,"get_count":103,"put_bytes":59775,"put_count":128}} -{"i":15,"series":1,"stats":{"get_bytes":56795,"get_count":105,"put_bytes":60667,"put_count":129}} -{"i":16,"series":1,"stats":{"get_bytes":59531,"get_count":110,"put_bytes":63104,"put_count":130}} -{"i":17,"series":1,"stats":{"get_bytes":59946,"get_count":111,"put_bytes":63388,"put_count":129}} -{"i":18,"series":1,"stats":{"get_bytes":57360,"get_count":109,"put_bytes":60717,"put_count":126}} -{"i":19,"series":1,"stats":{"get_bytes":67465,"get_count":117,"put_bytes":70551,"put_count":130}} -{"i":20,"series":1,"stats":{"get_bytes":61499,"get_count":107,"put_bytes":64854,"put_count":124}} -{"i":21,"series":1,"stats":{"get_bytes":64324,"get_count":115,"put_bytes":67382,"put_count":128}} -{"i":22,"series":1,"stats":{"get_bytes":64015,"get_count":112,"put_bytes":67078,"put_count":125}} -{"i":23,"series":1,"stats":{"get_bytes":72005,"get_count":120,"put_bytes":74891,"put_count":130}} -{"i":24,"series":1,"stats":{"get_bytes":71164,"get_count":121,"put_bytes":74018,"put_count":131}} -{"i":25,"series":1,"stats":{"get_bytes":71688,"get_count":125,"put_bytes":74206,"put_count":130}} -{"i":26,"series":1,"stats":{"get_bytes":70582,"get_count":122,"put_bytes":73145,"put_count":128}} -{"i":27,"series":1,"stats":{"get_bytes":72469,"get_count":123,"put_bytes":74779,"put_count":125}} -{"i":28,"series":1,"stats":{"get_bytes":72814,"get_count":124,"put_bytes":75299,"put_count":129}} -{"i":29,"series":1,"stats":{"get_bytes":73038,"get_count":120,"put_bytes":75755,"put_count":128}} -{"i":30,"series":1,"stats":{"get_bytes":78115,"get_count":126,"put_bytes":80598,"put_count":131}} -{"i":31,"series":1,"stats":{"get_bytes":71793,"get_count":122,"put_bytes":74073,"put_count":124}} -{"i":32,"series":1,"stats":{"get_bytes":78950,"get_count":124,"put_bytes":81502,"put_count":130}} -{"i":33,"series":1,"stats":{"get_bytes":76082,"get_count":127,"put_bytes":78139,"put_count":126}} -{"i":34,"series":1,"stats":{"get_bytes":78285,"get_count":123,"put_bytes":80920,"put_count":130}} -{"i":35,"series":1,"stats":{"get_bytes":77256,"get_count":122,"put_bytes":79533,"put_count":124}} -{"i":36,"series":1,"stats":{"get_bytes":78418,"get_count":122,"put_bytes":80780,"put_count":125}} -{"i":37,"series":1,"stats":{"get_bytes":79451,"get_count":129,"put_bytes":81595,"put_count":129}} -{"i":38,"series":1,"stats":{"get_bytes":81457,"get_count":129,"put_bytes":83643,"put_count":130}} -{"i":39,"series":1,"stats":{"get_bytes":75464,"get_count":124,"put_bytes":77751,"put_count":126}} -{"i":40,"series":1,"stats":{"get_bytes":80913,"get_count":128,"put_bytes":83131,"put_count":129}} -{"i":41,"series":1,"stats":{"get_bytes":80407,"get_count":127,"put_bytes":82774,"put_count":130}} -{"i":42,"series":1,"stats":{"get_bytes":82877,"get_count":127,"put_bytes":85068,"put_count":128}} -{"i":43,"series":1,"stats":{"get_bytes":80876,"get_count":123,"put_bytes":82994,"put_count":123}} -{"i":44,"series":1,"stats":{"get_bytes":86523,"get_count":129,"put_bytes":88663,"put_count":129}} -{"i":45,"series":1,"stats":{"get_bytes":83642,"get_count":129,"put_bytes":85749,"put_count":129}} -{"i":46,"series":1,"stats":{"get_bytes":85511,"get_count":131,"put_bytes":87607,"put_count":131}} -{"i":47,"series":1,"stats":{"get_bytes":85112,"get_count":129,"put_bytes":87147,"put_count":128}} -{"i":48,"series":1,"stats":{"get_bytes":88700,"get_count":126,"put_bytes":90751,"put_count":125}} -{"i":49,"series":1,"stats":{"get_bytes":86599,"get_count":130,"put_bytes":88856,"put_count":132}} -{"i":50,"series":1,"stats":{"get_bytes":83343,"get_count":123,"put_bytes":85475,"put_count":123}} -{"i":51,"series":1,"stats":{"get_bytes":86737,"get_count":123,"put_bytes":88869,"put_count":123}} -{"i":52,"series":1,"stats":{"get_bytes":87509,"get_count":132,"put_bytes":89552,"put_count":131}} -{"i":53,"series":1,"stats":{"get_bytes":83973,"get_count":124,"put_bytes":86105,"put_count":124}} -{"i":54,"series":1,"stats":{"get_bytes":88890,"get_count":132,"put_bytes":90931,"put_count":131}} -{"i":55,"series":1,"stats":{"get_bytes":86483,"get_count":124,"put_bytes":88547,"put_count":123}} -{"i":56,"series":1,"stats":{"get_bytes":86737,"get_count":128,"put_bytes":88856,"put_count":128}} -{"i":57,"series":1,"stats":{"get_bytes":90763,"get_count":131,"put_bytes":92816,"put_count":130}} -{"i":58,"series":1,"stats":{"get_bytes":88326,"get_count":129,"put_bytes":90363,"put_count":128}} -{"i":59,"series":1,"stats":{"get_bytes":90383,"get_count":132,"put_bytes":92431,"put_count":131}} -{"i":60,"series":1,"stats":{"get_bytes":89174,"get_count":132,"put_bytes":91223,"put_count":131}} -{"i":61,"series":1,"stats":{"get_bytes":93524,"get_count":132,"put_bytes":95573,"put_count":131}} -{"i":62,"series":1,"stats":{"get_bytes":86253,"get_count":128,"put_bytes":88316,"put_count":127}} -{"i":63,"series":1,"stats":{"get_bytes":94435,"get_count":131,"put_bytes":96525,"put_count":131}} -{"i":64,"series":1,"stats":{"get_bytes":94384,"get_count":128,"put_bytes":96493,"put_count":128}} -{"i":65,"series":1,"stats":{"get_bytes":87052,"get_count":125,"put_bytes":89072,"put_count":124}} -{"i":66,"series":1,"stats":{"get_bytes":93194,"get_count":128,"put_bytes":95287,"put_count":128}} -{"i":67,"series":1,"stats":{"get_bytes":93462,"get_count":132,"put_bytes":95509,"put_count":131}} -{"i":68,"series":1,"stats":{"get_bytes":95363,"get_count":130,"put_bytes":97402,"put_count":129}} -{"i":69,"series":1,"stats":{"get_bytes":95468,"get_count":131,"put_bytes":97497,"put_count":130}} -{"i":70,"series":1,"stats":{"get_bytes":96314,"get_count":131,"put_bytes":98334,"put_count":130}} -{"i":71,"series":1,"stats":{"get_bytes":92797,"get_count":126,"put_bytes":94814,"put_count":125}} -{"i":72,"series":1,"stats":{"get_bytes":92589,"get_count":128,"put_bytes":94606,"put_count":127}} -{"i":73,"series":1,"stats":{"get_bytes":94086,"get_count":128,"put_bytes":96202,"put_count":128}} -{"i":74,"series":1,"stats":{"get_bytes":91593,"get_count":126,"put_bytes":93615,"put_count":125}} -{"i":75,"series":1,"stats":{"get_bytes":98705,"get_count":132,"put_bytes":100718,"put_count":131}} -{"i":76,"series":1,"stats":{"get_bytes":96803,"get_count":129,"put_bytes":98822,"put_count":128}} -{"i":77,"series":1,"stats":{"get_bytes":94873,"get_count":130,"put_bytes":96925,"put_count":129}} -{"i":78,"series":1,"stats":{"get_bytes":96562,"get_count":129,"put_bytes":98599,"put_count":128}} -{"i":79,"series":1,"stats":{"get_bytes":96338,"get_count":129,"put_bytes":98328,"put_count":128}} -{"i":80,"series":1,"stats":{"get_bytes":97218,"get_count":129,"put_bytes":99302,"put_count":129}} -{"i":81,"series":1,"stats":{"get_bytes":100293,"get_count":132,"put_bytes":102369,"put_count":132}} -{"i":82,"series":1,"stats":{"get_bytes":97092,"get_count":126,"put_bytes":99165,"put_count":126}} -{"i":83,"series":1,"stats":{"get_bytes":93984,"get_count":122,"put_bytes":96014,"put_count":121}} -{"i":84,"series":1,"stats":{"get_bytes":103254,"get_count":131,"put_bytes":105255,"put_count":130}} -{"i":85,"series":1,"stats":{"get_bytes":98209,"get_count":128,"put_bytes":100245,"put_count":127}} -{"i":86,"series":1,"stats":{"get_bytes":100671,"get_count":131,"put_bytes":102707,"put_count":130}} -{"i":87,"series":1,"stats":{"get_bytes":98575,"get_count":129,"put_bytes":100594,"put_count":128}} -{"i":88,"series":1,"stats":{"get_bytes":99376,"get_count":128,"put_bytes":101448,"put_count":128}} -{"i":89,"series":1,"stats":{"get_bytes":103166,"get_count":130,"put_bytes":105142,"put_count":129}} -{"i":90,"series":1,"stats":{"get_bytes":106255,"get_count":132,"put_bytes":108322,"put_count":132}} -{"i":91,"series":1,"stats":{"get_bytes":99795,"get_count":130,"put_bytes":101795,"put_count":129}} -{"i":92,"series":1,"stats":{"get_bytes":106360,"get_count":130,"put_bytes":108345,"put_count":129}} -{"i":93,"series":1,"stats":{"get_bytes":100558,"get_count":125,"put_bytes":102664,"put_count":126}} -{"i":94,"series":1,"stats":{"get_bytes":103544,"get_count":127,"put_bytes":105539,"put_count":126}} -{"i":95,"series":1,"stats":{"get_bytes":103653,"get_count":128,"put_bytes":105630,"put_count":127}} -{"i":96,"series":1,"stats":{"get_bytes":101148,"get_count":131,"put_bytes":103179,"put_count":130}} -{"i":97,"series":1,"stats":{"get_bytes":101806,"get_count":130,"put_bytes":103953,"put_count":131}} -{"i":98,"series":1,"stats":{"get_bytes":101247,"get_count":128,"put_bytes":103325,"put_count":128}} -{"i":99,"series":1,"stats":{"get_bytes":104300,"get_count":130,"put_bytes":106306,"put_count":129}} -{"i":0,"series":2,"stats":{"get_bytes":107175,"get_count":132,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":105892,"get_count":128,"put_bytes":107885,"put_count":127}} -{"i":2,"series":2,"stats":{"get_bytes":105041,"get_count":129,"put_bytes":107015,"put_count":128}} -{"i":3,"series":2,"stats":{"get_bytes":107752,"get_count":132,"put_bytes":109733,"put_count":131}} -{"i":4,"series":2,"stats":{"get_bytes":100926,"get_count":127,"put_bytes":102985,"put_count":127}} -{"i":5,"series":2,"stats":{"get_bytes":106895,"get_count":130,"put_bytes":108873,"put_count":129}} -{"i":6,"series":2,"stats":{"get_bytes":110493,"get_count":131,"put_bytes":112556,"put_count":131}} -{"i":7,"series":2,"stats":{"get_bytes":109805,"get_count":128,"put_bytes":111762,"put_count":127}} -{"i":8,"series":2,"stats":{"get_bytes":112945,"get_count":133,"put_bytes":114994,"put_count":133}} -{"i":9,"series":2,"stats":{"get_bytes":111329,"get_count":131,"put_bytes":113313,"put_count":130}} -{"i":10,"series":2,"stats":{"get_bytes":107197,"get_count":128,"put_bytes":109184,"put_count":127}} -{"i":11,"series":2,"stats":{"get_bytes":109496,"get_count":128,"put_bytes":111556,"put_count":128}} -{"i":12,"series":2,"stats":{"get_bytes":108052,"get_count":126,"put_bytes":110027,"put_count":125}} -{"i":13,"series":2,"stats":{"get_bytes":108081,"get_count":128,"put_bytes":110057,"put_count":127}} -{"i":14,"series":2,"stats":{"get_bytes":111370,"get_count":130,"put_bytes":113411,"put_count":130}} -{"i":15,"series":2,"stats":{"get_bytes":99536,"get_count":123,"put_bytes":101541,"put_count":122}} -{"i":16,"series":2,"stats":{"get_bytes":111392,"get_count":129,"put_bytes":113462,"put_count":129}} -{"i":17,"series":2,"stats":{"get_bytes":112540,"get_count":129,"put_bytes":114770,"put_count":131}} -{"i":18,"series":2,"stats":{"get_bytes":113781,"get_count":129,"put_bytes":115734,"put_count":128}} -{"i":19,"series":2,"stats":{"get_bytes":110440,"get_count":125,"put_bytes":112451,"put_count":124}} -{"i":20,"series":2,"stats":{"get_bytes":111409,"get_count":128,"put_bytes":113470,"put_count":128}} -{"i":21,"series":2,"stats":{"get_bytes":117088,"get_count":132,"put_bytes":119128,"put_count":132}} -{"i":22,"series":2,"stats":{"get_bytes":113964,"get_count":129,"put_bytes":115930,"put_count":128}} -{"i":23,"series":2,"stats":{"get_bytes":111619,"get_count":125,"put_bytes":113596,"put_count":124}} -{"i":24,"series":2,"stats":{"get_bytes":114800,"get_count":129,"put_bytes":116776,"put_count":128}} -{"i":25,"series":2,"stats":{"get_bytes":115672,"get_count":126,"put_bytes":117736,"put_count":126}} -{"i":26,"series":2,"stats":{"get_bytes":117101,"get_count":131,"put_bytes":119167,"put_count":131}} -{"i":27,"series":2,"stats":{"get_bytes":119142,"get_count":133,"put_bytes":121117,"put_count":132}} -{"i":28,"series":2,"stats":{"get_bytes":117093,"get_count":133,"put_bytes":119136,"put_count":133}} -{"i":29,"series":2,"stats":{"get_bytes":110498,"get_count":126,"put_bytes":112481,"put_count":125}} -{"i":30,"series":2,"stats":{"get_bytes":115444,"get_count":129,"put_bytes":117557,"put_count":130}} -{"i":31,"series":2,"stats":{"get_bytes":118258,"get_count":130,"put_bytes":120205,"put_count":129}} -{"i":32,"series":2,"stats":{"get_bytes":118663,"get_count":132,"put_bytes":120756,"put_count":133}} -{"i":33,"series":2,"stats":{"get_bytes":112846,"get_count":128,"put_bytes":114802,"put_count":127}} -{"i":34,"series":2,"stats":{"get_bytes":119046,"get_count":129,"put_bytes":121005,"put_count":128}} -{"i":35,"series":2,"stats":{"get_bytes":120245,"get_count":128,"put_bytes":122209,"put_count":127}} -{"i":36,"series":2,"stats":{"get_bytes":119207,"get_count":129,"put_bytes":121295,"put_count":130}} -{"i":37,"series":2,"stats":{"get_bytes":117142,"get_count":126,"put_bytes":119110,"put_count":125}} -{"i":38,"series":2,"stats":{"get_bytes":116074,"get_count":127,"put_bytes":118006,"put_count":126}} -{"i":39,"series":2,"stats":{"get_bytes":113919,"get_count":127,"put_bytes":116078,"put_count":128}} -{"i":40,"series":2,"stats":{"get_bytes":114291,"get_count":126,"put_bytes":116256,"put_count":125}} -{"i":41,"series":2,"stats":{"get_bytes":118343,"get_count":130,"put_bytes":120291,"put_count":129}} -{"i":42,"series":2,"stats":{"get_bytes":114208,"get_count":125,"put_bytes":116166,"put_count":124}} -{"i":43,"series":2,"stats":{"get_bytes":120742,"get_count":129,"put_bytes":122742,"put_count":128}} -{"i":44,"series":2,"stats":{"get_bytes":120506,"get_count":128,"put_bytes":122549,"put_count":128}} -{"i":45,"series":2,"stats":{"get_bytes":124250,"get_count":132,"put_bytes":126269,"put_count":132}} -{"i":46,"series":2,"stats":{"get_bytes":124093,"get_count":132,"put_bytes":126231,"put_count":133}} -{"i":47,"series":2,"stats":{"get_bytes":121883,"get_count":128,"put_bytes":123981,"put_count":129}} -{"i":48,"series":2,"stats":{"get_bytes":122169,"get_count":130,"put_bytes":124105,"put_count":129}} -{"i":49,"series":2,"stats":{"get_bytes":123482,"get_count":131,"put_bytes":125524,"put_count":131}} -{"i":50,"series":2,"stats":{"get_bytes":120478,"get_count":128,"put_bytes":122608,"put_count":129}} -{"i":51,"series":2,"stats":{"get_bytes":123511,"get_count":132,"put_bytes":125502,"put_count":131}} -{"i":52,"series":2,"stats":{"get_bytes":124946,"get_count":132,"put_bytes":126852,"put_count":131}} -{"i":53,"series":2,"stats":{"get_bytes":119593,"get_count":125,"put_bytes":121563,"put_count":124}} -{"i":54,"series":2,"stats":{"get_bytes":121582,"get_count":130,"put_bytes":123677,"put_count":131}} -{"i":55,"series":2,"stats":{"get_bytes":122011,"get_count":127,"put_bytes":124065,"put_count":127}} -{"i":56,"series":2,"stats":{"get_bytes":123491,"get_count":131,"put_bytes":125503,"put_count":131}} -{"i":57,"series":2,"stats":{"get_bytes":123674,"get_count":129,"put_bytes":125689,"put_count":129}} -{"i":58,"series":2,"stats":{"get_bytes":130858,"get_count":134,"put_bytes":132959,"put_count":135}} -{"i":59,"series":2,"stats":{"get_bytes":130348,"get_count":129,"put_bytes":132252,"put_count":128}} -{"i":60,"series":2,"stats":{"get_bytes":131679,"get_count":132,"put_bytes":133614,"put_count":131}} -{"i":61,"series":2,"stats":{"get_bytes":131114,"get_count":129,"put_bytes":133197,"put_count":130}} -{"i":62,"series":2,"stats":{"get_bytes":124596,"get_count":127,"put_bytes":126633,"put_count":127}} -{"i":63,"series":2,"stats":{"get_bytes":121440,"get_count":126,"put_bytes":123589,"put_count":128}} -{"i":64,"series":2,"stats":{"get_bytes":128756,"get_count":127,"put_bytes":130916,"put_count":129}} -{"i":65,"series":2,"stats":{"get_bytes":130453,"get_count":130,"put_bytes":132548,"put_count":131}} -{"i":66,"series":2,"stats":{"get_bytes":129379,"get_count":129,"put_bytes":131478,"put_count":130}} -{"i":67,"series":2,"stats":{"get_bytes":132308,"get_count":130,"put_bytes":134306,"put_count":130}} -{"i":68,"series":2,"stats":{"get_bytes":132270,"get_count":131,"put_bytes":134288,"put_count":131}} -{"i":69,"series":2,"stats":{"get_bytes":129616,"get_count":130,"put_bytes":131616,"put_count":130}} -{"i":70,"series":2,"stats":{"get_bytes":132413,"get_count":131,"put_bytes":134364,"put_count":130}} -{"i":71,"series":2,"stats":{"get_bytes":130045,"get_count":129,"put_bytes":132073,"put_count":129}} -{"i":72,"series":2,"stats":{"get_bytes":128112,"get_count":128,"put_bytes":130052,"put_count":127}} -{"i":73,"series":2,"stats":{"get_bytes":129276,"get_count":130,"put_bytes":131338,"put_count":131}} -{"i":74,"series":2,"stats":{"get_bytes":126795,"get_count":127,"put_bytes":128724,"put_count":126}} -{"i":75,"series":2,"stats":{"get_bytes":128572,"get_count":128,"put_bytes":130582,"put_count":128}} -{"i":76,"series":2,"stats":{"get_bytes":131583,"get_count":132,"put_bytes":133656,"put_count":133}} -{"i":77,"series":2,"stats":{"get_bytes":136555,"get_count":129,"put_bytes":138485,"put_count":128}} -{"i":78,"series":2,"stats":{"get_bytes":134366,"get_count":129,"put_bytes":136294,"put_count":128}} -{"i":79,"series":2,"stats":{"get_bytes":133710,"get_count":129,"put_bytes":135638,"put_count":128}} -{"i":80,"series":2,"stats":{"get_bytes":136585,"get_count":133,"put_bytes":138739,"put_count":135}} -{"i":81,"series":2,"stats":{"get_bytes":130286,"get_count":128,"put_bytes":132385,"put_count":129}} -{"i":82,"series":2,"stats":{"get_bytes":137820,"get_count":132,"put_bytes":139812,"put_count":132}} -{"i":83,"series":2,"stats":{"get_bytes":134583,"get_count":128,"put_bytes":136567,"put_count":128}} -{"i":84,"series":2,"stats":{"get_bytes":133301,"get_count":128,"put_bytes":135324,"put_count":128}} -{"i":85,"series":2,"stats":{"get_bytes":133877,"get_count":127,"put_bytes":135875,"put_count":127}} -{"i":86,"series":2,"stats":{"get_bytes":129985,"get_count":126,"put_bytes":132035,"put_count":127}} -{"i":87,"series":2,"stats":{"get_bytes":140246,"get_count":131,"put_bytes":142162,"put_count":130}} -{"i":88,"series":2,"stats":{"get_bytes":133745,"get_count":130,"put_bytes":135755,"put_count":130}} -{"i":89,"series":2,"stats":{"get_bytes":131055,"get_count":126,"put_bytes":133307,"put_count":129}} -{"i":90,"series":2,"stats":{"get_bytes":133072,"get_count":131,"put_bytes":135260,"put_count":133}} -{"i":91,"series":2,"stats":{"get_bytes":139130,"get_count":131,"put_bytes":141062,"put_count":130}} -{"i":92,"series":2,"stats":{"get_bytes":134456,"get_count":127,"put_bytes":136540,"put_count":128}} -{"i":93,"series":2,"stats":{"get_bytes":134732,"get_count":127,"put_bytes":136868,"put_count":129}} -{"i":94,"series":2,"stats":{"get_bytes":137970,"get_count":131,"put_bytes":139898,"put_count":130}} -{"i":95,"series":2,"stats":{"get_bytes":135161,"get_count":127,"put_bytes":137247,"put_count":128}} -{"i":96,"series":2,"stats":{"get_bytes":139119,"get_count":131,"put_bytes":141119,"put_count":131}} -{"i":97,"series":2,"stats":{"get_bytes":139486,"get_count":130,"put_bytes":141641,"put_count":132}} -{"i":98,"series":2,"stats":{"get_bytes":142320,"get_count":130,"put_bytes":144500,"put_count":132}} -{"i":99,"series":2,"stats":{"get_bytes":132169,"get_count":126,"put_bytes":134240,"put_count":127}} +{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":6519,"put_count":35}} +{"i":2,"series":1,"stats":{"get_bytes":7742,"get_count":28,"put_bytes":10971,"put_count":43}} +{"i":3,"series":1,"stats":{"get_bytes":11277,"get_count":32,"put_bytes":14428,"put_count":46}} +{"i":4,"series":1,"stats":{"get_bytes":15861,"get_count":37,"put_bytes":20198,"put_count":68}} +{"i":5,"series":1,"stats":{"get_bytes":19308,"get_count":43,"put_bytes":22950,"put_count":64}} +{"i":6,"series":1,"stats":{"get_bytes":20933,"get_count":38,"put_bytes":25558,"put_count":73}} +{"i":7,"series":1,"stats":{"get_bytes":25662,"get_count":47,"put_bytes":30218,"put_count":81}} +{"i":8,"series":1,"stats":{"get_bytes":30144,"get_count":48,"put_bytes":34690,"put_count":82}} +{"i":9,"series":1,"stats":{"get_bytes":32104,"get_count":55,"put_bytes":36897,"put_count":93}} +{"i":10,"series":1,"stats":{"get_bytes":35617,"get_count":49,"put_bytes":40031,"put_count":81}} +{"i":11,"series":1,"stats":{"get_bytes":38827,"get_count":54,"put_bytes":43796,"put_count":94}} +{"i":12,"series":1,"stats":{"get_bytes":41747,"get_count":70,"put_bytes":46546,"put_count":108}} +{"i":13,"series":1,"stats":{"get_bytes":41298,"get_count":68,"put_bytes":45844,"put_count":102}} +{"i":14,"series":1,"stats":{"get_bytes":45814,"get_count":67,"put_bytes":50908,"put_count":109}} +{"i":15,"series":1,"stats":{"get_bytes":47243,"get_count":71,"put_bytes":51942,"put_count":107}} +{"i":16,"series":1,"stats":{"get_bytes":49844,"get_count":72,"put_bytes":54874,"put_count":113}} +{"i":17,"series":1,"stats":{"get_bytes":50905,"get_count":74,"put_bytes":55887,"put_count":114}} +{"i":18,"series":1,"stats":{"get_bytes":49539,"get_count":75,"put_bytes":54434,"put_count":114}} +{"i":19,"series":1,"stats":{"get_bytes":59204,"get_count":92,"put_bytes":63340,"put_count":120}} +{"i":20,"series":1,"stats":{"get_bytes":53996,"get_count":81,"put_bytes":58422,"put_count":113}} +{"i":21,"series":1,"stats":{"get_bytes":56878,"get_count":80,"put_bytes":61938,"put_count":121}} +{"i":22,"series":1,"stats":{"get_bytes":57211,"get_count":83,"put_bytes":61775,"put_count":117}} +{"i":23,"series":1,"stats":{"get_bytes":65829,"get_count":108,"put_bytes":68885,"put_count":121}} +{"i":24,"series":1,"stats":{"get_bytes":64920,"get_count":104,"put_bytes":68621,"put_count":126}} +{"i":25,"series":1,"stats":{"get_bytes":65356,"get_count":102,"put_bytes":69556,"put_count":131}} +{"i":26,"series":1,"stats":{"get_bytes":64373,"get_count":94,"put_bytes":69010,"put_count":129}} +{"i":27,"series":1,"stats":{"get_bytes":67478,"get_count":107,"put_bytes":70921,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":67869,"get_count":106,"put_bytes":72012,"put_count":134}} +{"i":29,"series":1,"stats":{"get_bytes":68545,"get_count":106,"put_bytes":71920,"put_count":123}} +{"i":30,"series":1,"stats":{"get_bytes":73611,"get_count":113,"put_bytes":77671,"put_count":140}} +{"i":31,"series":1,"stats":{"get_bytes":67769,"get_count":107,"put_bytes":71605,"put_count":131}} +{"i":32,"series":1,"stats":{"get_bytes":74875,"get_count":111,"put_bytes":78712,"put_count":135}} +{"i":33,"series":1,"stats":{"get_bytes":72397,"get_count":114,"put_bytes":76441,"put_count":141}} +{"i":34,"series":1,"stats":{"get_bytes":74728,"get_count":113,"put_bytes":78044,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":74250,"get_count":116,"put_bytes":77438,"put_count":131}} +{"i":36,"series":1,"stats":{"get_bytes":75270,"get_count":113,"put_bytes":78545,"put_count":129}} +{"i":37,"series":1,"stats":{"get_bytes":76288,"get_count":117,"put_bytes":79945,"put_count":138}} +{"i":38,"series":1,"stats":{"get_bytes":78472,"get_count":120,"put_bytes":82316,"put_count":144}} +{"i":39,"series":1,"stats":{"get_bytes":72991,"get_count":115,"put_bytes":76226,"put_count":130}} +{"i":40,"series":1,"stats":{"get_bytes":78483,"get_count":121,"put_bytes":81563,"put_count":134}} +{"i":41,"series":1,"stats":{"get_bytes":78043,"get_count":118,"put_bytes":81349,"put_count":134}} +{"i":42,"series":1,"stats":{"get_bytes":80519,"get_count":118,"put_bytes":84308,"put_count":141}} +{"i":43,"series":1,"stats":{"get_bytes":79002,"get_count":121,"put_bytes":82284,"put_count":137}} +{"i":44,"series":1,"stats":{"get_bytes":84630,"get_count":127,"put_bytes":87585,"put_count":138}} +{"i":45,"series":1,"stats":{"get_bytes":81893,"get_count":123,"put_bytes":85512,"put_count":144}} +{"i":46,"series":1,"stats":{"get_bytes":83817,"get_count":128,"put_bytes":87318,"put_count":147}} +{"i":47,"series":1,"stats":{"get_bytes":83553,"get_count":128,"put_bytes":86920,"put_count":145}} +{"i":48,"series":1,"stats":{"get_bytes":87152,"get_count":125,"put_bytes":90316,"put_count":139}} +{"i":49,"series":1,"stats":{"get_bytes":85056,"get_count":127,"put_bytes":88492,"put_count":145}} +{"i":50,"series":1,"stats":{"get_bytes":81605,"get_count":117,"put_bytes":84758,"put_count":131}} +{"i":51,"series":1,"stats":{"get_bytes":85290,"get_count":119,"put_bytes":88376,"put_count":132}} +{"i":52,"series":1,"stats":{"get_bytes":86074,"get_count":128,"put_bytes":89503,"put_count":146}} +{"i":53,"series":1,"stats":{"get_bytes":82817,"get_count":122,"put_bytes":85687,"put_count":132}} +{"i":54,"series":1,"stats":{"get_bytes":87781,"get_count":130,"put_bytes":91081,"put_count":146}} +{"i":55,"series":1,"stats":{"get_bytes":85214,"get_count":120,"put_bytes":88273,"put_count":133}} +{"i":56,"series":1,"stats":{"get_bytes":85673,"get_count":127,"put_bytes":88899,"put_count":142}} +{"i":57,"series":1,"stats":{"get_bytes":89977,"get_count":132,"put_bytes":92910,"put_count":143}} +{"i":58,"series":1,"stats":{"get_bytes":87311,"get_count":129,"put_bytes":90520,"put_count":144}} +{"i":59,"series":1,"stats":{"get_bytes":89642,"get_count":135,"put_bytes":92369,"put_count":143}} +{"i":60,"series":1,"stats":{"get_bytes":88152,"get_count":129,"put_bytes":91446,"put_count":145}} +{"i":61,"series":1,"stats":{"get_bytes":92710,"get_count":131,"put_bytes":95863,"put_count":145}} +{"i":62,"series":1,"stats":{"get_bytes":85431,"get_count":127,"put_bytes":88385,"put_count":138}} +{"i":63,"series":1,"stats":{"get_bytes":93713,"get_count":134,"put_bytes":96949,"put_count":150}} +{"i":64,"series":1,"stats":{"get_bytes":93582,"get_count":131,"put_bytes":96812,"put_count":146}} +{"i":65,"series":1,"stats":{"get_bytes":86287,"get_count":126,"put_bytes":89721,"put_count":144}} +{"i":66,"series":1,"stats":{"get_bytes":92118,"get_count":128,"put_bytes":95619,"put_count":147}} +{"i":67,"series":1,"stats":{"get_bytes":92875,"get_count":134,"put_bytes":95819,"put_count":145}} +{"i":68,"series":1,"stats":{"get_bytes":94456,"get_count":130,"put_bytes":97610,"put_count":144}} +{"i":69,"series":1,"stats":{"get_bytes":94240,"get_count":131,"put_bytes":97527,"put_count":147}} +{"i":70,"series":1,"stats":{"get_bytes":95340,"get_count":131,"put_bytes":98762,"put_count":149}} +{"i":71,"series":1,"stats":{"get_bytes":91853,"get_count":126,"put_bytes":95355,"put_count":145}} +{"i":72,"series":1,"stats":{"get_bytes":91845,"get_count":131,"put_bytes":95135,"put_count":147}} +{"i":73,"series":1,"stats":{"get_bytes":93444,"get_count":132,"put_bytes":96309,"put_count":142}} +{"i":74,"series":1,"stats":{"get_bytes":90604,"get_count":127,"put_bytes":93967,"put_count":144}} +{"i":75,"series":1,"stats":{"get_bytes":97866,"get_count":134,"put_bytes":101300,"put_count":152}} +{"i":76,"series":1,"stats":{"get_bytes":96072,"get_count":131,"put_bytes":99365,"put_count":147}} +{"i":77,"series":1,"stats":{"get_bytes":93849,"get_count":132,"put_bytes":96580,"put_count":140}} +{"i":78,"series":1,"stats":{"get_bytes":95377,"get_count":130,"put_bytes":98456,"put_count":143}} +{"i":79,"series":1,"stats":{"get_bytes":95274,"get_count":132,"put_bytes":99056,"put_count":155}} +{"i":80,"series":1,"stats":{"get_bytes":96128,"get_count":133,"put_bytes":99555,"put_count":151}} +{"i":81,"series":1,"stats":{"get_bytes":99709,"get_count":138,"put_bytes":103137,"put_count":156}} +{"i":82,"series":1,"stats":{"get_bytes":95831,"get_count":127,"put_bytes":99684,"put_count":151}} +{"i":83,"series":1,"stats":{"get_bytes":92715,"get_count":123,"put_bytes":95865,"put_count":137}} +{"i":84,"series":1,"stats":{"get_bytes":102124,"get_count":134,"put_bytes":105614,"put_count":153}} +{"i":85,"series":1,"stats":{"get_bytes":97091,"get_count":129,"put_bytes":100169,"put_count":142}} +{"i":86,"series":1,"stats":{"get_bytes":99448,"get_count":131,"put_bytes":102601,"put_count":145}} +{"i":87,"series":1,"stats":{"get_bytes":97359,"get_count":129,"put_bytes":100790,"put_count":147}} +{"i":88,"series":1,"stats":{"get_bytes":98257,"get_count":131,"put_bytes":102101,"put_count":155}} +{"i":89,"series":1,"stats":{"get_bytes":101820,"get_count":133,"put_bytes":105733,"put_count":158}} +{"i":90,"series":1,"stats":{"get_bytes":105076,"get_count":135,"put_bytes":108787,"put_count":157}} +{"i":91,"series":1,"stats":{"get_bytes":98922,"get_count":135,"put_bytes":102422,"put_count":154}} +{"i":92,"series":1,"stats":{"get_bytes":104982,"get_count":132,"put_bytes":108833,"put_count":156}} +{"i":93,"series":1,"stats":{"get_bytes":99176,"get_count":130,"put_bytes":103586,"put_count":162}} +{"i":94,"series":1,"stats":{"get_bytes":102213,"get_count":130,"put_bytes":105774,"put_count":150}} +{"i":95,"series":1,"stats":{"get_bytes":102390,"get_count":132,"put_bytes":106245,"put_count":156}} +{"i":96,"series":1,"stats":{"get_bytes":99872,"get_count":134,"put_bytes":102950,"put_count":147}} +{"i":97,"series":1,"stats":{"get_bytes":100940,"get_count":135,"put_bytes":104658,"put_count":157}} +{"i":98,"series":1,"stats":{"get_bytes":99828,"get_count":133,"put_bytes":103321,"put_count":152}} +{"i":99,"series":1,"stats":{"get_bytes":103264,"get_count":135,"put_bytes":106626,"put_count":152}} +{"i":0,"series":2,"stats":{"get_bytes":106237,"get_count":140,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":104104,"get_count":131,"put_bytes":107741,"put_count":152}} +{"i":2,"series":2,"stats":{"get_bytes":103479,"get_count":132,"put_bytes":107466,"put_count":158}} +{"i":3,"series":2,"stats":{"get_bytes":105396,"get_count":133,"put_bytes":109461,"put_count":160}} +{"i":4,"series":2,"stats":{"get_bytes":99286,"get_count":130,"put_bytes":103202,"put_count":155}} +{"i":5,"series":2,"stats":{"get_bytes":104792,"get_count":131,"put_bytes":108853,"put_count":158}} +{"i":6,"series":2,"stats":{"get_bytes":108316,"get_count":133,"put_bytes":112242,"put_count":158}} +{"i":7,"series":2,"stats":{"get_bytes":108222,"get_count":134,"put_bytes":112290,"put_count":161}} +{"i":8,"series":2,"stats":{"get_bytes":110738,"get_count":138,"put_bytes":114657,"put_count":163}} +{"i":9,"series":2,"stats":{"get_bytes":109367,"get_count":134,"put_bytes":113148,"put_count":157}} +{"i":10,"series":2,"stats":{"get_bytes":105071,"get_count":131,"put_bytes":108851,"put_count":154}} +{"i":11,"series":2,"stats":{"get_bytes":108392,"get_count":137,"put_bytes":111961,"put_count":157}} +{"i":12,"series":2,"stats":{"get_bytes":106081,"get_count":130,"put_bytes":109916,"put_count":154}} +{"i":13,"series":2,"stats":{"get_bytes":105853,"get_count":132,"put_bytes":109707,"put_count":156}} +{"i":14,"series":2,"stats":{"get_bytes":109958,"get_count":135,"put_bytes":114002,"put_count":162}} +{"i":15,"series":2,"stats":{"get_bytes":97848,"get_count":127,"put_bytes":101204,"put_count":144}} +{"i":16,"series":2,"stats":{"get_bytes":109556,"get_count":134,"put_bytes":113274,"put_count":156}} +{"i":17,"series":2,"stats":{"get_bytes":110933,"get_count":136,"put_bytes":114499,"put_count":156}} +{"i":18,"series":2,"stats":{"get_bytes":111529,"get_count":135,"put_bytes":115663,"put_count":163}} +{"i":19,"series":2,"stats":{"get_bytes":108148,"get_count":129,"put_bytes":111441,"put_count":145}} +{"i":20,"series":2,"stats":{"get_bytes":108837,"get_count":132,"put_bytes":112684,"put_count":156}} +{"i":21,"series":2,"stats":{"get_bytes":115246,"get_count":139,"put_bytes":119240,"put_count":165}} +{"i":22,"series":2,"stats":{"get_bytes":111964,"get_count":135,"put_bytes":115884,"put_count":160}} +{"i":23,"series":2,"stats":{"get_bytes":109389,"get_count":131,"put_bytes":113101,"put_count":153}} +{"i":24,"series":2,"stats":{"get_bytes":111808,"get_count":131,"put_bytes":115790,"put_count":157}} +{"i":25,"series":2,"stats":{"get_bytes":112928,"get_count":129,"put_bytes":116758,"put_count":153}} +{"i":26,"series":2,"stats":{"get_bytes":114977,"get_count":139,"put_bytes":118483,"put_count":158}} +{"i":27,"series":2,"stats":{"get_bytes":116191,"get_count":138,"put_bytes":119948,"put_count":161}} +{"i":28,"series":2,"stats":{"get_bytes":114316,"get_count":135,"put_bytes":118576,"put_count":165}} +{"i":29,"series":2,"stats":{"get_bytes":107953,"get_count":133,"put_bytes":111461,"put_count":152}} +{"i":30,"series":2,"stats":{"get_bytes":112735,"get_count":136,"put_bytes":116867,"put_count":164}} +{"i":31,"series":2,"stats":{"get_bytes":115493,"get_count":137,"put_bytes":119691,"put_count":166}} +{"i":32,"series":2,"stats":{"get_bytes":116304,"get_count":139,"put_bytes":120783,"put_count":172}} +{"i":33,"series":2,"stats":{"get_bytes":109730,"get_count":131,"put_bytes":113988,"put_count":161}} +{"i":34,"series":2,"stats":{"get_bytes":115868,"get_count":136,"put_bytes":119852,"put_count":162}} +{"i":35,"series":2,"stats":{"get_bytes":117164,"get_count":133,"put_bytes":121206,"put_count":160}} +{"i":36,"series":2,"stats":{"get_bytes":116400,"get_count":134,"put_bytes":121094,"put_count":170}} +{"i":37,"series":2,"stats":{"get_bytes":114775,"get_count":132,"put_bytes":118699,"put_count":157}} +{"i":38,"series":2,"stats":{"get_bytes":112757,"get_count":131,"put_bytes":117388,"put_count":166}} +{"i":39,"series":2,"stats":{"get_bytes":110309,"get_count":131,"put_bytes":113871,"put_count":151}} +{"i":40,"series":2,"stats":{"get_bytes":111342,"get_count":131,"put_bytes":115398,"put_count":158}} +{"i":41,"series":2,"stats":{"get_bytes":115709,"get_count":143,"put_bytes":119558,"put_count":167}} +{"i":42,"series":2,"stats":{"get_bytes":110642,"get_count":127,"put_bytes":114915,"put_count":157}} +{"i":43,"series":2,"stats":{"get_bytes":117673,"get_count":131,"put_bytes":121235,"put_count":151}} +{"i":44,"series":2,"stats":{"get_bytes":117699,"get_count":137,"put_bytes":121481,"put_count":160}} +{"i":45,"series":2,"stats":{"get_bytes":121353,"get_count":142,"put_bytes":125495,"put_count":170}} +{"i":46,"series":2,"stats":{"get_bytes":120186,"get_count":140,"put_bytes":123821,"put_count":161}} +{"i":47,"series":2,"stats":{"get_bytes":118448,"get_count":135,"put_bytes":122925,"put_count":168}} +{"i":48,"series":2,"stats":{"get_bytes":118469,"get_count":137,"put_bytes":122951,"put_count":170}} +{"i":49,"series":2,"stats":{"get_bytes":119761,"get_count":136,"put_bytes":123817,"put_count":163}} +{"i":50,"series":2,"stats":{"get_bytes":117416,"get_count":136,"put_bytes":121201,"put_count":159}} +{"i":51,"series":2,"stats":{"get_bytes":120080,"get_count":139,"put_bytes":123434,"put_count":156}} +{"i":52,"series":2,"stats":{"get_bytes":122196,"get_count":144,"put_bytes":126672,"put_count":177}} +{"i":53,"series":2,"stats":{"get_bytes":115800,"get_count":129,"put_bytes":119785,"put_count":155}} +{"i":54,"series":2,"stats":{"get_bytes":118420,"get_count":142,"put_bytes":122557,"put_count":170}} +{"i":55,"series":2,"stats":{"get_bytes":118031,"get_count":130,"put_bytes":122019,"put_count":156}} +{"i":56,"series":2,"stats":{"get_bytes":120365,"get_count":140,"put_bytes":124774,"put_count":172}} +{"i":57,"series":2,"stats":{"get_bytes":120326,"get_count":139,"put_bytes":124575,"put_count":169}} +{"i":58,"series":2,"stats":{"get_bytes":126829,"get_count":139,"put_bytes":131386,"put_count":173}} +{"i":59,"series":2,"stats":{"get_bytes":125729,"get_count":133,"put_bytes":130832,"put_count":175}} +{"i":60,"series":2,"stats":{"get_bytes":127163,"get_count":136,"put_bytes":131778,"put_count":171}} +{"i":61,"series":2,"stats":{"get_bytes":126622,"get_count":134,"put_bytes":131463,"put_count":172}} +{"i":62,"series":2,"stats":{"get_bytes":120376,"get_count":131,"put_bytes":124722,"put_count":162}} +{"i":63,"series":2,"stats":{"get_bytes":117992,"get_count":138,"put_bytes":122529,"put_count":172}} +{"i":64,"series":2,"stats":{"get_bytes":124680,"get_count":139,"put_bytes":129090,"put_count":171}} +{"i":65,"series":2,"stats":{"get_bytes":125481,"get_count":137,"put_bytes":129961,"put_count":170}} +{"i":66,"series":2,"stats":{"get_bytes":125328,"get_count":138,"put_bytes":129535,"put_count":167}} +{"i":67,"series":2,"stats":{"get_bytes":127745,"get_count":144,"put_bytes":131872,"put_count":172}} +{"i":68,"series":2,"stats":{"get_bytes":128214,"get_count":140,"put_bytes":132474,"put_count":170}} +{"i":69,"series":2,"stats":{"get_bytes":124509,"get_count":140,"put_bytes":129050,"put_count":174}} +{"i":70,"series":2,"stats":{"get_bytes":128341,"get_count":142,"put_bytes":132119,"put_count":165}} +{"i":71,"series":2,"stats":{"get_bytes":125323,"get_count":134,"put_bytes":129588,"put_count":164}} +{"i":72,"series":2,"stats":{"get_bytes":123821,"get_count":135,"put_bytes":128092,"put_count":165}} +{"i":73,"series":2,"stats":{"get_bytes":125335,"get_count":140,"put_bytes":130239,"put_count":179}} +{"i":74,"series":2,"stats":{"get_bytes":122783,"get_count":134,"put_bytes":127269,"put_count":167}} +{"i":75,"series":2,"stats":{"get_bytes":124414,"get_count":141,"put_bytes":128473,"put_count":168}} +{"i":76,"series":2,"stats":{"get_bytes":126788,"get_count":142,"put_bytes":131414,"put_count":177}} +{"i":77,"series":2,"stats":{"get_bytes":131185,"get_count":135,"put_bytes":135654,"put_count":168}} +{"i":78,"series":2,"stats":{"get_bytes":129171,"get_count":140,"put_bytes":133522,"put_count":171}} +{"i":79,"series":2,"stats":{"get_bytes":128734,"get_count":134,"put_bytes":133367,"put_count":169}} +{"i":80,"series":2,"stats":{"get_bytes":131195,"get_count":143,"put_bytes":135879,"put_count":179}} +{"i":81,"series":2,"stats":{"get_bytes":125430,"get_count":137,"put_bytes":129637,"put_count":166}} +{"i":82,"series":2,"stats":{"get_bytes":132465,"get_count":145,"put_bytes":136807,"put_count":176}} +{"i":83,"series":2,"stats":{"get_bytes":129025,"get_count":136,"put_bytes":133934,"put_count":175}} +{"i":84,"series":2,"stats":{"get_bytes":128066,"get_count":136,"put_bytes":132249,"put_count":165}} +{"i":85,"series":2,"stats":{"get_bytes":128611,"get_count":137,"put_bytes":133094,"put_count":170}} +{"i":86,"series":2,"stats":{"get_bytes":125953,"get_count":142,"put_bytes":130583,"put_count":177}} +{"i":87,"series":2,"stats":{"get_bytes":134276,"get_count":140,"put_bytes":138836,"put_count":174}} +{"i":88,"series":2,"stats":{"get_bytes":128599,"get_count":138,"put_bytes":133003,"put_count":170}} +{"i":89,"series":2,"stats":{"get_bytes":126078,"get_count":140,"put_bytes":130147,"put_count":167}} +{"i":90,"series":2,"stats":{"get_bytes":127884,"get_count":141,"put_bytes":131944,"put_count":168}} +{"i":91,"series":2,"stats":{"get_bytes":134233,"get_count":143,"put_bytes":138244,"put_count":170}} +{"i":92,"series":2,"stats":{"get_bytes":128689,"get_count":140,"put_bytes":133010,"put_count":171}} +{"i":93,"series":2,"stats":{"get_bytes":129812,"get_count":140,"put_bytes":134679,"put_count":179}} +{"i":94,"series":2,"stats":{"get_bytes":132369,"get_count":143,"put_bytes":136568,"put_count":172}} +{"i":95,"series":2,"stats":{"get_bytes":129756,"get_count":142,"put_bytes":133954,"put_count":171}} +{"i":96,"series":2,"stats":{"get_bytes":133823,"get_count":142,"put_bytes":138356,"put_count":176}} +{"i":97,"series":2,"stats":{"get_bytes":133390,"get_count":141,"put_bytes":137948,"put_count":175}} +{"i":98,"series":2,"stats":{"get_bytes":136765,"get_count":146,"put_bytes":140620,"put_count":170}} +{"i":99,"series":2,"stats":{"get_bytes":126420,"get_count":139,"put_bytes":130901,"put_count":172}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.png b/actors/evm/tests/measurements/mapping_add_n100.png index c0006af9d4f96f4b26936be6fd071651681dcb09..a0549cdb669f5de4c484b0f97ec745e6cd7c59a4 100644 GIT binary patch literal 22174 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 literal 22313 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

##awwH)1e!#O10^>PtMr1J^CH@b^Y3&0`Zg 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 z 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 From 0fe4a69a22014f8b99c62161111fd757c395b0dd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 8 Dec 2022 12:03:50 -0800 Subject: [PATCH 176/339] fix: correctly handle "0" in EVM call gas (#928) 1. Update the FVM's SDK to the version that treats 0 as 0 instead of infinity. 2. Correctly set the gas limit to infinity if we're demoting a call to METHOD_SEND. 3. If the user passes 0 gas in the EVM, leave it at 0 (except when demoting to METHOD_SEND). --- Cargo.lock | 4 +-- actors/embryo/Cargo.toml | 2 +- .../evm/src/interpreter/instructions/call.rs | 34 +++++++++++-------- actors/evm/src/interpreter/precompiles/evm.rs | 2 +- actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- actors/evm/src/interpreter/precompiles/mod.rs | 2 +- actors/evm/tests/call.rs | 3 -- runtime/Cargo.toml | 2 +- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd76e2a74..1692f59af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2167,9 +2167,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.16" +version = "3.0.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7016663436cc9e98b68bbdee40a2a333efa22eb984a1d2099a51d73b9cb2fd7a" +checksum = "5d6f88e45b7984efbc2add1c98ca24155a454a21513d4c1d9ee40a1a109d4181" dependencies = [ "cid 0.8.6", "fvm_ipld_encoding", diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index a32c68132..b24f41628 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.16", optional = true } +fvm_sdk = { version = "3.0.0-alpha.17", optional = true } fvm_shared = { version = "3.0.0-alpha.14", optional = true } [features] diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index d78a14c6c..391d64046 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -234,24 +234,32 @@ pub fn call_generic( // send value in read-only mode anyways. Ok(RawBytes::default()) } else { - let method = if !actor_exists + let (method, gas_limit) = if !actor_exists || matches!(target_actor_type, Some(Type::Embryo | Type::Account)) // See https://github.com/filecoin-project/ref-fvm/issues/980 for this // hocus pocus || (input_data.is_empty() && ((gas == 0 && value > 0) || (gas == 2300 && value == 0))) { - // If the target actor doesn't exist or is an account or an embryo, - // switch to a basic "send" so the call will still work even if the - // target actor would reject a normal ethereum call. - METHOD_SEND + // We switch to a bare send when: + // + // 1. The target is an embryo/account or doesn't exist. Otherwise, + // sendign funds to an account/embryo would fail when we try to call + // InvokeContract. + // 2. The gas wouldn't let code execute anyways. This lets us support + // solidity's "transfer" method. + // + // At the same time, we ignore the supplied gas value and set it to + // infinity as user code won't execute anyways. The only code that might + // run is related to account creation, which doesn't count against this + // gas limit in the EVM anyways. + (METHOD_SEND, None) } else { // Otherwise, invoke normally. - Method::InvokeContract as u64 + (Method::InvokeContract as u64, Some(effective_gas_limit(system, gas))) }; // TODO: support IPLD codecs #758 let params = RawBytes::serialize(BytesSer(input_data))?; let value = TokenAmount::from(&value); - let gas_limit = effective_gas_limit(system, gas); let send_flags = if kind == CallKind::StaticCall { SendFlags::READ_ONLY } else { @@ -274,7 +282,7 @@ pub fn call_generic( Method::InvokeContractDelegate as u64, RawBytes::serialize(¶ms)?, TokenAmount::from(&value), - effective_gas_limit(system, gas), + Some(effective_gas_limit(system, gas)), SendFlags::default(), ) } @@ -316,12 +324,8 @@ pub fn call_generic( Ok(U256::from(call_result)) } -fn effective_gas_limit(system: &System, gas: U256) -> Option { +fn effective_gas_limit(system: &System, gas: U256) -> u64 { let gas_rsvp = (63 * system.rt.gas_available()) / 64; - Some(if gas.is_zero() { - gas_rsvp - } else { - let gas = gas.to_u64_saturating(); - std::cmp::min(gas, gas_rsvp) - }) + let gas = gas.to_u64_saturating(); + std::cmp::min(gas, gas_rsvp) } diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 4e04537c3..10d5abd10 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -334,7 +334,7 @@ mod tests { impl Default for PrecompileContext { fn default() -> Self { - Self { call_type: CallKind::Call, gas_limit: None } + Self { call_type: CallKind::Call, gas_limit: u64::MAX } } } diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 1d6f6c923..cfbac2cb5 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -207,7 +207,7 @@ pub(super) fn call_actor( method, RawBytes::from(input_data.to_vec()), TokenAmount::from(&value), - ctx.gas_limit, + Some(ctx.gas_limit), flags, ) }; diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 1543e4c83..3d72a805c 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -105,7 +105,7 @@ impl From for StatusCode { #[derive(Debug, PartialEq, Eq)] pub struct PrecompileContext { pub call_type: CallKind, - pub gas_limit: Option, + pub gas_limit: u64, } /// Native Type of a given contract diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index eae92937a..b39fc8905 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -251,7 +251,6 @@ fn test_call_convert_to_send() { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; - rt.expect_gas_available(10_000_000_000u64); rt.expect_send( target, METHOD_SEND, @@ -293,7 +292,6 @@ fn test_call_convert_to_send2() { // we don't expected return data let return_data = vec![]; - rt.expect_gas_available(10_000_000_000u64); rt.expect_send( target, METHOD_SEND, @@ -334,7 +332,6 @@ fn test_call_convert_to_send3() { // we don't expected return data let return_data = vec![]; - rt.expect_gas_available(10_000_000_000u64); rt.expect_send( target, METHOD_SEND, diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 4d9599db5..b933e54d0 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.16", optional = true } +fvm_sdk = { version = "3.0.0-alpha.17", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } From 077d1873f05946f9b72fa7fd8e3c39b87fb59a87 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 8 Dec 2022 12:42:53 -0800 Subject: [PATCH 177/339] feat: refactor sends to use the unified function (#923) This removes the send_read_only variant and makes the simplified `send` variant just call `send_generalized. --- .../evm/src/interpreter/instructions/call.rs | 6 +- .../src/interpreter/instructions/lifecycle.rs | 3 +- actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- actors/evm/src/interpreter/system.rs | 27 +-- actors/evm/tests/call.rs | 15 +- runtime/src/runtime/fvm.rs | 157 +++++++----------- runtime/src/runtime/mod.rs | 18 +- runtime/src/test_utils.rs | 151 +++++++---------- test_vm/src/lib.rs | 97 ++++------- 9 files changed, 174 insertions(+), 302 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 391d64046..0038f20d7 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -265,9 +265,7 @@ pub fn call_generic( } else { SendFlags::default() }; - system.send_generalized( - &dst_addr, method, params, value, gas_limit, send_flags, - ) + system.send(&dst_addr, method, params, value, gas_limit, send_flags) } } CallKind::DelegateCall => match get_cid_type(system.rt, dst) { @@ -277,7 +275,7 @@ pub fn call_generic( // and then invoke self with delegate; readonly context is sticky let params = DelegateCallParams { code, input: input_data.into() }; - system.send_generalized( + system.send( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, RawBytes::serialize(¶ms)?, diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 2c6c8662a..34f1c58d5 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -2,6 +2,7 @@ use bytes::Bytes; use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, EAM_ACTOR_ADDR}; use fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}; +use fvm_shared::sys::SendFlags; use fvm_shared::MethodNum; use fvm_shared::METHOD_SEND; use fvm_shared::{address::Address, econ::TokenAmount}; @@ -118,7 +119,7 @@ fn create_init( value: TokenAmount, ) -> Result { // send bytecode & params to EAM to generate the address and contract - let ret = system.send(&EAM_ACTOR_ADDR, method, params, value); + let ret = system.send(&EAM_ACTOR_ADDR, method, params, value, None, SendFlags::default()); // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 // Normally EVM will do some checks here to ensure that a contract has the capability diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index cfbac2cb5..f81f8dc90 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -202,7 +202,7 @@ pub(super) fn call_actor( let address = &bytes[send_data_size..send_data_size + address_size]; let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; - system.send_generalized( + system.send( &address, method, RawBytes::from(input_data.to_vec()), diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 0e9aa337d..905a8e46a 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -146,33 +146,8 @@ impl<'r, RT: Runtime> System<'r, RT> { nonce } - /// Send a message, saving and reloading state as necessary. - pub fn send( - &mut self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - ) -> Result { - self.flush()?; - let result = self.rt.send(to, method, params, value)?; - self.reload()?; - Ok(result) - } - - /// Send a message in "read-only" mode (for staticcall). - pub fn send_read_only( - &mut self, - to: &Address, - method: MethodNum, - params: RawBytes, - ) -> Result { - self.flush()?; - self.rt.send_read_only(to, method, params) - } - /// Generalized send - pub fn send_generalized( + pub fn send( &mut self, to: &Address, method: MethodNum, diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index b39fc8905..2fc3e3063 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -44,7 +44,7 @@ push1 0x00 push1 0x00 calldataload # gas -push1 0x00 +push4 0xffffffff # do the call call @@ -183,11 +183,13 @@ fn test_call() { return_data[31] = 0x42; rt.expect_gas_available(10_000_000_000u64); - rt.expect_send( + rt.expect_send_generalized( f4_target, evm::Method::InvokeContract as u64, proxy_call_input_data, TokenAmount::zero(), + Some(0xffffffff), + SendFlags::empty(), RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), ExitCode::OK, ); @@ -251,11 +253,13 @@ fn test_call_convert_to_send() { let mut return_data = vec![0u8; 32]; return_data[31] = 0x42; - rt.expect_send( + rt.expect_send_generalized( target, METHOD_SEND, proxy_call_input_data, TokenAmount::zero(), + None, + SendFlags::empty(), RawBytes::serialize(BytesSer(&return_data)).expect("failed to serialize return data"), ExitCode::OK, ); @@ -400,7 +404,7 @@ push1 0x00 push1 0x0e # gas -push1 0x00 +push4 0xffffffff # call_actor must be from delegatecall delegatecall @@ -483,9 +487,10 @@ fn test_callactor_inner(exit_code: ExitCode) { 0x42, proxy_call_input_data, TokenAmount::zero(), + Some(0xffffffff), + send_flags, RawBytes::from(return_data), exit_code, - send_flags, ); // invoke diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 957808f11..e266d106a 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -1,7 +1,6 @@ use anyhow::{anyhow, Error}; use cid::multihash::Code; use cid::Cid; -use fvm::SyscallResult; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; @@ -17,7 +16,6 @@ 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::receipt::Receipt; use fvm_shared::sector::{ AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo, @@ -102,68 +100,6 @@ impl MessageInfo for FvmMessage { } } -fn handle_send_result( - to: &Address, - method: MethodNum, - res: SyscallResult, -) -> Result { - match res { - Ok(ret) => { - if ret.exit_code.is_success() { - Ok(ret.return_data) - } else { - // The returned code can't be simply propagated as it may be a system exit code. - // TODO: improve propagation once we return a RuntimeError. - // Ref https://github.com/filecoin-project/builtin-actors/issues/144 - let exit_code = match ret.exit_code { - // This means the called actor did something wrong. We can't "make up" a - // reasonable exit code. - ExitCode::SYS_MISSING_RETURN - | ExitCode::SYS_ILLEGAL_INSTRUCTION - | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, - // We don't expect any other system errors. - code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, - // Otherwise, pass it through. - code => code, - }; - Err(ActorError::unchecked_with_data( - exit_code, - format!("send to {} method {} aborted with code {}", to, method, ret.exit_code), - ret.return_data, - )) - } - } - Err(err) => Err(match err { - // Some of these errors are from operations in the Runtime or SDK layer - // before or after the underlying VM send syscall. - ErrorNumber::NotFound => { - // This means that the receiving actor doesn't exist. - // TODO: we can't reasonably determine the correct "exit code" here. - actor_error!(unspecified; "receiver not found") - } - ErrorNumber::InsufficientFunds => { - // This means that the send failed because we have insufficient funds. We will - // get a _syscall error_, not an exit code, because the target actor will not - // run (and therefore will not exit). - actor_error!(insufficient_funds; "not enough funds") - } - ErrorNumber::LimitExceeded => { - // This means we've exceeded the recursion limit. - // TODO: Define a better exit code. - actor_error!(assertion_failed; "recursion limit exceeded") - } - ErrorNumber::ReadOnly => ActorError::unchecked( - ExitCode::USR_READ_ONLY, - "attempted to mutate state while in readonly mode".into(), - ), - err => { - // We don't expect any other syscall exit codes. - actor_error!(assertion_failed; "unexpected error: {}", err) - } - }), - } -} - impl Runtime for FvmRuntime where B: Blockstore, @@ -361,39 +297,6 @@ where &self.blockstore } - fn send( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - ) -> Result { - if self.in_transaction { - return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); - } - handle_send_result( - to, - method, - fvm::send::send(to, method, params, value, None, SendFlags::default()), - ) - } - - fn send_read_only( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - ) -> Result { - if self.in_transaction { - return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); - } - handle_send_result( - to, - method, - fvm::send::send(to, method, params, TokenAmount::default(), None, SendFlags::READ_ONLY), - ) - } - fn send_generalized( &self, to: &Address, @@ -406,7 +309,65 @@ where if self.in_transaction { return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); } - handle_send_result(to, method, fvm::send::send(to, method, params, value, gas_limit, flags)) + + match fvm::send::send(to, method, params, value, gas_limit, flags) { + Ok(ret) => { + if ret.exit_code.is_success() { + Ok(ret.return_data) + } else { + // The returned code can't be simply propagated as it may be a system exit code. + // TODO: improve propagation once we return a RuntimeError. + // Ref https://github.com/filecoin-project/builtin-actors/issues/144 + let exit_code = match ret.exit_code { + // This means the called actor did something wrong. We can't "make up" a + // reasonable exit code. + ExitCode::SYS_MISSING_RETURN + | ExitCode::SYS_ILLEGAL_INSTRUCTION + | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, + // We don't expect any other system errors. + code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, + // Otherwise, pass it through. + code => code, + }; + Err(ActorError::unchecked_with_data( + exit_code, + format!( + "send to {} method {} aborted with code {}", + to, method, ret.exit_code + ), + ret.return_data, + )) + } + } + Err(err) => Err(match err { + // Some of these errors are from operations in the Runtime or SDK layer + // before or after the underlying VM send syscall. + ErrorNumber::NotFound => { + // This means that the receiving actor doesn't exist. + // TODO: we can't reasonably determine the correct "exit code" here. + actor_error!(unspecified; "receiver not found") + } + ErrorNumber::InsufficientFunds => { + // This means that the send failed because we have insufficient funds. We will + // get a _syscall error_, not an exit code, because the target actor will not + // run (and therefore will not exit). + actor_error!(insufficient_funds; "not enough funds") + } + ErrorNumber::LimitExceeded => { + // This means we've exceeded the recursion limit. + // TODO: Define a better exit code. + actor_error!(assertion_failed; "recursion limit exceeded") + } + ErrorNumber::ReadOnly => ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "attempted to mutate state while in readonly mode".into(), + ), + err => { + // We don't expect any other syscall exit codes. + actor_error!(assertion_failed; "unexpected error: {}", err) + } + }), + } } fn new_actor_address(&mut self) -> Result { diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index cc01e0eaa..c60ed9d63 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -188,21 +188,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { method: MethodNum, params: RawBytes, value: TokenAmount, - ) -> Result; - - /// Like [`Runtime::send`] except that neither the called actor nor any recursively called actor - /// can make any state changes or emit any events. Specifically: - /// - /// - Value transfers are rejected. - /// - Events are discarded. - /// - State changes are rejected when the called actor attempts to update its state-root. - /// - Actor deletion is forbidden. - fn send_read_only( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - ) -> Result; + ) -> Result { + self.send_generalized(to, method, params, value, None, SendFlags::empty()) + } /// Generailizes [`Runtime::send`] and [`Runtime::send_read_only`] to allow the caller to /// specify a gas limit and send flags. diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 6135b6364..c09a3f4b0 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -368,6 +368,7 @@ pub struct ExpectedMessage { pub method: MethodNum, pub params: RawBytes, pub value: TokenAmount, + pub gas_limit: Option, pub send_flags: SendFlags, // returns from applying expectedMessage @@ -540,72 +541,6 @@ impl MockRuntime { self.delegated_addresses_source.insert(target, source); } - fn send_inner( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - _gas_limit: Option, - send_flags: SendFlags, - ) -> Result { - // TODO gas_limit is currently ignored, what should we do about it? - self.require_in_call(); - if self.in_transaction { - return Err(actor_error!(assertion_failed; "side-effect within transaction")); - } - - assert!( - !self.expectations.borrow_mut().expect_sends.is_empty(), - "unexpected message to: {:?} method: {:?}, value: {:?}, params: {:?}", - to, - method, - value, - params - ); - - let expected_msg = self.expectations.borrow_mut().expect_sends.pop_front().unwrap(); - - assert!( - expected_msg.to == *to - && expected_msg.method == method - && expected_msg.params == params - && expected_msg.value == value - && expected_msg.send_flags == send_flags, - "message sent does not match expectation.\n\ - message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ - expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", - to, - method, - value, - params, - expected_msg.to, - expected_msg.method, - expected_msg.value, - expected_msg.params, - ); - - { - let mut balance = self.balance.borrow_mut(); - if value > *balance { - return Err(ActorError::unchecked( - ExitCode::SYS_SENDER_STATE_INVALID, - format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance), - )); - } - *balance -= value; - } - - match expected_msg.exit_code { - ExitCode::OK => Ok(expected_msg.send_return), - x => Err(ActorError::unchecked_with_data( - x, - "Expected message Fail".to_string(), - expected_msg.send_return, - )), - } - } - pub fn call( &mut self, method_num: MethodNum, @@ -737,6 +672,7 @@ impl MockRuntime { send_return, exit_code, send_flags: SendFlags::default(), + gas_limit: None, }) } @@ -748,9 +684,10 @@ impl MockRuntime { method: MethodNum, params: RawBytes, value: TokenAmount, + gas_limit: Option, + send_flags: SendFlags, send_return: RawBytes, exit_code: ExitCode, - send_flags: SendFlags, ) { self.expectations.borrow_mut().expect_sends.push_back(ExpectedMessage { to, @@ -760,6 +697,7 @@ impl MockRuntime { exit_code, value, send_flags, + gas_limit, }) } @@ -1166,25 +1104,6 @@ impl Runtime for MockRuntime { &self.store } - fn send( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - ) -> Result { - self.send_inner(to, method, params, value, None, SendFlags::default()) - } - - fn send_read_only( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - ) -> Result { - self.send_inner(to, method, params, TokenAmount::default(), None, SendFlags::READ_ONLY) - } - fn send_generalized( &self, to: &Address, @@ -1192,9 +1111,65 @@ impl Runtime for MockRuntime { params: RawBytes, value: TokenAmount, gas_limit: Option, - flags: SendFlags, + send_flags: SendFlags, ) -> Result { - self.send_inner(to, method, params, value, gas_limit, flags) + // TODO gas_limit is currently ignored, what should we do about it? + self.require_in_call(); + if self.in_transaction { + return Err(actor_error!(assertion_failed; "side-effect within transaction")); + } + + assert!( + !self.expectations.borrow_mut().expect_sends.is_empty(), + "unexpected message to: {:?} method: {:?}, value: {:?}, params: {:?}", + to, + method, + value, + params + ); + + let expected_msg = self.expectations.borrow_mut().expect_sends.pop_front().unwrap(); + + assert_eq!(expected_msg.gas_limit, gas_limit, "gas limit did not match expectation"); + assert_eq!(expected_msg.send_flags, send_flags, "send flags did not match expectation"); + + assert!( + expected_msg.to == *to + && expected_msg.method == method + && expected_msg.params == params + && expected_msg.value == value, + "message sent does not match expectation.\n\ + message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ + expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", + to, + method, + value, + params, + expected_msg.to, + expected_msg.method, + expected_msg.value, + expected_msg.params, + ); + + { + let mut balance = self.balance.borrow_mut(); + if value > *balance { + return Err(ActorError::unchecked( + ExitCode::SYS_SENDER_STATE_INVALID, + format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance), + )); + } + *balance -= value; + } + + match expected_msg.exit_code { + ExitCode::OK => Ok(expected_msg.send_return), + x => Err(ActorError::unchecked_with_data( + x, + "Expected message Fail".to_string(), + expected_msg.send_return, + )), + } } fn new_actor_address(&mut self) -> Result { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 91ab12b95..6f20bc315 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -789,49 +789,6 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { res } - - fn send_inner( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - _gas_limit: Option, - mut send_flags: SendFlags, - ) -> Result { - // replicate FVM by silently propagating read only flag to subcalls - if self.read_only() { - send_flags.set(SendFlags::READ_ONLY, true) - } - - // TODO gas_limit is current ignored, what should we do about it? - if !self.allow_side_effects { - return Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, - "Calling send is not allowed during side-effect lock".to_string(), - )); - } - - let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; - let mut new_ctx = InvocationCtx { - v: self.v, - top: self.top.clone(), - msg: new_actor_msg, - allow_side_effects: true, - caller_validated: false, - policy: self.policy, - subinvocations: RefCell::new(vec![]), - actor_exit: RefCell::new(None), - read_only: send_flags.read_only(), - }; - let res = new_ctx.invoke_actor(); - let invoc = new_ctx.gather_trace(res.clone()); - RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { - subinvocs.push(invoc); - subinvocs - }); - res - } } impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { @@ -1012,35 +969,47 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { self.v.get_actor(Address::new_id(id)).and_then(|act| act.predictable_address) } - fn send( + fn send_generalized( &self, to: &Address, method: MethodNum, params: RawBytes, value: TokenAmount, + _gas_limit: Option, + mut send_flags: SendFlags, ) -> Result { - self.send_inner(to, method, params, value, None, SendFlags::default()) - } + // replicate FVM by silently propagating read only flag to subcalls + if self.read_only() { + send_flags.set(SendFlags::READ_ONLY, true) + } - fn send_read_only( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - ) -> Result { - self.send_inner(to, method, params, TokenAmount::zero(), None, SendFlags::READ_ONLY) - } + // TODO gas_limit is current ignored, what should we do about it? + if !self.allow_side_effects { + return Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "Calling send is not allowed during side-effect lock".to_string(), + )); + } - fn send_generalized( - &self, - to: &Address, - method: MethodNum, - params: RawBytes, - value: TokenAmount, - gas_limit: Option, - flags: SendFlags, - ) -> Result { - self.send_inner(to, method, params, value, gas_limit, flags) + let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; + let mut new_ctx = InvocationCtx { + v: self.v, + top: self.top.clone(), + msg: new_actor_msg, + allow_side_effects: true, + caller_validated: false, + policy: self.policy, + subinvocations: RefCell::new(vec![]), + actor_exit: RefCell::new(None), + read_only: send_flags.read_only(), + }; + let res = new_ctx.invoke_actor(); + let invoc = new_ctx.gather_trace(res.clone()); + RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { + subinvocs.push(invoc); + subinvocs + }); + res } fn get_randomness_from_tickets( From 36810e96e3112d6abbdc51d78c12869478842b9f Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Mon, 7 Nov 2022 01:08:56 +1100 Subject: [PATCH 178/339] Proof of concept exported API for Account actor (#797) --- actors/account/src/lib.rs | 7 ++- actors/account/tests/account_actor_test.rs | 73 +++++++++++++--------- runtime/src/builtin/shared.rs | 36 ++++++++++- runtime/src/test_utils.rs | 26 ++++---- test_vm/tests/test_vm_test.rs | 15 +++-- 5 files changed, 107 insertions(+), 50 deletions(-) diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index 25a630dcc..2f27a5208 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -12,7 +12,7 @@ use num_traits::FromPrimitive; use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorError}; +use fil_actors_runtime::{actor_error, restrict_internal_api, ActorError}; use fil_actors_runtime::{cbor, ActorDowncast}; use crate::types::AuthenticateMessageParams; @@ -33,6 +33,7 @@ pub enum Method { Constructor = METHOD_CONSTRUCTOR, PubkeyAddress = 2, AuthenticateMessage = 3, + AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -109,6 +110,8 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; + match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -118,7 +121,7 @@ impl ActorCode for Actor { let addr = Self::pubkey_address(rt)?; Ok(RawBytes::serialize(addr)?) } - Some(Method::AuthenticateMessage) => { + Some(Method::AuthenticateMessage) | Some(Method::AuthenticateMessageExported) => { Self::authenticate_message(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index a437ca7c5..4f9bdf3a0 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -16,12 +16,8 @@ use fil_actors_runtime::test_utils::*; #[test] fn construction() { fn construct(addr: Address, exit_code: ExitCode) { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); if exit_code.is_success() { @@ -59,12 +55,8 @@ fn construction() { #[test] fn token_receiver() { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let param = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(); @@ -74,6 +66,7 @@ fn token_receiver() { ) .unwrap(); + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); rt.expect_validate_caller_any(); let ret = rt.call::( Method::UniversalReceiverHook as MethodNum, @@ -83,25 +76,15 @@ fn token_receiver() { assert_eq!(RawBytes::default(), ret.unwrap()); } -fn check_state(rt: &MockRuntime) { - let test_address = Address::new_id(1000); - let (_, acc) = check_state_invariants(&rt.get_state(), &test_address); - acc.assert_empty(); -} - #[test] fn authenticate_message() { - let mut rt = MockRuntime { - receiver: Address::new_id(100), - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() - }; + let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); let addr = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); - - rt.call::(1, &RawBytes::serialize(addr).unwrap()).unwrap(); + rt.call::(Method::Constructor as MethodNum, &RawBytes::serialize(addr).unwrap()) + .unwrap(); let state: State = rt.get_state(); assert_eq!(state.address, addr); @@ -110,6 +93,7 @@ fn authenticate_message() { RawBytes::serialize(AuthenticateMessageParams { signature: vec![], message: vec![] }) .unwrap(); + // Valid signature rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { sig: Signature::new_secp256k1(vec![]), @@ -117,8 +101,13 @@ fn authenticate_message() { plaintext: vec![], result: Ok(()), }); - assert_eq!(RawBytes::default(), rt.call::(3, ¶ms).unwrap()); + assert_eq!( + RawBytes::default(), + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms).unwrap() + ); + rt.verify(); + // Invalid signature rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { sig: Signature::new_secp256k1(vec![]), @@ -126,10 +115,34 @@ fn authenticate_message() { plaintext: vec![], result: Err(anyhow!("bad signature")), }); - assert_eq!( + expect_abort_contains_message( ExitCode::USR_ILLEGAL_ARGUMENT, - rt.call::(3, ¶ms).unwrap_err().exit_code() + "bad signature", + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), ); - rt.verify(); + + // Invalid caller of internal method number + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), + ); + + // Ok to call exported method number + rt.expect_validate_caller_any(); + rt.expect_verify_signature(ExpectedVerifySig { + sig: Signature::new_secp256k1(vec![]), + signer: addr, + plaintext: vec![], + result: Ok(()), + }); + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms).unwrap(); +} + +fn check_state(rt: &MockRuntime) { + let test_address = Address::new_id(1000); + let (_, acc) = check_state_invariants(&rt.get_state(), &test_address); + acc.assert_empty(); } diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 5abc54a2c..aea1180c0 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -3,8 +3,8 @@ use crate::{actor_error, ActorContext, ActorError}; use fvm_shared::address::Address; -use fvm_shared::ActorID; use fvm_shared::METHOD_SEND; +use fvm_shared::{ActorID, MethodNum}; use crate::runtime::builtins::Type; use crate::runtime::Runtime; @@ -38,3 +38,37 @@ pub fn resolve_to_actor_id( Err(actor_error!(illegal_argument, "failed to resolve or initialize address {}", address)) } + +// The lowest FRC-42 method number. +pub const FIRST_EXPORTED_METHOD_NUMBER: MethodNum = 1 << 24; + +// Checks whether the caller is allowed to invoke some method number. +// All method numbers below the FRC-42 range are restricted to built-in actors +// (including the account and multisig actors). +// Methods may subsequently enforce tighter restrictions. +pub fn restrict_internal_api(rt: &mut RT, method: MethodNum) -> Result<(), ActorError> +where + RT: Runtime, +{ + if method >= FIRST_EXPORTED_METHOD_NUMBER { + return Ok(()); + } + let caller = rt.message().caller(); + let code_cid = rt.get_actor_code_cid(&caller.id().unwrap()); + match code_cid { + None => { + return Err( + actor_error!(forbidden; "no code for caller {} of method {}", caller, method), + ) + } + 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), + ); + } + } + } + Ok(()) +} diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 8ba043d4c..80cf773c2 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -42,18 +42,18 @@ use crate::runtime::{ use crate::{actor_error, ActorError}; lazy_static::lazy_static! { - pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system"); - pub static ref INIT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/init"); - pub static ref CRON_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/cron"); - pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/account"); - pub static ref POWER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagepower"); - pub static ref MINER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storageminer"); - pub static ref MARKET_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagemarket"); - pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/paymentchannel"); - pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/multisig"); - pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward"); - pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry"); - pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/datacap"); + pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/system"); + pub static ref INIT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/init"); + pub static ref CRON_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/cron"); + pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/account"); + pub static ref POWER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagepower"); + pub static ref MINER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storageminer"); + pub static ref MARKET_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagemarket"); + pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/paymentchannel"); + pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/multisig"); + pub static ref REWARD_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/reward"); + 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 ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System); @@ -99,7 +99,7 @@ lazy_static::lazy_static! { const IPLD_RAW: u64 = 0x55; /// Returns an identity CID for bz. -pub fn make_builtin(bz: &[u8]) -> Cid { +pub fn make_identity_cid(bz: &[u8]) -> Cid { Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long")) } diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 1e17fe49c..760f4340b 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -1,5 +1,7 @@ use fil_actor_account::State as AccountState; -use fil_actors_runtime::test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID}; +use fil_actors_runtime::test_utils::{ + make_identity_cid, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, +}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -18,14 +20,19 @@ fn state_control() { let addr2 = Address::new_id(2222); // set actor - let a1 = - actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from_atto(10u8)); + let a1 = actor( + *ACCOUNT_ACTOR_CODE_ID, + make_identity_cid(b"a1-head"), + 42, + TokenAmount::from_atto(10u8), + ); v.set_actor(addr1, a1.clone()); let out = v.get_actor(addr1).unwrap(); assert_eq!(out, a1); let check = v.checkpoint(); - let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from_atto(1u8)); + let a2 = + actor(*PAYCH_ACTOR_CODE_ID, make_identity_cid(b"a2-head"), 88, TokenAmount::from_atto(1u8)); v.set_actor(addr2, a2.clone()); assert_eq!(v.get_actor(addr2).unwrap(), a2); // rollback removes a2 but not a1 From 0f3d72f710042a2105cdb2cc7468414e0337122b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 7 Nov 2022 14:49:19 +0000 Subject: [PATCH 179/339] Export stable methods for public access (#807) * Export Datacap Actor methods * Export Init Actor methods * Export Market Actor methods * Export Miner Actor methods * Export Multisig Actor methods * Export Verifreg Actor methods * Address review --- Cargo.lock | 3 + actors/datacap/src/lib.rs | 48 +++++++---- actors/datacap/tests/datacap_actor_test.rs | 39 ++++++++- actors/datacap/tests/harness/mod.rs | 1 + actors/init/Cargo.toml | 1 + actors/init/src/lib.rs | 9 ++- actors/init/tests/init_actor_test.rs | 63 ++++++++++++++- actors/market/Cargo.toml | 1 + actors/market/src/lib.rs | 15 ++-- actors/market/tests/harness.rs | 1 + actors/market/tests/market_actor_test.rs | 49 ++++++++++- actors/miner/Cargo.toml | 1 + actors/miner/src/lib.rs | 41 ++++++---- actors/miner/tests/change_beneficiary_test.rs | 36 ++++++++- actors/miner/tests/declare_recoveries.rs | 2 +- .../tests/miner_actor_test_commitment.rs | 2 +- .../tests/miner_actor_test_construction.rs | 7 ++ actors/miner/tests/report_consensus_fault.rs | 10 +-- actors/multisig/src/lib.rs | 32 +++++--- actors/multisig/tests/multisig_actor_test.rs | 81 ++++++++++++++++--- actors/verifreg/src/lib.rs | 18 +++-- actors/verifreg/tests/harness/mod.rs | 1 + actors/verifreg/tests/verifreg_actor_test.rs | 63 ++++++++++++++- runtime/src/builtin/shared.rs | 2 +- runtime/src/test_utils.rs | 13 +-- 25 files changed, 445 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a0c6b556..2a91ab1d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,6 +619,7 @@ dependencies = [ "anyhow", "cid", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", @@ -639,6 +640,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_verifreg", "fil_actors_runtime", + "frc42_dispatch", "frc46_token", "fvm_ipld_amt", "fvm_ipld_bitfield", @@ -669,6 +671,7 @@ dependencies = [ "fil_actor_power", "fil_actor_reward", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index 68c68081f..e824980ad 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -20,7 +20,8 @@ use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorError, AsActorError, + SYSTEM_ACTOR_ADDR, }; pub use self::state::State; @@ -42,9 +43,10 @@ lazy_static! { * BigInt::from(1_000_000_000_000_000_000_000_i128) ); } -/// Static method numbers for builtin-actor private dispatch. -/// The methods are also expected to be exposed via FRC-XXXX standard calling convention, -/// with numbers determined by name. + +/// Datacap actor methods available +/// Some methods are available under 2 method nums -- a static number for "private" builtin actor usage, +/// and via FRC-XXXX calling convention, with number determined by method name. #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -65,6 +67,19 @@ pub enum Method { Burn = 19, BurnFrom = 20, Allowance = 21, + // Method numbers derived from FRC-XXXX standards + NameExported = frc42_dispatch::method_hash!("Name"), + SymbolExported = frc42_dispatch::method_hash!("Symbol"), + TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), + BalanceOfExported = frc42_dispatch::method_hash!("BalanceOf"), + TransferExported = frc42_dispatch::method_hash!("Transfer"), + TransferFromExported = frc42_dispatch::method_hash!("TransferFrom"), + IncreaseAllowanceExported = frc42_dispatch::method_hash!("IncreaseAllowance"), + DecreaseAllowanceExported = frc42_dispatch::method_hash!("DecreaseAllowance"), + RevokeAllowanceExported = frc42_dispatch::method_hash!("RevokeAllowance"), + BurnExported = frc42_dispatch::method_hash!("Burn"), + BurnFromExported = frc42_dispatch::method_hash!("BurnFrom"), + AllowanceExported = frc42_dispatch::method_hash!("Allowance"), } pub struct Actor; @@ -448,6 +463,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; // I'm trying to find a fixed template for these blocks so we can macro it. // Current blockers: // - the serialize method maps () to CBOR null (we want no bytes instead) @@ -465,51 +481,51 @@ impl ActorCode for Actor { let ret = Self::destroy(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "destroy result") } - Some(Method::Name) => { + Some(Method::Name) | Some(Method::NameExported) => { let ret = Self::name(rt)?; serialize(&ret, "name result") } - Some(Method::Symbol) => { + Some(Method::Symbol) | Some(Method::SymbolExported) => { let ret = Self::symbol(rt)?; serialize(&ret, "symbol result") } - Some(Method::TotalSupply) => { + Some(Method::TotalSupply) | Some(Method::TotalSupplyExported) => { let ret = Self::total_supply(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "total_supply result") } - Some(Method::BalanceOf) => { + Some(Method::BalanceOf) | Some(Method::BalanceOfExported) => { let ret = Self::balance_of(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "balance_of result") } - Some(Method::Transfer) => { + Some(Method::Transfer) | Some(Method::TransferExported) => { let ret = Self::transfer(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer result") } - Some(Method::TransferFrom) => { + Some(Method::TransferFrom) | Some(Method::TransferFromExported) => { let ret = Self::transfer_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer_from result") } - Some(Method::IncreaseAllowance) => { + Some(Method::IncreaseAllowance) | Some(Method::IncreaseAllowanceExported) => { let ret = Self::increase_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "increase_allowance result") } - Some(Method::DecreaseAllowance) => { + Some(Method::DecreaseAllowance) | Some(Method::DecreaseAllowanceExported) => { let ret = Self::decrease_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "decrease_allowance result") } - Some(Method::RevokeAllowance) => { + Some(Method::RevokeAllowance) | Some(Method::RevokeAllowanceExported) => { Self::revoke_allowance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Burn) => { + Some(Method::Burn) | Some(Method::BurnExported) => { let ret = Self::burn(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn result") } - Some(Method::BurnFrom) => { + Some(Method::BurnFrom) | Some(Method::BurnFromExported) => { let ret = Self::burn_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn_from result") } - Some(Method::Allowance) => { + Some(Method::Allowance) | Some(Method::AllowanceExported) => { let ret = Self::allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "allowance result") } diff --git a/actors/datacap/tests/datacap_actor_test.rs b/actors/datacap/tests/datacap_actor_test.rs index 0033f9f21..2c90367b0 100644 --- a/actors/datacap/tests/datacap_actor_test.rs +++ b/actors/datacap/tests/datacap_actor_test.rs @@ -34,7 +34,9 @@ mod mint { use fil_actor_datacap::{Actor, Method, MintParams, INFINITE_ALLOWANCE}; use fil_actors_runtime::cbor::serialize; - use fil_actors_runtime::test_utils::{expect_abort_contains_message, MARKET_ACTOR_CODE_ID}; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, MARKET_ACTOR_CODE_ID, + }; use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use std::ops::Sub; @@ -62,6 +64,21 @@ mod mint { h.check_state(&rt); } + #[test] + fn requires_builtin_caller() { + let (mut rt, h) = make_harness(); + let amt = TokenAmount::from_whole(1); + let params = MintParams { to: *ALICE, amount: amt, operators: vec![] }; + + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), + ); + h.check_state(&rt); + } + #[test] fn requires_verifreg_caller() { let (mut rt, h) = make_harness(); @@ -189,8 +206,11 @@ mod transfer { mod destroy { use crate::{make_harness, ALICE, BOB}; use fil_actor_datacap::DestroyParams; - use fil_actors_runtime::test_utils::{expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID}; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + }; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; + use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; @@ -198,6 +218,21 @@ mod destroy { use fil_actors_runtime::cbor::serialize; use fvm_shared::error::ExitCode; + #[test] + fn requires_builtin_caller() { + let (mut rt, h) = make_harness(); + let amt = TokenAmount::from_whole(1); + let params = DestroyParams { owner: *ALICE, amount: amt }; + + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), + ); + h.check_state(&rt); + } + #[test] fn only_governor_allowed() { let (mut rt, h) = make_harness(); diff --git a/actors/datacap/tests/harness/mod.rs b/actors/datacap/tests/harness/mod.rs index 0ca77a71d..ee3e7e506 100644 --- a/actors/datacap/tests/harness/mod.rs +++ b/actors/datacap/tests/harness/mod.rs @@ -41,6 +41,7 @@ pub struct Harness { impl Harness { pub fn construct_and_verify(&self, rt: &mut MockRuntime, registry: &Address) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 5a316801e..1cba951d0 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,6 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +frc42_dispatch = "1.0.0" fvm_shared = { version = "2.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 14b65f7f4..b509bda46 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -4,7 +4,9 @@ use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, cbor, ActorContext, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{ + actor_error, cbor, restrict_internal_api, ActorContext, ActorError, SYSTEM_ACTOR_ADDR, +}; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; @@ -27,6 +29,8 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, Exec = 2, + // Method numbers derived from FRC-XXXX standards + ExecExported = frc42_dispatch::method_hash!("Exec"), } /// Init actor @@ -102,12 +106,13 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Exec) => { + Some(Method::Exec) | Some(Method::ExecExported) => { let res = Self::exec(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index b69992209..d4292c6f0 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -6,6 +6,7 @@ use fil_actor_init::testing::check_state_invariants; use fil_actor_init::{ Actor as InitActor, ConstructorParams, ExecParams, ExecReturn, Method, State, }; +use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ @@ -15,7 +16,7 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; +use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; use num_traits::Zero; use serde::Serialize; @@ -287,7 +288,67 @@ fn sending_constructor_failure() { check_state(&rt); } +#[test] +fn exec_restricted_correctly() { + let mut rt = construct_runtime(); + construct_and_verify(&mut rt); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + + // cannot call the unexported method num + let fake_constructor_params = + RawBytes::serialize(ConstructorParams { network_name: String::from("fake_param") }) + .unwrap(); + let exec_params = ExecParams { + code_cid: *MULTISIG_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(fake_constructor_params.clone()).unwrap(), + }; + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::Exec as MethodNum, + &serialize(&exec_params, "params").unwrap(), + ), + ); + + // can call the exported method num + + // Assign addresses + let unique_address = Address::new_actor(b"multisig"); + rt.new_actor_addr = Some(unique_address); + + // Next id + let expected_id = 100; + let expected_id_addr = Address::new_id(expected_id); + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id); + + // Expect a send to the multisig actor constructor + rt.expect_send( + expected_id_addr, + METHOD_CONSTRUCTOR, + RawBytes::serialize(&fake_constructor_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + rt.expect_validate_caller_any(); + + let ret = rt + .call::(Method::ExecExported as u64, &RawBytes::serialize(&exec_params).unwrap()) + .unwrap(); + let exec_ret: ExecReturn = RawBytes::deserialize(&ret).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); + rt.verify(); +} + fn construct_and_verify(rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let params = ConstructorParams { network_name: "mock".to_string() }; let ret = diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 055540fc4..be67aa01a 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -18,6 +18,7 @@ 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 = "1.0.0" frc46_token = "1.1.0" fvm_ipld_bitfield = "0.5.2" fvm_ipld_blockstore = "0.1.1" diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 6f08d20b5..610156468 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -30,9 +30,10 @@ use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorDowncast, ActorError, AsActorError, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, - REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, + AsActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, + DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use crate::ext::verifreg::{AllocationID, AllocationRequest}; @@ -71,6 +72,9 @@ pub enum Method { OnMinerSectorsTerminate = 7, ComputeDataCommitment = 8, CronTick = 9, + // Method numbers derived from FRC-XXXX standards + AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), + WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), } /// Market Actor @@ -1165,16 +1169,17 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; Ok(RawBytes::default()) } - Some(Method::AddBalance) => { + Some(Method::AddBalance) | Some(Method::AddBalanceExported) => { Self::add_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::WithdrawBalance) => { + Some(Method::WithdrawBalance) | Some(Method::WithdrawBalanceExported) => { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 2bf48c600..8ab5de6b4 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -114,6 +114,7 @@ pub fn check_state_with_expected(rt: &MockRuntime, expected_patterns: &[Regex]) } pub fn construct_and_verify(rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( RawBytes::default(), diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 6fcad4c43..3a78313f0 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -28,7 +28,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; use fvm_shared::sector::StoragePower; -use fvm_shared::{HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR, METHOD_SEND}; use regex::Regex; use std::ops::Add; @@ -58,6 +58,7 @@ fn simple_construction() { ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( @@ -808,7 +809,17 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t deal.verified_deal = true; // add funds for client using its BLS address -> will be resolved and persisted - add_participant_funds(&mut rt, client_bls, deal.client_balance_requirement()); + let amount = deal.client_balance_requirement(); + + rt.set_value(amount.clone()); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, client_resolved); + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + assert!(rt + .call::(Method::AddBalance as u64, &RawBytes::serialize(client_bls).unwrap()) + .is_ok()); + rt.verify(); + rt.add_balance(amount); + assert_eq!( deal.client_balance_requirement(), get_escrow_balance(&rt, &client_resolved).unwrap() @@ -1953,3 +1964,37 @@ fn insufficient_provider_balance_in_a_batch() { check_state(&rt); } + +#[test] +fn add_balance_restricted_correctly() { + let mut rt = setup(); + let amount = TokenAmount::from_atto(1000); + rt.set_value(amount); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::AddBalance as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ), + ); + + // can call the exported method num + rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "forbidden, allowed: [Account, Multisig]", + rt.call::( + Method::AddBalanceExported as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ), + ); + + rt.verify(); +} diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 0abd4b82b..856575035 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,6 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +frc42_dispatch = "1.0.0" fvm_shared = { version = "2.0.0-alpha.2", default-features = false } fvm_ipld_bitfield = "0.5.2" fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 2aaafba08..7a7487309 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -40,9 +40,9 @@ use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorContext, ActorDowncast, ActorError, BURNT_FUNDS_ACTOR_ADDR, - CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, + BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; pub use monies::*; pub use partition_state::*; @@ -123,6 +123,9 @@ pub enum Method { ChangeBeneficiary = 30, GetBeneficiary = 31, ExtendSectorExpiration2 = 32, + // Method numbers derived from FRC-XXXX standards + ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), + GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -3417,10 +3420,12 @@ pub struct ReplicaUpdateInner { } enum ExtensionKind { - ExtendCommittmentLegacy, // handle only legacy sectors - ExtendCommittment, // handle both Simple QAP and legacy sectors - // TODO: when landing https://github.com/filecoin-project/builtin-actors/pull/518 - // ExtendProofValidity + // handle only legacy sectors + ExtendCommittmentLegacy, + // handle both Simple QAP and legacy sectors + // TODO: when landing https://github.com/filecoin-project/builtin-actors/pull/518 + // ExtendProofValidity + ExtendCommittment, } // ExtendSectorExpiration param @@ -3636,18 +3641,19 @@ fn extend_simple_qap_sector( let old_duration = sector.expiration - sector.activation; let deal_space = §or.deal_weight / old_duration; let old_verified_deal_space = §or.verified_deal_weight / old_duration; - let (expected_verified_deal_space, new_verified_deal_space) = - match claim_space_by_sector.get(§or.sector_number) { - None => { - return Err(actor_error!( + let (expected_verified_deal_space, new_verified_deal_space) = match claim_space_by_sector + .get(§or.sector_number) + { + None => { + return Err(actor_error!( illegal_argument, "claim missing from declaration for sector {} with non-zero verified deal weight {}", sector.sector_number, §or.verified_deal_weight - )) - } - Some(space) => space, - }; + )); + } + Some(space) => space, + }; // claims must be completely accounted for if BigInt::from(*expected_verified_deal_space as i64) != old_verified_deal_space { return Err(actor_error!(illegal_argument, "declared verified deal space in claims ({}) does not match verified deal space ({}) for sector {}", expected_verified_deal_space, old_verified_deal_space, sector.sector_number)); @@ -4863,6 +4869,7 @@ impl ActorCode for Actor { RT: Runtime, RT::Blockstore: Clone, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -4980,11 +4987,11 @@ impl ActorCode for Actor { let res = Self::prove_replica_updates2(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::ChangeBeneficiary) => { + Some(Method::ChangeBeneficiary) | Some(Method::ChangeBenificiaryExported) => { Self::change_beneficiary(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::GetBeneficiary) => { + Some(Method::GetBeneficiary) | Some(Method::GetBeneficiaryExported) => { let res = Self::get_beneficiary(rt)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/miner/tests/change_beneficiary_test.rs b/actors/miner/tests/change_beneficiary_test.rs index db93608da..7d1c9fb3b 100644 --- a/actors/miner/tests/change_beneficiary_test.rs +++ b/actors/miner/tests/change_beneficiary_test.rs @@ -1,7 +1,10 @@ -use fil_actor_miner::BeneficiaryTerm; -use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message, MockRuntime}; +use fil_actor_miner::{Actor, BeneficiaryTerm, GetBeneficiaryReturn, Method}; +use fil_actors_runtime::test_utils::{ + expect_abort, expect_abort_contains_message, make_identity_cid, MockRuntime, +}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; -use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; +use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, MethodNum}; use num_traits::Zero; mod util; @@ -441,3 +444,30 @@ fn successfully_get_beneficiary() { assert_eq!(beneficiary_return.active.beneficiary, info.beneficiary); assert_eq!(beneficiary_return.active.term, info.beneficiary_term); } + +#[test] +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)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::GetBeneficiary as MethodNum, &RawBytes::default()), + ); + + // can call the exported method num + rt.expect_validate_caller_any(); + let beneficiary_return: GetBeneficiaryReturn = rt + .call::(Method::GetBeneficiaryExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + assert_eq!(h.owner, beneficiary_return.active.beneficiary); + assert_eq!(BeneficiaryTerm::default(), beneficiary_return.active.term); + + rt.verify(); +} diff --git a/actors/miner/tests/declare_recoveries.rs b/actors/miner/tests/declare_recoveries.rs index 6b3ba50d2..4b6a28311 100644 --- a/actors/miner/tests/declare_recoveries.rs +++ b/actors/miner/tests/declare_recoveries.rs @@ -128,7 +128,7 @@ fn recovery_fails_during_active_consensus_fault() { h.commit_and_prove_sectors(&mut rt, 1, DEFAULT_SECTOR_EXPIRATION as u64, vec![], true); // consensus fault - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; h.report_consensus_fault( &mut rt, diff --git a/actors/miner/tests/miner_actor_test_commitment.rs b/actors/miner/tests/miner_actor_test_commitment.rs index 341d578f4..ef2338f68 100644 --- a/actors/miner/tests/miner_actor_test_commitment.rs +++ b/actors/miner/tests/miner_actor_test_commitment.rs @@ -446,7 +446,7 @@ mod miner_actor_test_commitment { epoch: rt.epoch - 1, fault_type: ConsensusFaultType::DoubleForkMining, }; - let test_addr = Address::new_actor(b"satoshi"); + let test_addr = Address::new_id(1234); h.report_consensus_fault(&mut rt, test_addr, Some(fault)).unwrap(); let precommit_params = h.make_pre_commit_params(102, challenge_epoch, expiration, vec![]); diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 41f4a7b17..e0b09a191 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -68,6 +68,7 @@ fn simple_construction() { let mut env = prepare_env(); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -141,6 +142,7 @@ fn control_addresses_are_resolved_during_construction() { env.rt.id_addresses.insert(control2, control2id); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -175,6 +177,7 @@ fn fails_if_control_address_is_not_an_account_actor() { env.rt.actor_code_cids.insert(control1, *PAYCH_ACTOR_CODE_ID); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); env.rt.expect_send( env.worker, @@ -199,6 +202,7 @@ fn test_construct_with_invalid_peer_id() { env.peer_id = vec![0; env.rt.policy.max_peer_id_length + 1]; let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -218,6 +222,7 @@ fn fails_if_control_addresses_exceeds_maximum_length() { } let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -237,6 +242,7 @@ fn test_construct_with_large_multiaddr() { } let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env @@ -255,6 +261,7 @@ fn test_construct_with_empty_multiaddr() { env.multiaddrs.push(BytesDe(vec![1])); let params = constructor_params(&env); + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); let result = env diff --git a/actors/miner/tests/report_consensus_fault.rs b/actors/miner/tests/report_consensus_fault.rs index 85a0f0058..6d96cff3f 100644 --- a/actors/miner/tests/report_consensus_fault.rs +++ b/actors/miner/tests/report_consensus_fault.rs @@ -27,7 +27,7 @@ fn invalid_report_rejected() { rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, h.report_consensus_fault(&mut rt, test_addr, None), @@ -41,7 +41,7 @@ fn mistargeted_report_rejected() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -64,7 +64,7 @@ fn report_consensus_fault_pays_reward_and_charges_fee() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let epoch = rt.epoch; let receiver = rt.receiver; h.report_consensus_fault( @@ -85,7 +85,7 @@ fn report_consensus_fault_updates_consensus_fault_reported_field() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let receiver = rt.receiver; let start_info = h.get_info(&rt); @@ -117,7 +117,7 @@ fn double_report_of_consensus_fault_fails() { let (h, mut rt) = setup(); rt.set_epoch(1); - let test_addr = Address::new_actor("satoshi".as_bytes()); + let test_addr = Address::new_id(1234); let receiver = rt.receiver; let start_info = h.get_info(&rt); diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 2397e9851..56c413292 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -15,8 +15,8 @@ use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize_vec; use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, ActorContext, - ActorError, AsActorError, Map, INIT_ACTOR_ADDR, + actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, + restrict_internal_api, ActorContext, ActorError, AsActorError, Map, INIT_ACTOR_ADDR, }; pub use self::state::*; @@ -42,6 +42,16 @@ pub enum Method { SwapSigner = 7, ChangeNumApprovalsThreshold = 8, LockBalance = 9, + // Method numbers derived from FRC-XXXX standards + ProposeExported = frc42_dispatch::method_hash!("Propose"), + ApproveExported = frc42_dispatch::method_hash!("Approve"), + CancelExported = frc42_dispatch::method_hash!("Cancel"), + AddSignerExported = frc42_dispatch::method_hash!("AddSigner"), + RemoveSignerExported = frc42_dispatch::method_hash!("RemoveSigner"), + SwapSignerExported = frc42_dispatch::method_hash!("SwapSigner"), + ChangeNumApprovalsThresholdExported = + frc42_dispatch::method_hash!("ChangeNumApprovalsThreshold"), + LockBalanceExported = frc42_dispatch::method_hash!("LockBalance"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -556,40 +566,42 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Propose) => { + Some(Method::Propose) | Some(Method::ProposeExported) => { let res = Self::propose(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::Approve) => { + Some(Method::Approve) | Some(Method::ApproveExported) => { let res = Self::approve(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::Cancel) => { + Some(Method::Cancel) | Some(Method::CancelExported) => { Self::cancel(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::AddSigner) => { + Some(Method::AddSigner) | Some(Method::AddSignerExported) => { Self::add_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::RemoveSigner) => { + Some(Method::RemoveSigner) | Some(Method::RemoveSignerExported) => { Self::remove_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::SwapSigner) => { + Some(Method::SwapSigner) | Some(Method::SwapSignerExported) => { Self::swap_signer(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangeNumApprovalsThreshold) => { + Some(Method::ChangeNumApprovalsThreshold) + | Some(Method::ChangeNumApprovalsThresholdExported) => { Self::change_num_approvals_threshold(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::LockBalance) => { + Some(Method::LockBalance) | Some(Method::LockBalanceExported) => { Self::lock_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index cdb6f8392..b7a768761 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -1,7 +1,7 @@ use fil_actor_multisig::testing::check_state_invariants; use fil_actor_multisig::{ - compute_proposal_hash, Actor as MultisigActor, ConstructorParams, Method, ProposeReturn, State, - Transaction, TxnID, TxnIDParams, SIGNERS_MAX, + compute_proposal_hash, Actor as MultisigActor, ConstructorParams, Method, ProposeParams, + ProposeReturn, State, Transaction, TxnID, TxnIDParams, SIGNERS_MAX, }; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; @@ -11,6 +11,7 @@ use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; +use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -120,7 +121,7 @@ mod constructor_tests { RawBytes::default(), rt.call::( Method::Constructor as u64, - &RawBytes::serialize(¶ms).unwrap() + &RawBytes::serialize(¶ms).unwrap(), ) .unwrap() ); @@ -752,6 +753,58 @@ fn test_fail_propose_from_non_signer() { check_state(&rt); } +#[test] +fn test_propose_restricted_correctly() { + let msig = Address::new_id(1000); + let mut rt = construct_runtime(msig); + let h = util::ActorHarness::new(); + + let anne = Address::new_id(101); + let bob = Address::new_id(102); + // We will treat Chuck as having code CID b"103" + let chuck = Address::new_id(103); + let no_unlock_duration = 0; + let start_epoch = 0; + let signers = vec![anne, bob]; + + let send_value = TokenAmount::from_atto(10u8); + h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"103"), Address::new_id(103)); + let propose_params = serialize( + &ProposeParams { + to: chuck, + value: send_value, + method: METHOD_SEND, + params: RawBytes::default(), + }, + "propose params", + ) + .unwrap(); + + // cannot call the unexported method num + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::Propose as u64, &propose_params), + ); + + rt.verify(); + + // can call the exported method num + rt.expect_validate_caller_type([Type::Account, Type::Multisig].to_vec()); + // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "forbidden, allowed: [Account, Multisig]", + rt.call::(Method::ProposeExported as u64, &propose_params), + ); + + rt.verify(); +} + // AddSigner #[test] fn test_add_signer() { @@ -765,7 +818,8 @@ fn test_add_signer() { #[allow(dead_code)] desc: &'a str, - id_addr_mapping: Vec<(Address, Address)>, // non-id to id + id_addr_mapping: Vec<(Address, Address)>, + // non-id to id initial_signers: Vec

, initial_approvals: u64, @@ -778,7 +832,7 @@ fn test_add_signer() { } let test_cases = vec![ - TestCase{ + TestCase { desc: "happy path add signer", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob], @@ -789,7 +843,7 @@ fn test_add_signer() { expect_approvals: 2, code: ExitCode::OK, }, - TestCase{ + TestCase { desc: "add signer and increase threshold", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob], @@ -800,7 +854,7 @@ fn test_add_signer() { expect_approvals: 3, code: ExitCode::OK, }, - TestCase{ + TestCase { desc: "fail to add signer that already exists", id_addr_mapping: Vec::new(), initial_signers: vec![anne, bob, chuck], @@ -811,28 +865,28 @@ fn test_add_signer() { expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, }, - TestCase{ + TestCase { desc: "fail to add signer with ID address that already exists even thugh we only have non ID address as approver", id_addr_mapping: vec![(chuck_pubkey, chuck)], initial_signers: vec![anne, bob, chuck_pubkey], initial_approvals: 3, add_signer: chuck, - increase:false, + increase: false, expect_signers: vec![anne, bob, chuck], expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, }, - TestCase{ + TestCase { desc: "fail to add signer with ID address that already exists even thugh we only have non ID address as approver", id_addr_mapping: vec![(chuck_pubkey, chuck)], initial_signers: vec![anne, bob, chuck], initial_approvals: 3, add_signer: chuck_pubkey, - increase:false, + increase: false, expect_signers: vec![anne, bob, chuck], expect_approvals: 3, code: ExitCode::USR_FORBIDDEN, - } + }, ]; for tc in test_cases { @@ -1090,7 +1144,7 @@ fn test_signer_swap() { swap_from: anne, expect_signers: vec![], code: ExitCode::USR_ILLEGAL_ARGUMENT, - } + }, ]; for tc in test_cases { @@ -1449,6 +1503,7 @@ mod approval_tests { ); check_state(&rt); } + #[test] fn fail_approval_if_not_enough_unlocked_balance_available() { let msig = Address::new_id(100); diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index 4322e8078..eb6716cc2 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -22,9 +22,9 @@ use fil_actors_runtime::cbor::{deserialize, serialize}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_actor_id, ActorDowncast, - ActorError, BatchReturn, Map, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + actor_error, cbor, make_map_with_root_and_bitwidth, resolve_to_actor_id, restrict_internal_api, + ActorDowncast, ActorError, BatchReturn, Map, DATACAP_TOKEN_ACTOR_ADDR, + STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fil_actors_runtime::{ActorContext, AsActorError, BatchReturnGen}; @@ -60,6 +60,10 @@ pub enum Method { GetClaims = 10, ExtendClaimTerms = 11, RemoveExpiredClaims = 12, + // Method numbers derived from FRC-XXXX standards + RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), + ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), + RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -1067,6 +1071,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; @@ -1089,7 +1094,8 @@ impl ActorCode for Actor { Self::remove_verified_client_data_cap(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::RemoveExpiredAllocations) => { + Some(Method::RemoveExpiredAllocations) + | Some(Method::RemoveExpiredAllocationsExported) => { let res = Self::remove_expired_allocations(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -1097,7 +1103,7 @@ impl ActorCode for Actor { let res = Self::claim_allocations(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::ExtendClaimTerms) => { + Some(Method::ExtendClaimTerms) | Some(Method::ExtendClaimTermsExported) => { let res = Self::extend_claim_terms(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -1105,7 +1111,7 @@ impl ActorCode for Actor { let res = Self::get_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::RemoveExpiredClaims) => { + Some(Method::RemoveExpiredClaims) | Some(Method::RemoveExpiredClaimsExported) => { let res = Self::remove_expired_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 0c98971d2..d608197af 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -62,6 +62,7 @@ pub struct Harness { impl Harness { pub fn construct_and_verify(&self, rt: &mut MockRuntime, root_param: &Address) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index c22d363d5..642cd690f 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -64,6 +64,7 @@ mod construction { let mut rt = new_runtime(); let root_pubkey = Address::new_bls(&[7u8; BLS_PUB_LEN]).unwrap(); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, @@ -439,17 +440,22 @@ mod allocs_claims { use fvm_shared::bigint::BigInt; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; - use fvm_shared::ActorID; + use fvm_shared::{ActorID, MethodNum}; use num_traits::Zero; use std::str::FromStr; - use fil_actor_verifreg::Claim; - use fil_actor_verifreg::{AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, State}; + use fil_actor_verifreg::{ + Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, Method, State, + }; + use fil_actor_verifreg::{Claim, ExtendClaimTermsReturn}; + use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::policy_constants::{ MAXIMUM_VERIFIED_ALLOCATION_TERM, MINIMUM_VERIFIED_ALLOCATION_SIZE, MINIMUM_VERIFIED_ALLOCATION_TERM, }; - use fil_actors_runtime::test_utils::ACCOUNT_ACTOR_CODE_ID; + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + }; use fil_actors_runtime::FailCode; use harness::*; @@ -901,6 +907,55 @@ mod allocs_claims { assert!(h.load_claim(&mut rt, PROVIDER1, id2).is_none()); // removed h.check_state(&rt); } + + #[test] + fn extend_claims_restricted_correctly() { + let (h, mut rt) = new_harness(); + let size = MINIMUM_VERIFIED_ALLOCATION_SIZE as u64; + let sector = 0; + let start = 0; + let min_term = MINIMUM_VERIFIED_ALLOCATION_TERM; + let max_term = min_term + 1000; + + let claim1 = make_claim("1", CLIENT1, PROVIDER1, size, min_term, max_term, start, sector); + + let id1 = h.create_claim(&mut rt, &claim1).unwrap(); + + // Extend claim terms and verify return value. + let params = ExtendClaimTermsParams { + terms: vec![ClaimTerm { provider: PROVIDER1, claim_id: id1, term_max: max_term + 1 }], + }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(CLIENT1)); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + h.extend_claim_terms(&mut rt, ¶ms), + ); + + // can call the exported method num + + rt.expect_validate_caller_any(); + let ret: ExtendClaimTermsReturn = rt + .call::( + Method::ExtendClaimTermsExported as MethodNum, + &serialize(¶ms, "extend claim terms params").unwrap(), + ) + .unwrap() + .deserialize() + .expect("failed to deserialize extend claim terms return"); + + rt.verify(); + + assert_eq!(ret.codes(), vec![ExitCode::OK]); + + // Verify state directly. + assert_claim(&rt, PROVIDER1, id1, &Claim { term_max: max_term + 1, ..claim1 }); + h.check_state(&rt); + } } mod datacap { diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index aea1180c0..2208a45bb 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -11,7 +11,7 @@ use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// Types of built-in actors that can be treated as principles. +/// Types of built-in actors that can be treated as principals. /// This distinction is legacy and should be removed prior to FVM support for /// user-programmable actors. pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 80cf773c2..3f9565565 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -432,6 +432,8 @@ impl MockRuntime { } pub fn set_caller(&mut self, code_id: Cid, address: Address) { + // fail if called with a non-ID address, since the caller() method must always return an ID + address.id().unwrap(); self.caller = address; self.caller_type = code_id; self.actor_code_cids.insert(address, code_id); @@ -759,11 +761,12 @@ impl Runtime for MockRuntime { types, expected_caller_type, ); - let call_type = self.resolve_builtin_actor_type(&self.caller_type).unwrap(); - for expected in &types { - if &call_type == expected { - self.expectations.borrow_mut().expect_validate_caller_type = None; - return Ok(()); + if let Some(call_type) = self.resolve_builtin_actor_type(&self.caller_type) { + for expected in &types { + if &call_type == expected { + self.expectations.borrow_mut().expect_validate_caller_type = None; + return Ok(()); + } } } From 370223b77474be245ce69730353934237ed5cb17 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 7 Nov 2022 20:39:31 +0000 Subject: [PATCH 180/339] Restrict internal APIs of all actors (#809) --- actors/cron/src/lib.rs | 3 ++- actors/cron/tests/cron_actor_test.rs | 1 + actors/paych/src/lib.rs | 3 ++- actors/paych/tests/paych_actor_test.rs | 4 ++++ actors/power/src/lib.rs | 5 +++-- actors/power/tests/harness/mod.rs | 1 + actors/reward/src/lib.rs | 5 +++-- actors/reward/tests/reward_actor_test.rs | 1 + actors/system/src/lib.rs | 5 ++++- 9 files changed, 21 insertions(+), 7 deletions(-) diff --git a/actors/cron/src/lib.rs b/actors/cron/src/lib.rs index 173aca0f5..c6d6661f1 100644 --- a/actors/cron/src/lib.rs +++ b/actors/cron/src/lib.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, cbor, ActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, cbor, restrict_internal_api, ActorError, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; @@ -83,6 +83,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; diff --git a/actors/cron/tests/cron_actor_test.rs b/actors/cron/tests/cron_actor_test.rs index bc92de96f..18609b0d6 100644 --- a/actors/cron/tests/cron_actor_test.rs +++ b/actors/cron/tests/cron_actor_test.rs @@ -114,6 +114,7 @@ fn epoch_tick_with_entries() { } fn construct_and_verify(rt: &mut MockRuntime, params: &ConstructorParams) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt.call::(1, &RawBytes::serialize(¶ms).unwrap()).unwrap(); assert_eq!(RawBytes::default(), ret); diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 8299b6ea2..75d9a5091 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -4,7 +4,7 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, resolve_to_actor_id, ActorDowncast, ActorError, Array, + actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, Array, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -324,6 +324,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt, cbor::deserialize_params(params)?)?; diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index 42afb0a36..969033e02 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -108,6 +108,7 @@ mod paych_constructor { #[test] fn actor_doesnt_exist_test() { let mut rt = construct_runtime(); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), @@ -226,6 +227,7 @@ mod paych_constructor { ExitCode::OK, ); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: non_id_addr, to: to_addr }; expect_abort( @@ -263,6 +265,7 @@ mod paych_constructor { ExitCode::OK, ); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { from: from_addr, to: non_id_addr }; expect_abort( @@ -1198,6 +1201,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche fn construct_and_verify(rt: &mut MockRuntime, sender: Address, receiver: Address) { let params = ConstructorParams { from: sender, to: receiver }; + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_type(vec![Type::Init]); call(rt, METHOD_CONSTRUCTOR, &RawBytes::serialize(¶ms).unwrap()); rt.verify(); diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 6229f61f0..21dc612b6 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -9,8 +9,8 @@ use ext::init; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, make_map_with_root_and_bitwidth, ActorDowncast, ActorError, Multimap, - CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + actor_error, cbor, make_map_with_root_and_bitwidth, restrict_internal_api, ActorDowncast, + ActorError, Multimap, CRON_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; @@ -625,6 +625,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 50114a29c..5849e2b9c 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -101,6 +101,7 @@ pub struct Harness { impl Harness { pub fn construct(&self, rt: &mut MockRuntime) { + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); rt.call::(Method::Constructor as MethodNum, &RawBytes::default()).unwrap(); rt.verify() diff --git a/actors/reward/src/lib.rs b/actors/reward/src/lib.rs index e01a414b5..54c4f41d4 100644 --- a/actors/reward/src/lib.rs +++ b/actors/reward/src/lib.rs @@ -3,8 +3,8 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, ActorError, BURNT_FUNDS_ACTOR_ADDR, EXPECTED_LEADERS_PER_EPOCH, - STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorError, BURNT_FUNDS_ACTOR_ADDR, + EXPECTED_LEADERS_PER_EPOCH, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; @@ -223,6 +223,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { let param: Option = cbor::deserialize_params(params)?; diff --git a/actors/reward/tests/reward_actor_test.rs b/actors/reward/tests/reward_actor_test.rs index c303bb541..947d48fdc 100644 --- a/actors/reward/tests/reward_actor_test.rs +++ b/actors/reward/tests/reward_actor_test.rs @@ -340,6 +340,7 @@ fn construct_and_verify(curr_power: &StoragePower) -> MockRuntime { caller_type: *SYSTEM_ACTOR_CODE_ID, ..Default::default() }; + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let ret = rt .call::( diff --git a/actors/system/src/lib.rs b/actors/system/src/lib.rs index f68d8e2f6..c7abe1af7 100644 --- a/actors/system/src/lib.rs +++ b/actors/system/src/lib.rs @@ -11,7 +11,9 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_error, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{ + actor_error, restrict_internal_api, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR, +}; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); @@ -73,6 +75,7 @@ impl ActorCode for Actor { where RT: Runtime, { + restrict_internal_api(rt, method)?; match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor(rt)?; From dec77b88c657ce45b8dd710fd5058308881a59ef Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Wed, 9 Nov 2022 08:59:56 +1100 Subject: [PATCH 181/339] Exported API method for market actor escrow/locked balance (#812) --- actors/market/src/lib.rs | 35 ++++++- actors/market/src/types.rs | 55 +++++------ actors/market/tests/cron_tick_deal_expiry.rs | 16 ++-- .../market/tests/cron_tick_timedout_deals.rs | 7 +- actors/market/tests/harness.rs | 72 +++++++-------- actors/market/tests/market_actor_test.rs | 91 ++++++++++--------- 6 files changed, 151 insertions(+), 125 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 610156468..3bbbc0e19 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -26,6 +26,7 @@ use log::info; use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; +use crate::balance_table::BalanceTable; use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; @@ -72,9 +73,10 @@ pub enum Method { OnMinerSectorsTerminate = 7, ComputeDataCommitment = 8, CronTick = 9, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), } /// Market Actor @@ -140,6 +142,33 @@ impl Actor { Ok(WithdrawBalanceReturn { amount_withdrawn: amount_extracted }) } + /// Returns the escrow balance and locked amount for an address. + fn get_balance( + rt: &mut impl Runtime, + account: Address, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let nominal = rt.resolve_address(&account).ok_or_else(|| { + actor_error!(illegal_argument, "failed to resolve address {}", account) + })?; + let account = Address::new_id(nominal); + + let store = rt.store(); + let st: State = rt.state()?; + let balances = BalanceTable::from_root(store, &st.escrow_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load escrow table")?; + let locks = BalanceTable::from_root(store, &st.locked_table) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load locked table")?; + let balance = balances + .get(&account) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get escrow balance")?; + let locked = locks + .get(&account) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get locked balance")?; + + Ok(GetBalanceReturn { balance, locked }) + } + /// Publish a new set of storage deals (not yet included in a sector). fn publish_storage_deals( rt: &mut impl Runtime, @@ -1207,6 +1236,10 @@ impl ActorCode for Actor { Self::cron_tick(rt)?; Ok(RawBytes::default()) } + Some(Method::GetBalanceExported) => { + let res = Self::get_balance(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index 39ee64e13..bac78d0df 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -22,7 +22,7 @@ use super::deal::{ClientDealProposal, DealProposal, DealState}; pub const PROPOSALS_AMT_BITWIDTH: u32 = 5; pub const STATES_AMT_BITWIDTH: u32 = 6; -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct WithdrawBalanceParams { pub provider_or_client: Address, pub amount: TokenAmount, @@ -30,33 +30,32 @@ pub struct WithdrawBalanceParams { impl Cbor for WithdrawBalanceParams {} -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] #[serde(transparent)] pub struct WithdrawBalanceReturn { pub amount_withdrawn: TokenAmount, } -#[derive(Serialize_tuple, Deserialize_tuple)] -pub struct OnMinerSectorsTerminateParams { - pub epoch: ChainEpoch, - pub deal_ids: Vec, +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetBalanceReturn { + pub balance: TokenAmount, + pub locked: TokenAmount, } -#[derive(Serialize_tuple)] - -pub struct OnMinerSectorsTerminateParamsRef<'a> { +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct OnMinerSectorsTerminateParams { pub epoch: ChainEpoch, - pub deal_ids: &'a [DealID], + pub deal_ids: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct PublishStorageDealsParams { pub deals: Vec, } impl Cbor for PublishStorageDealsParams {} -#[derive(Serialize_tuple, Deserialize_tuple, Debug)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] // Add Eq when BitField does pub struct PublishStorageDealsReturn { pub ids: Vec, pub valid_deals: BitField, @@ -65,41 +64,36 @@ pub struct PublishStorageDealsReturn { // Changed since V2: // - Array of Sectors rather than just one // - Removed SectorStart -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifyDealsForActivationParams { pub sectors: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct SectorDeals { pub sector_type: RegisteredSealProof, pub sector_expiry: ChainEpoch, pub deal_ids: Vec, } -#[derive(Serialize_tuple)] -pub struct VerifyDealsForActivationParamsRef<'a> { - pub sectors: &'a [SectorDeals], -} - -#[derive(Serialize_tuple, Deserialize_tuple, Default)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifyDealsForActivationReturn { pub sectors: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple, Default, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq, Default)] pub struct SectorDealData { /// Option::None signifies commitment to empty sector, meaning no deals. pub commd: Option, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ActivateDealsParams { pub deal_ids: Vec, pub sector_expiry: ChainEpoch, } -#[derive(Serialize_tuple, Deserialize_tuple, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct VerifiedDealInfo { pub client: ActorID, pub allocation_id: AllocationID, @@ -107,13 +101,13 @@ pub struct VerifiedDealInfo { pub size: PaddedPieceSize, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ActivateDealsResult { #[serde(with = "bigint_ser")] pub nonverified_deal_space: BigInt, pub verified_infos: Vec, } -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Default)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct DealSpaces { #[serde(with = "bigint_ser")] pub deal_space: BigInt, @@ -121,17 +115,12 @@ pub struct DealSpaces { pub verified_deal_space: BigInt, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ComputeDataCommitmentParams { pub inputs: Vec, } -#[derive(Serialize_tuple)] -pub struct ComputeDataCommitmentParamsRef<'a> { - pub inputs: &'a [SectorDataSpec], -} - -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct ComputeDataCommitmentReturn { pub commds: Vec, } @@ -142,7 +131,7 @@ pub type DealArray<'bs, BS> = Array<'bs, DealProposal, BS>; /// A specialization of a array to deals. pub type DealMetaArray<'bs, BS> = Array<'bs, DealState, BS>; -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct SectorDataSpec { pub deal_ids: Vec, pub sector_type: RegisteredSealProof, diff --git a/actors/market/tests/cron_tick_deal_expiry.rs b/actors/market/tests/cron_tick_deal_expiry.rs index 38b7bce7c..6118cd3fe 100644 --- a/actors/market/tests/cron_tick_deal_expiry.rs +++ b/actors/market/tests/cron_tick_deal_expiry.rs @@ -163,8 +163,8 @@ fn expired_deal_should_unlock_the_remaining_client_and_provider_locked_balance_a ); let deal_proposal = get_deal_proposal(&mut rt, deal_id); - let c_escrow = get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(); - let p_escrow = get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(); + let c_escrow = get_balance(&mut rt, &CLIENT_ADDR).balance; + let p_escrow = get_balance(&mut rt, &PROVIDER_ADDR).balance; // move the current epoch so that deal is expired rt.set_epoch(END_EPOCH + 1000); @@ -173,11 +173,13 @@ fn expired_deal_should_unlock_the_remaining_client_and_provider_locked_balance_a // assert balances let payment = deal_proposal.total_storage_fee(); - assert_eq!(c_escrow - &payment, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, CLIENT_ADDR).is_zero()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(c_escrow - &payment, client_acct.balance); + assert!(client_acct.locked.is_zero()); - assert_eq!(p_escrow + &payment, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, PROVIDER_ADDR).is_zero()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(p_escrow + &payment, provider_acct.balance); + assert!(provider_acct.locked.is_zero()); // deal should be deleted assert_deal_deleted(&mut rt, deal_id, deal_proposal); @@ -201,7 +203,7 @@ fn all_payments_are_made_for_a_deal_client_withdraws_collateral_and_client_accou // move the current epoch so that deal is expired rt.set_epoch(END_EPOCH + 100); cron_tick(&mut rt); - assert_eq!(deal_proposal.client_collateral, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(deal_proposal.client_collateral, get_balance(&mut rt, &CLIENT_ADDR).balance); // client withdraws collateral -> account should be removed as it now has zero balance withdraw_client_balance( diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index 55714aab5..dfdff2e45 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -36,7 +36,7 @@ fn timed_out_deal_is_slashed_and_deleted() { ); let deal_proposal = get_deal_proposal(&mut rt, deal_id); - let c_escrow = get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(); + let c_escrow = get_balance(&mut rt, &CLIENT_ADDR).balance; // do a cron tick for it -> should time out and get slashed rt.set_epoch(process_epoch(START_EPOCH, deal_id)); @@ -50,8 +50,9 @@ fn timed_out_deal_is_slashed_and_deleted() { ); cron_tick(&mut rt); - assert_eq!(c_escrow, get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); - assert!(get_locked_balance(&mut rt, CLIENT_ADDR).is_zero()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(c_escrow, client_acct.balance); + assert!(client_acct.locked.is_zero()); assert_account_zero(&mut rt, PROVIDER_ADDR); assert_deal_deleted(&mut rt, deal_id, deal_proposal); check_state(&rt); diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 8ab5de6b4..7bccd1506 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -10,10 +10,10 @@ use std::{cell::RefCell, collections::HashMap}; use fil_actor_market::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD}; use fil_actor_market::ext::verifreg::{AllocationID, AllocationRequest, AllocationsResponse}; use fil_actor_market::{ - balance_table::BalanceTable, deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, - gen_rand_next_epoch, testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, + deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, gen_rand_next_epoch, + testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, Actor as MarketActor, ClientDealProposal, DealArray, DealMetaArray, DealProposal, DealState, - Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, + GetBalanceReturn, Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, PublishStorageDealsReturn, SectorDeals, State, VerifyDealsForActivationParams, VerifyDealsForActivationReturn, WithdrawBalanceParams, WithdrawBalanceReturn, NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, @@ -123,13 +123,16 @@ pub fn construct_and_verify(rt: &mut MockRuntime) { rt.verify(); } -pub fn get_escrow_balance(rt: &MockRuntime, addr: &Address) -> Result { - let st: State = rt.get_state(); - - let et = BalanceTable::from_root(rt.store(), &st.escrow_table) - .expect("failed to construct balance table from blockstore"); - - Ok(et.get(addr).expect("address does not exist in escrow balance table")) +pub fn get_balance(rt: &mut MockRuntime, addr: &Address) -> GetBalanceReturn { + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.expect_validate_caller_any(); + let ret: GetBalanceReturn = rt + .call::(Method::GetBalanceExported as u64, &RawBytes::serialize(addr).unwrap()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + ret } pub fn expect_get_control_addresses( @@ -316,12 +319,6 @@ pub fn get_pending_deal_allocation(rt: &mut MockRuntime, deal_id: DealID) -> All *pending_allocations.get(&deal_id_key(deal_id)).unwrap().unwrap_or(&NO_ALLOCATION_ID) } -pub fn get_locked_balance(rt: &mut MockRuntime, addr: Address) -> TokenAmount { - let st: State = rt.get_state(); - let lt = BalanceTable::from_root(&rt.store, &st.locked_table).unwrap(); - lt.get(&addr).unwrap() -} - pub fn get_deal_state(rt: &mut MockRuntime, deal_id: DealID) -> DealState { let st: State = rt.get_state(); let states = DealMetaArray::load(&st.states, &rt.store).unwrap(); @@ -359,10 +356,8 @@ pub fn cron_tick_and_assert_balances( deal_id: DealID, ) -> (TokenAmount, TokenAmount) { // fetch current client and provider escrow balances - let c_locked = get_locked_balance(rt, client_addr); - let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); - let p_locked = get_locked_balance(rt, provider_addr); - let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); + let c_acct = get_balance(rt, &client_addr); + let p_acct = get_balance(rt, &provider_addr); let mut amount_slashed = TokenAmount::zero(); let s = get_deal_state(rt, deal_id); @@ -399,10 +394,10 @@ pub fn cron_tick_and_assert_balances( let payment = duration * d.storage_price_per_epoch; // expected updated amounts - let updated_client_escrow = c_escrow - &payment; - let updated_provider_escrow = (p_escrow + &payment) - &amount_slashed; - let mut updated_client_locked = c_locked - &payment; - let mut updated_provider_locked = p_locked; + let updated_client_escrow = c_acct.balance - &payment; + let updated_provider_escrow = (p_acct.balance + &payment) - &amount_slashed; + let mut updated_client_locked = c_acct.locked - &payment; + let mut updated_provider_locked = p_acct.locked; // if the deal has expired or been slashed, locked amount will be zero for provider and client. let is_deal_expired = payment_end == d.end_epoch; if is_deal_expired || s.slash_epoch != EPOCH_UNDEFINED { @@ -412,10 +407,12 @@ pub fn cron_tick_and_assert_balances( cron_tick(rt); - assert_eq!(updated_client_escrow, get_escrow_balance(rt, &client_addr).unwrap()); - assert_eq!(updated_client_locked, get_locked_balance(rt, client_addr)); - assert_eq!(updated_provider_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); - assert_eq!(updated_provider_locked, get_locked_balance(rt, provider_addr)); + let client_acct = get_balance(rt, &client_addr); + let provider_acct = get_balance(rt, &provider_addr); + assert_eq!(updated_client_escrow, client_acct.balance); + assert_eq!(updated_client_locked, client_acct.locked); + assert_eq!(updated_provider_escrow, provider_acct.balance); + assert_eq!(updated_provider_locked, provider_acct.locked); (payment, amount_slashed) } @@ -424,19 +421,17 @@ pub fn cron_tick_no_change(rt: &mut MockRuntime, client_addr: Address, provider_ let epoch_cid = st.deal_ops_by_epoch; // fetch current client and provider escrow balances - let c_locked = get_locked_balance(rt, client_addr); - let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); - let p_locked = get_locked_balance(rt, provider_addr); - let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); + let client_acct = get_balance(rt, &client_addr); + let provider_acct = get_balance(rt, &provider_addr); cron_tick(rt); let st: State = rt.get_state(); + let new_client_acct = get_balance(rt, &client_addr); + let new_provider_acct = get_balance(rt, &provider_addr); assert_eq!(epoch_cid, st.deal_ops_by_epoch); - assert_eq!(c_locked, get_locked_balance(rt, client_addr)); - assert_eq!(c_escrow, get_escrow_balance(rt, &client_addr).unwrap()); - assert_eq!(p_locked, get_locked_balance(rt, provider_addr)); - assert_eq!(p_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); + assert_eq!(client_acct, new_client_acct); + assert_eq!(provider_acct, new_provider_acct); } pub fn publish_deals( @@ -983,8 +978,9 @@ pub fn terminate_deals_raw( } pub fn assert_account_zero(rt: &mut MockRuntime, addr: Address) { - assert!(get_escrow_balance(rt, &addr).unwrap().is_zero()); - assert!(get_locked_balance(rt, addr).is_zero()); + let account = get_balance(rt, &addr); + assert!(account.balance.is_zero()); + assert!(account.locked.is_zero()); } pub fn verify_deals_for_activation( diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 3a78313f0..b4e0c63e0 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -209,10 +209,9 @@ fn adds_to_provider_escrow_funds() { rt.verify(); - assert_eq!( - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), - TokenAmount::from_atto(tc.total) - ); + let acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(acct.balance, TokenAmount::from_atto(tc.total)); + assert_eq!(acct.locked, TokenAmount::zero()); check_state(&rt); } } @@ -224,7 +223,7 @@ fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() add_participant_funds(&mut rt, CLIENT_ADDR, TokenAmount::from_atto(20u8)); - assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_balance(&mut rt, &CLIENT_ADDR).balance); rt.expect_validate_caller_addr(vec![CLIENT_ADDR]); @@ -245,7 +244,7 @@ fn fails_if_withdraw_from_non_provider_funds_is_not_initiated_by_the_recipient() rt.verify(); // verify there was no withdrawal - assert_eq!(TokenAmount::from_atto(20u8), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(20u8), get_balance(&mut rt, &CLIENT_ADDR).balance); check_state(&rt); } @@ -268,8 +267,12 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun end_epoch, ); let deal = get_deal_proposal(&mut rt, deal_id); - assert_eq!(deal.provider_collateral, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); - assert_eq!(deal.client_balance_requirement(), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(deal.provider_collateral, provider_acct.balance); + assert_eq!(deal.provider_collateral, provider_acct.locked); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(deal.client_balance_requirement(), client_acct.balance); + assert_eq!(deal.client_balance_requirement(), client_acct.locked); let withdraw_amount = TokenAmount::from_atto(1u8); let withdrawable_amount = TokenAmount::zero(); @@ -295,6 +298,10 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun let withdrawable_amount = TokenAmount::from_atto(25u8); add_provider_funds(&mut rt, withdrawable_amount.clone(), &MinerAddresses::default()); + let provider_acct = get_balance(&mut rt, &PROVIDER_ADDR); + assert_eq!(&deal.provider_collateral + &withdrawable_amount, provider_acct.balance); + assert_eq!(deal.provider_collateral, provider_acct.locked); + withdraw_provider_balance( &mut rt, withdraw_amount.clone(), @@ -306,6 +313,10 @@ fn balance_after_withdrawal_must_always_be_greater_than_or_equal_to_locked_amoun // add some more funds to the client & ensure withdrawal is limited by the locked funds add_participant_funds(&mut rt, CLIENT_ADDR, withdrawable_amount.clone()); + let client_acct = get_balance(&mut rt, &CLIENT_ADDR); + assert_eq!(deal.client_balance_requirement() + &withdrawable_amount, client_acct.balance); + assert_eq!(deal.client_balance_requirement(), client_acct.locked); + withdraw_client_balance(&mut rt, withdraw_amount, withdrawable_amount, CLIENT_ADDR); check_state(&rt); } @@ -420,10 +431,7 @@ fn adds_to_non_provider_funds() { rt.verify(); - assert_eq!( - get_escrow_balance(&rt, caller_addr).unwrap(), - TokenAmount::from_atto(tc.total) - ); + assert_eq!(get_balance(&mut rt, caller_addr).balance, TokenAmount::from_atto(tc.total)); check_state(&rt); } } @@ -436,7 +444,7 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(amount, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(amount, get_balance(&mut rt, &PROVIDER_ADDR).balance); // worker calls WithdrawBalance, balance is transferred to owner let withdraw_amount = TokenAmount::from_atto(1); @@ -449,7 +457,7 @@ fn withdraws_from_provider_escrow_funds_and_sends_to_owner() { WORKER_ADDR, ); - assert_eq!(TokenAmount::from_atto(19), get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(TokenAmount::from_atto(19), get_balance(&mut rt, &PROVIDER_ADDR).balance); check_state(&rt); } @@ -460,12 +468,12 @@ fn withdraws_from_non_provider_escrow_funds() { let amount = TokenAmount::from_atto(20); add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, amount); let withdraw_amount = TokenAmount::from_atto(1); withdraw_client_balance(&mut rt, withdraw_amount.clone(), withdraw_amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::from_atto(19)); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, TokenAmount::from_atto(19)); check_state(&rt); } @@ -480,7 +488,7 @@ fn client_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let withdraw_amount = TokenAmount::from_atto(25); withdraw_client_balance(&mut rt, withdraw_amount, amount, CLIENT_ADDR); - assert_eq!(get_escrow_balance(&rt, &CLIENT_ADDR).unwrap(), TokenAmount::zero()); + assert_eq!(get_balance(&mut rt, &CLIENT_ADDR).balance, TokenAmount::zero()); check_state(&rt); } @@ -491,7 +499,7 @@ fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { let amount = TokenAmount::from_atto(20); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); let withdraw_amount = TokenAmount::from_atto(25); withdraw_provider_balance( @@ -503,7 +511,7 @@ fn worker_withdrawing_more_than_escrow_balance_limits_to_available_funds() { WORKER_ADDR, ); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), TokenAmount::zero()); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, TokenAmount::zero()); check_state(&rt); } @@ -554,7 +562,7 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker let amount = TokenAmount::from_atto(20u8); add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); // only signing parties can add balance for client AND provider. rt.expect_validate_caller_addr(vec![OWNER_ADDR, WORKER_ADDR]); @@ -577,7 +585,7 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker rt.verify(); // verify there was no withdrawal - assert_eq!(get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap(), amount); + assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); check_state(&rt); } @@ -820,10 +828,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t rt.verify(); rt.add_balance(amount); - assert_eq!( - deal.client_balance_requirement(), - get_escrow_balance(&rt, &client_resolved).unwrap() - ); + assert_eq!(deal.client_balance_requirement(), get_balance(&mut rt, &client_resolved).balance); // add funds for provider using it's BLS address -> will be resolved and persisted rt.value_received = deal.provider_collateral.clone(); @@ -841,7 +846,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t ); rt.verify(); rt.add_balance(deal.provider_collateral.clone()); - assert_eq!(deal.provider_collateral, get_escrow_balance(&rt, &provider_resolved).unwrap()); + assert_eq!(deal.provider_collateral, get_balance(&mut rt, &provider_resolved).balance); // publish deal using the BLS addresses rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -1056,13 +1061,13 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assert locked balance for all clients and provider let provider_locked_expected = &deal1.provider_collateral + &deal2.provider_collateral + &deal3.provider_collateral; - let client1_locked = get_locked_balance(&mut rt, client1_addr); - let client2_locked = get_locked_balance(&mut rt, client2_addr); - let client3_locked = get_locked_balance(&mut rt, client3_addr); + let client1_locked = get_balance(&mut rt, &client1_addr).locked; + let client2_locked = get_balance(&mut rt, &client2_addr).locked; + let client3_locked = get_balance(&mut rt, &client3_addr).locked; assert_eq!(deal1.client_balance_requirement(), client1_locked); assert_eq!(deal2.client_balance_requirement(), client2_locked); assert_eq!(deal3.client_balance_requirement(), client3_locked); - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); // assert locked funds dealStates let st: State = rt.get_state(); @@ -1095,16 +1100,16 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assert locked balances for clients and provider let provider_locked_expected = &provider_locked_expected + &deal4.provider_collateral + &deal5.provider_collateral; - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); - let client3_locked_updated = get_locked_balance(&mut rt, client3_addr); + let client3_locked_updated = get_balance(&mut rt, &client3_addr).locked; assert_eq!( &client3_locked + &deal4.client_balance_requirement() + &deal5.client_balance_requirement(), client3_locked_updated ); - let client1_locked = get_locked_balance(&mut rt, client1_addr); - let client2_locked = get_locked_balance(&mut rt, client2_addr); + let client1_locked = get_balance(&mut rt, &client1_addr).locked; + let client2_locked = get_balance(&mut rt, &client2_addr).locked; assert_eq!(deal1.client_balance_requirement(), client1_locked); assert_eq!(deal2.client_balance_requirement(), client2_locked); @@ -1138,15 +1143,15 @@ fn publish_multiple_deals_for_different_clients_and_ensure_balances_are_correct( // assertions let st: State = rt.get_state(); let provider2_locked = &deal6.provider_collateral + &deal7.provider_collateral; - assert_eq!(provider2_locked, get_locked_balance(&mut rt, provider2_addr)); - let client1_locked_updated = get_locked_balance(&mut rt, client1_addr); + assert_eq!(provider2_locked, get_balance(&mut rt, &provider2_addr).locked); + let client1_locked_updated = get_balance(&mut rt, &client1_addr).locked; assert_eq!( &deal7.client_balance_requirement() + &client1_locked + &deal6.client_balance_requirement(), client1_locked_updated ); // assert first provider's balance as well - assert_eq!(provider_locked_expected, get_locked_balance(&mut rt, PROVIDER_ADDR)); + assert_eq!(provider_locked_expected, get_balance(&mut rt, &PROVIDER_ADDR).locked); let total_client_collateral_locked = &total_client_collateral_locked + &deal6.client_collateral + &deal7.client_collateral; @@ -1676,7 +1681,7 @@ fn market_actor_deals() { // test adding provider funds let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); - assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(funds, get_balance(&mut rt, &PROVIDER_ADDR).balance); add_participant_funds(&mut rt, CLIENT_ADDR, funds); let mut deal_proposal = @@ -1714,7 +1719,7 @@ fn max_deal_label_size() { // Test adding provider funds from both worker and owner address let funds = TokenAmount::from_atto(20_000_000); add_provider_funds(&mut rt, funds.clone(), &MinerAddresses::default()); - assert_eq!(funds, get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap()); + assert_eq!(funds, get_balance(&mut rt, &PROVIDER_ADDR).balance); add_participant_funds(&mut rt, CLIENT_ADDR, funds); let mut deal_proposal = @@ -1779,10 +1784,10 @@ fn insufficient_client_balance_in_a_batch() { rt.verify(); - assert_eq!(deal2.client_balance_requirement(), get_escrow_balance(&rt, &CLIENT_ADDR).unwrap()); + assert_eq!(deal2.client_balance_requirement(), get_balance(&mut rt, &CLIENT_ADDR).balance); assert_eq!( deal1.provider_balance_requirement().add(deal2.provider_balance_requirement()), - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap() + get_balance(&mut rt, &PROVIDER_ADDR).balance ); let buf1 = RawBytes::serialize(&deal1).expect("failed to marshal deal proposal"); @@ -1894,11 +1899,11 @@ fn insufficient_provider_balance_in_a_batch() { assert_eq!( deal1.client_balance_requirement().add(deal2.client_balance_requirement()), - get_escrow_balance(&rt, &CLIENT_ADDR).unwrap() + get_balance(&mut rt, &CLIENT_ADDR).balance ); assert_eq!( deal2.provider_balance_requirement().clone(), - get_escrow_balance(&rt, &PROVIDER_ADDR).unwrap() + get_balance(&mut rt, &PROVIDER_ADDR).balance ); let buf1 = RawBytes::serialize(&deal1).expect("failed to marshal deal proposal"); From 85427321b9f8c701c0fe2820814f1feb81cc2e80 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Nov 2022 09:23:27 -0500 Subject: [PATCH 182/339] Power actor: Add exported getters for raw power (#810) * Power actor: Add exported getters for raw power * FRC-XXXX is FRC-0042 * Power actor: network_raw_power: Return this_epoch_raw_byte_power * Power actor: miner_raw_power: Return whether above consensus min power * Power actor: types: serialize one-element structs transparently * Address review * Miner actor: Add exported getters for info and monies (#811) * Miner actor: Add exported getters for info and monies * Tweak comment * Miner actor: Replace GetWorker and GetControls with IsControllingAddress * Miner actor: Add exported GetAvailableBalance * Miner actor: Add exported GetVestingFunds * Miner actor: Remove exported monies getters * Miner actor: types: serialize one-element structs transparently * Address review * Address review --- Cargo.lock | 1 + actors/datacap/src/lib.rs | 4 +- actors/init/src/lib.rs | 2 +- actors/miner/src/lib.rs | 94 ++++++++++++++- actors/miner/src/state.rs | 2 +- actors/miner/src/types.rs | 49 +++++++- actors/miner/tests/apply_rewards.rs | 15 ++- actors/miner/tests/exported_getters.rs | 150 ++++++++++++++++++++++++ actors/miner/tests/util.rs | 28 +++-- actors/multisig/src/lib.rs | 2 +- actors/power/Cargo.toml | 1 + actors/power/src/lib.rs | 40 +++++++ actors/power/src/state.rs | 39 +++--- actors/power/src/types.rs | 38 +++++- actors/power/tests/harness/mod.rs | 4 +- actors/power/tests/power_actor_tests.rs | 59 +++++++++- actors/verifreg/src/lib.rs | 2 +- 17 files changed, 480 insertions(+), 50 deletions(-) create mode 100644 actors/miner/tests/exported_getters.rs diff --git a/Cargo.lock b/Cargo.lock index 2a91ab1d1..7fe3049e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,7 @@ dependencies = [ "cid", "fil_actor_reward", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index e824980ad..f17a805c3 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -46,7 +46,7 @@ lazy_static! { /// Datacap actor methods available /// Some methods are available under 2 method nums -- a static number for "private" builtin actor usage, -/// and via FRC-XXXX calling convention, with number determined by method name. +/// and via FRC-0042 calling convention, with number determined by method name. #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { @@ -67,7 +67,7 @@ pub enum Method { Burn = 19, BurnFrom = 20, Allowance = 21, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards NameExported = frc42_dispatch::method_hash!("Name"), SymbolExported = frc42_dispatch::method_hash!("Symbol"), TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index b509bda46..b2fbd271b 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -29,7 +29,7 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, Exec = 2, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ExecExported = frc42_dispatch::method_hash!("Exec"), } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 7a7487309..719554223 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -23,6 +23,7 @@ use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::*; use fvm_shared::smooth::FilterEstimate; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use itertools::Itertools; use log::{error, info, warn}; use multihash::Code::Blake2b256; use num_derive::FromPrimitive; @@ -123,9 +124,14 @@ pub enum Method { ChangeBeneficiary = 30, GetBeneficiary = 31, ExtendSectorExpiration2 = 32, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), + GetOwnerExported = frc42_dispatch::method_hash!("GetOwner"), + IsControllingAddressExported = frc42_dispatch::method_hash!("IsControllingAddress"), + GetSectorSizeExported = frc42_dispatch::method_hash!("GetSectorSize"), + GetAvailableBalanceExported = frc42_dispatch::method_hash!("GetAvailableBalance"), + GetVestingFundsExported = frc42_dispatch::method_hash!("GetVestingFunds"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -205,6 +211,7 @@ impl Actor { Ok(()) } + /// Returns the "controlling" addresses: the owner, the worker, and all control addresses fn control_addresses(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -216,6 +223,71 @@ impl Actor { }) } + /// Returns the owner address + fn get_owner(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let owner = get_miner_info(rt.store(), &state)?.owner; + Ok(GetOwnerReturn { owner }) + } + + /// Returns whether the provided address is "controlling". + /// The "controlling" addresses are the Owner, the Worker, and all Control Addresses. + fn is_controlling_address( + rt: &mut impl Runtime, + params: IsControllingAddressParam, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let input = match rt.resolve_address(¶ms.address) { + Some(a) => Address::new_id(a), + None => return Ok(IsControllingAddressReturn { is_controlling: false }), + }; + let state: State = rt.state()?; + let info = get_miner_info(rt.store(), &state)?; + let is_controlling = info + .control_addresses + .iter() + .chain(&[info.worker, info.owner]) + .into_iter() + .any(|a| *a == input); + + Ok(IsControllingAddressReturn { is_controlling }) + } + + /// Returns the miner's sector size + fn get_sector_size(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let sector_size = get_miner_info(rt.store(), &state)?.sector_size; + Ok(GetSectorSizeReturn { sector_size }) + } + + /// Returns the available balance of this miner. + /// This is calculated as actor balance - (vesting funds + pre-commit deposit + ip requirement + fee debt) + /// Can go negative if the miner is in IP debt. + fn get_available_balance( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let available_balance = + state.get_available_balance(&rt.current_balance()).map_err(|e| { + actor_error!(illegal_state, "failed to calculate available balance: {}", e) + })?; + Ok(GetAvailableBalanceReturn { available_balance }) + } + + /// Returns the funds vesting in this miner as a list of (vesting_epoch, vesting_amount) tuples. + fn get_vesting_funds(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let vesting_funds = state + .load_vesting_funds(rt.store()) + .map_err(|e| actor_error!(illegal_state, "failed to load vesting funds: {}", e))?; + let ret = vesting_funds.funds.into_iter().map(|v| (v.epoch, v.amount)).collect_vec(); + Ok(GetVestingFundsReturn { vesting_funds: ret }) + } + /// Will ALWAYS overwrite the existing control addresses with the control addresses passed in the params. /// If an empty addresses vector is passed, the control addresses will be cleared. /// A worker change will be scheduled if the worker passed in the params is different from the existing worker. @@ -5000,6 +5072,26 @@ impl ActorCode for Actor { Ok(RawBytes::default()) } None => Err(actor_error!(unhandled_message, "Invalid method")), + Some(Method::GetOwnerExported) => { + let res = Self::get_owner(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::IsControllingAddressExported) => { + let res = Self::is_controlling_address(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetSectorSizeExported) => { + let res = Self::get_sector_size(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetAvailableBalanceExported) => { + let res = Self::get_available_balance(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetVestingFundsExported) => { + let res = Self::get_vesting_funds(rt)?; + Ok(RawBytes::serialize(res)?) + } } } } diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 401792706..270cd3e4e 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -983,7 +983,7 @@ impl State { &self, actor_balance: &TokenAmount, ) -> anyhow::Result { - // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit + // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit - &self.initial_pledge Ok(self.get_unlocked_balance(actor_balance)? - &self.fee_debt) } diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 0d8dd31f1..49c8290d7 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -13,7 +13,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{ PoStProof, RegisteredPoStProof, RegisteredSealProof, RegisteredUpdateProof, SectorNumber, - StoragePower, + SectorSize, StoragePower, }; use fvm_shared::smooth::FilterEstimate; @@ -482,3 +482,50 @@ pub struct GetBeneficiaryReturn { } impl Cbor for GetBeneficiaryReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetOwnerReturn { + pub owner: Address, +} + +impl Cbor for GetOwnerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct IsControllingAddressParam { + pub address: Address, +} + +impl Cbor for IsControllingAddressParam {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct IsControllingAddressReturn { + pub is_controlling: bool, +} + +impl Cbor for IsControllingAddressReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetSectorSizeReturn { + pub sector_size: SectorSize, +} + +impl Cbor for GetSectorSizeReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GetAvailableBalanceReturn { + pub available_balance: TokenAmount, +} + +impl Cbor for GetAvailableBalanceReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetVestingFundsReturn { + pub vesting_funds: Vec<(ChainEpoch, TokenAmount)>, +} + +impl Cbor for GetVestingFundsReturn {} diff --git a/actors/miner/tests/apply_rewards.rs b/actors/miner/tests/apply_rewards.rs index 1bec82b51..44f36c332 100644 --- a/actors/miner/tests/apply_rewards.rs +++ b/actors/miner/tests/apply_rewards.rs @@ -18,6 +18,7 @@ use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; mod util; + use fil_actor_miner::testing::check_state_invariants; use util::*; @@ -165,11 +166,12 @@ fn rewards_pay_back_fee_debt() { assert!(st.locked_funds.is_zero()); let amt = rt.get_balance(); - let available_before = st.get_available_balance(&amt).unwrap(); + let available_before = h.get_available_balance(&mut rt).unwrap(); assert!(available_before.is_positive()); let init_fee_debt: TokenAmount = 2 * &amt; // FeeDebt twice total balance st.fee_debt = init_fee_debt.clone(); - let available_after = st.get_available_balance(&amt).unwrap(); + rt.replace_state(&st); + let available_after = h.get_available_balance(&mut rt).unwrap(); assert!(available_after.is_negative()); rt.replace_state(&st); @@ -178,7 +180,7 @@ fn rewards_pay_back_fee_debt() { let penalty = TokenAmount::zero(); // manually update actor balance to include the added funds from outside let new_balance = &amt + &reward; - rt.set_balance(new_balance.clone()); + rt.set_balance(new_balance); // pledge change is new reward - reward taken for fee debt // 3*LockedRewardFactor*amt - 2*amt = remainingLocked @@ -203,7 +205,7 @@ fn rewards_pay_back_fee_debt() { BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), - expect_burnt.clone(), + expect_burnt, RawBytes::default(), ExitCode::OK, ); @@ -212,13 +214,10 @@ fn rewards_pay_back_fee_debt() { rt.call::(Method::ApplyRewards as u64, &RawBytes::serialize(params).unwrap()).unwrap(); rt.verify(); - // Set balance to deduct fee - let final_balance = &new_balance - &expect_burnt; - let st = h.get_state(&rt); // balance funds used to pay off fee debt // available balance should be 2 - let available_balance = st.get_available_balance(&final_balance).unwrap(); + let available_balance = h.get_available_balance(&mut rt).unwrap(); assert_eq!(available_before + reward - init_fee_debt - &remaining_locked, available_balance); assert!(!st.fee_debt.is_positive()); // remaining funds locked in vesting table diff --git a/actors/miner/tests/exported_getters.rs b/actors/miner/tests/exported_getters.rs new file mode 100644 index 000000000..459ec768d --- /dev/null +++ b/actors/miner/tests/exported_getters.rs @@ -0,0 +1,150 @@ +use fil_actor_miner::{ + Actor, GetAvailableBalanceReturn, GetOwnerReturn, GetSectorSizeReturn, + IsControllingAddressParam, IsControllingAddressReturn, Method, +}; +use fil_actors_runtime::cbor::serialize; +use fil_actors_runtime::test_utils::make_identity_cid; +use fil_actors_runtime::INIT_ACTOR_ADDR; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, sector::MAX_SECTOR_NUMBER}; +use std::ops::Sub; + +mod util; + +use util::*; + +const PERIOD_OFFSET: ChainEpoch = 100; + +// an expiration ~10 days greater than effective min expiration taking into account 30 days max +// between pre and prove commit +const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; + +#[test] +fn info_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.set_balance(BIG_BALANCE.clone()); + h.construct_and_verify(&mut rt); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + // owner is good + rt.expect_validate_caller_any(); + let owner_ret: GetOwnerReturn = rt + .call::(Method::GetOwnerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.owner, owner_ret.owner); + + // check that the controlling addresses all return true + for control in h.control_addrs.iter().chain(&[h.worker, h.owner]) { + rt.expect_validate_caller_any(); + let is_control_ret: IsControllingAddressReturn = rt + .call::( + Method::IsControllingAddressExported as u64, + &serialize(&IsControllingAddressParam { address: *control }, "serializing control") + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + assert!(is_control_ret.is_controlling); + + rt.verify(); + } + + // check that a non-controlling address doesn't return true + + rt.expect_validate_caller_any(); + let is_control_ret: IsControllingAddressReturn = rt + .call::( + Method::IsControllingAddressExported as u64, + &serialize( + &IsControllingAddressParam { address: INIT_ACTOR_ADDR }, + "serializing control", + ) + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + assert!(!is_control_ret.is_controlling); + + rt.verify(); + + // sector size is good + rt.expect_validate_caller_any(); + let sector_size_ret: GetSectorSizeReturn = rt + .call::(Method::GetSectorSizeExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.sector_size, sector_size_ret.sector_size); + + h.check_state(&rt); +} + +#[test] +fn collateral_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.balance.replace(BIG_BALANCE.clone()); + + let precommit_epoch = PERIOD_OFFSET + 1; + rt.set_epoch(precommit_epoch); + + h.construct_and_verify(&mut rt); + let dl_info = h.deadline(&rt); + + // Precommit a sector + // Use the max sector number to make sure everything works. + let sector_no = MAX_SECTOR_NUMBER; + let prove_commit_epoch = precommit_epoch + rt.policy.pre_commit_challenge_delay + 1; + let expiration = + dl_info.period_end() + DEFAULT_SECTOR_EXPIRATION * rt.policy.wpost_proving_period; // something on deadline boundary but > 180 days + + let precommit_params = + h.make_pre_commit_params(sector_no, precommit_epoch - 1, expiration, vec![]); + let precommit = + h.pre_commit_sector_and_get(&mut rt, precommit_params, PreCommitConfig::empty(), true); + + // run prove commit logic + rt.set_epoch(prove_commit_epoch); + let actor_balance = TokenAmount::from_whole(1000); + rt.balance.replace(actor_balance.clone()); + let pcc = ProveCommitConfig::empty(); + + let sector = h + .prove_commit_sector_and_confirm( + &mut rt, + &precommit, + h.make_prove_commit_params(sector_no), + pcc, + ) + .unwrap(); + + // query available balance + + rt.expect_validate_caller_any(); + let available_balance_ret: GetAvailableBalanceReturn = rt + .call::(Method::GetAvailableBalanceExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert_eq!(actor_balance.sub(sector.initial_pledge), available_balance_ret.available_balance); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index e5929b958..5064f596b 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -22,14 +22,14 @@ use fil_actor_miner::{ CronEventPayload, Deadline, DeadlineInfo, Deadlines, DeclareFaultsParams, DeclareFaultsRecoveredParams, DeferredCronEventParams, DisputeWindowedPoStParams, ExpirationQueue, ExpirationSet, ExtendSectorExpiration2Params, ExtendSectorExpirationParams, - FaultDeclaration, GetBeneficiaryReturn, GetControlAddressesReturn, Method, - MinerConstructorParams as ConstructorParams, MinerInfo, Partition, PendingBeneficiaryChange, - PoStPartition, PowerPair, PreCommitSectorBatchParams, PreCommitSectorBatchParams2, - PreCommitSectorParams, ProveCommitSectorParams, RecoveryDeclaration, - ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, SectorPreCommitOnChainInfo, - Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, TerminationDeclaration, - VestingFunds, WindowedPoSt, WithdrawBalanceParams, WithdrawBalanceReturn, - CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, + FaultDeclaration, GetAvailableBalanceReturn, GetBeneficiaryReturn, GetControlAddressesReturn, + Method, MinerConstructorParams as ConstructorParams, MinerInfo, Partition, + PendingBeneficiaryChange, PoStPartition, PowerPair, PreCommitSectorBatchParams, + PreCommitSectorBatchParams2, PreCommitSectorParams, ProveCommitSectorParams, + RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, + SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, + TerminationDeclaration, VestingFunds, WindowedPoSt, WithdrawBalanceParams, + WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, }; use fil_actor_miner::{Method as MinerMethod, ProveCommitAggregateParams}; use fil_actor_power::{ @@ -860,6 +860,7 @@ impl ActorHarness { pc: &SectorPreCommitOnChainInfo, params: ProveCommitSectorParams, ) -> Result<(), ActorError> { + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); let seal_rand = TEST_RANDOMNESS_ARRAY_FROM_ONE; let seal_int_rand = TEST_RANDOMNESS_ARRAY_FROM_TWO; let interactive_epoch = pc.pre_commit_epoch + rt.policy.pre_commit_challenge_delay; @@ -2531,6 +2532,17 @@ impl ActorHarness { } ret } + + 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.expect_validate_caller_any(); + let available_balance_ret: GetAvailableBalanceReturn = rt + .call::(Method::GetAvailableBalanceExported as u64, &RawBytes::default())? + .deserialize()?; + rt.verify(); + Ok(available_balance_ret.available_balance) + } } #[allow(dead_code)] diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 56c413292..8bd64b74a 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -42,7 +42,7 @@ pub enum Method { SwapSigner = 7, ChangeNumApprovalsThreshold = 8, LockBalance = 9, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards ProposeExported = frc42_dispatch::method_hash!("Propose"), ApproveExported = frc42_dispatch::method_hash!("Approve"), CancelExported = frc42_dispatch::method_hash!("Cancel"), diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 471bb44ca..93b5f21a8 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,6 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +frc42_dispatch = "1.0.0" fvm_shared = { version = "2.0.0-alpha.2", default-features = false } fvm_ipld_hamt = "0.5.1" num-traits = "0.2.14" diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 21dc612b6..43437812c 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -61,12 +61,16 @@ pub enum Method { // OnConsensusFault = 7, SubmitPoRepForBulkVerify = 8, CurrentTotalPower = 9, + // Method numbers derived from FRC-0042 standards + NetworkRawPowerExported = frc42_dispatch::method_hash!("NetworkRawPower"), + MinerRawPowerExported = frc42_dispatch::method_hash!("MinerRawPower"), } pub const ERR_TOO_MANY_PROVE_COMMITS: ExitCode = ExitCode::new(32); /// Storage Power Actor pub struct Actor; + impl Actor { /// Constructor for StoragePower actor fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { @@ -364,6 +368,34 @@ impl Actor { }) } + /// Returns the total raw power of the network. + /// This is defined as the sum of the active (i.e. non-faulty) byte commitments + /// of all miners that have more than the consensus minimum amount of storage active. + /// This value is static over an epoch, and does NOT get updated as messages are executed. + /// It is recalculated after all messages at an epoch have been executed. + fn network_raw_power(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(NetworkRawPowerReturn { raw_byte_power: st.this_epoch_raw_byte_power }) + } + + /// Returns the raw power claimed by the specified miner, + /// and whether the miner has more than the consensus minimum amount of storage active. + /// The raw power is defined as the active (i.e. non-faulty) byte commitments of the miner. + fn miner_raw_power( + rt: &mut impl Runtime, + params: MinerRawPowerParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + let (raw_byte_power, meets_consensus_minimum) = + st.miner_nominal_power_meets_consensus_minimum(rt.policy(), rt.store(), params.miner)?; + + Ok(MinerRawPowerReturn { raw_byte_power, meets_consensus_minimum }) + } + fn process_batch_proof_verifies( rt: &mut impl Runtime, rewret: &ThisEpochRewardReturn, @@ -660,6 +692,14 @@ impl ActorCode for Actor { let res = Self::current_total_power(rt)?; Ok(RawBytes::serialize(res)?) } + Some(Method::NetworkRawPowerExported) => { + let res = Self::network_raw_power(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::MinerRawPowerExported) => { + let res = Self::miner_raw_power(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } diff --git a/actors/power/src/state.rs b/actors/power/src/state.rs index 91739d0eb..fb029d76f 100644 --- a/actors/power/src/state.rs +++ b/actors/power/src/state.rs @@ -3,12 +3,12 @@ use std::ops::Neg; -use anyhow::{anyhow, Context}; +use anyhow::anyhow; use cid::Cid; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::{ actor_error, make_empty_map, make_map_with_root, make_map_with_root_and_bitwidth, - ActorDowncast, ActorError, Map, Multimap, + ActorDowncast, ActorError, AsActorError, Map, Multimap, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -21,7 +21,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; use fvm_shared::smooth::{AlphaBetaFilter, FilterEstimate, DEFAULT_ALPHA, DEFAULT_BETA}; -use fvm_shared::HAMT_BIT_WIDTH; +use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; use integer_encoding::VarInt; use lazy_static::lazy_static; use num_traits::Signed; @@ -103,26 +103,37 @@ impl State { &self, policy: &Policy, s: &BS, - miner: &Address, - ) -> anyhow::Result { - let claims = make_map_with_root_and_bitwidth(&self.claims, s, HAMT_BIT_WIDTH)?; + miner: ActorID, + ) -> Result<(StoragePower, bool), ActorError> { + let claims = make_map_with_root_and_bitwidth(&self.claims, s, HAMT_BIT_WIDTH) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to load claims for miner: {}", miner) + })?; - let claim = - get_claim(&claims, miner)?.ok_or_else(|| anyhow!("no claim for actor: {}", miner))?; + let claim = get_claim(&claims, &Address::new_id(miner)) + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("failed to get claim for miner: {}", miner) + })? + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("no claim for actor: {}", miner) + })?; - let miner_nominal_power = &claim.raw_byte_power; + let miner_nominal_power = claim.raw_byte_power.clone(); let miner_min_power = consensus_miner_min_power(policy, claim.window_post_proof_type) - .context("could not get miner min power from proof type: {}")?; + .context_code( + ExitCode::USR_ILLEGAL_STATE, + "could not get miner min power from proof type: {}", + )?; - if miner_nominal_power >= &miner_min_power { + if miner_nominal_power >= miner_min_power { // If miner is larger than min power requirement, valid - Ok(true) + Ok((miner_nominal_power, true)) } else if self.miner_above_min_power_count >= CONSENSUS_MINER_MIN_MINERS { // if min consensus miners requirement met, return false - Ok(false) + Ok((miner_nominal_power, false)) } else { // if fewer miners than consensus minimum, return true if non-zero power - Ok(miner_nominal_power.is_positive()) + Ok((miner_nominal_power.clone(), miner_nominal_power.is_positive())) } } diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 521be8f83..26f63b88d 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -9,6 +9,7 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; use fvm_shared::smooth::FilterEstimate; +use fvm_shared::ActorID; pub type SectorTermination = i64; @@ -23,7 +24,7 @@ pub const CRON_QUEUE_HAMT_BITWIDTH: u32 = 6; pub const CRON_QUEUE_AMT_BITWIDTH: u32 = 6; pub const PROOF_VALIDATION_BATCH_AMT_BITWIDTH: u32 = 4; -#[derive(Serialize_tuple, Deserialize_tuple, Clone)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] pub struct CreateMinerParams { pub owner: Address, pub worker: Address, @@ -32,9 +33,10 @@ pub struct CreateMinerParams { pub peer: Vec, pub multiaddrs: Vec, } + impl Cbor for CreateMinerParams {} -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct CreateMinerReturn { /// Canonical ID-based address for the actor. pub id_address: Address, @@ -42,7 +44,7 @@ pub struct CreateMinerReturn { pub robust_address: Address, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct UpdateClaimedPowerParams { #[serde(with = "bigint_ser")] pub raw_byte_delta: StoragePower, @@ -50,13 +52,13 @@ pub struct UpdateClaimedPowerParams { pub quality_adjusted_delta: StoragePower, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct EnrollCronEventParams { pub event_epoch: ChainEpoch, pub payload: RawBytes, } -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] pub struct CurrentTotalPowerReturn { #[serde(with = "bigint_ser")] pub raw_byte_power: StoragePower, @@ -65,3 +67,29 @@ pub struct CurrentTotalPowerReturn { pub pledge_collateral: TokenAmount, pub quality_adj_power_smoothed: FilterEstimate, } + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct NetworkRawPowerReturn { + #[serde(with = "bigint_ser")] + pub raw_byte_power: StoragePower, +} + +impl Cbor for NetworkRawPowerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerRawPowerParams { + pub miner: ActorID, +} + +impl Cbor for MinerRawPowerParams {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct MinerRawPowerReturn { + #[serde(with = "bigint_ser")] + pub raw_byte_power: StoragePower, + pub meets_consensus_minimum: bool, +} + +impl Cbor for MinerRawPowerReturn {} diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 5849e2b9c..e82fa8b5d 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -222,9 +222,7 @@ impl Harness { pub fn get_claim(&self, rt: &MockRuntime, miner: &Address) -> Option { let st: State = rt.get_state(); - let claims = - make_map_with_root_and_bitwidth(&st.claims, rt.store(), HAMT_BIT_WIDTH).unwrap(); - claims.get(&miner.to_bytes()).unwrap().cloned() + st.get_claim(rt.store(), miner).unwrap() } pub fn delete_claim(&mut self, rt: &mut MockRuntime, miner: &Address) { diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index ff569abb2..184889025 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -2,8 +2,8 @@ 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, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, - SYSTEM_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{runtime::Policy, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, RawBytes}; @@ -17,9 +17,11 @@ use num_traits::Zero; use std::ops::Neg; use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, CreateMinerParams, EnrollCronEventParams, - Method, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, + EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, + State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, }; +use fil_actors_runtime::cbor::serialize; use crate::harness::*; @@ -589,6 +591,55 @@ fn claimed_power_is_externally_available() { h.check_state(&rt); } +#[test] +fn get_network_and_miner_power() { + let power_unit = &consensus_miner_min_power( + &Policy::default(), + RegisteredPoStProof::StackedDRGWindow32GiBV1, + ) + .unwrap(); + + let (mut h, mut rt) = setup(); + + h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap(); + h.update_claimed_power(&mut rt, MINER1, power_unit, power_unit); + + // manually update state in lieu of cron running + let mut state: State = rt.get_state(); + state.this_epoch_raw_byte_power = power_unit.clone(); + rt.replace_state(&state); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + rt.expect_validate_caller_any(); + let network_power: NetworkRawPowerReturn = rt + .call::(Method::NetworkRawPowerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(power_unit, &network_power.raw_byte_power); + + rt.expect_validate_caller_any(); + let miner_power: MinerRawPowerReturn = rt + .call::( + Method::MinerRawPowerExported as u64, + &serialize( + &MinerRawPowerParams { miner: MINER1.id().unwrap() }, + "serializing MinerRawPowerParams", + ) + .unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(power_unit, &miner_power.raw_byte_power); + + h.check_state(&rt); +} + #[test] fn given_no_miner_claim_update_pledge_total_should_abort() { let (mut h, mut rt) = setup(); diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index eb6716cc2..d7962cfd2 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -60,7 +60,7 @@ pub enum Method { GetClaims = 10, ExtendClaimTerms = 11, RemoveExpiredClaims = 12, - // Method numbers derived from FRC-XXXX standards + // Method numbers derived from FRC-0042 standards RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), From 5c3ca4346fcedfd1675c353f125ab36a407d297b Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Thu, 10 Nov 2022 05:58:34 +1100 Subject: [PATCH 183/339] Built-in market API for deal proposal metadata (#818) --- actors/market/src/lib.rs | 141 +++++++++++++++++++++++++++ actors/market/src/types.rs | 69 +++++++++++++ actors/market/tests/deal_api_test.rs | 81 +++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 actors/market/tests/deal_api_test.rs diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 3bbbc0e19..8d8ebb591 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -77,6 +77,15 @@ pub enum Method { AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), + GetDealDataCommitmentExported = frc42_dispatch::method_hash!("GetDealDataCommitment"), + GetDealClientExported = frc42_dispatch::method_hash!("GetDealClient"), + GetDealProviderExported = frc42_dispatch::method_hash!("GetDealProvider"), + GetDealLabelExported = frc42_dispatch::method_hash!("GetDealLabel"), + GetDealTermExported = frc42_dispatch::method_hash!("GetDealTerm"), + GetDealEpochPriceExported = frc42_dispatch::method_hash!("GetDealEpochPrice"), + GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), + GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), + GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), } /// Market Actor @@ -821,6 +830,101 @@ impl Actor { } Ok(()) } + + /// Returns the data commitment and size of a deal proposal. + /// This will be available after the deal is published (whether or not is is activated) + /// and up until some undefined period after it is terminated. + fn get_deal_data_commitment( + rt: &mut impl Runtime, + params: GetDealDataCommitmentParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealDataCommitmentReturn { data: found.piece_cid, size: found.piece_size }) + } + + /// Returns the client of a deal proposal. + fn get_deal_client( + rt: &mut impl Runtime, + params: GetDealClientParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealClientReturn { client: found.client.id().unwrap() }) + } + + /// Returns the provider of a deal proposal. + fn get_deal_provider( + rt: &mut impl Runtime, + params: GetDealProviderParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealProviderReturn { provider: found.provider.id().unwrap() }) + } + + /// Returns the label of a deal proposal. + fn get_deal_label( + rt: &mut impl Runtime, + params: GetDealLabelParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealLabelReturn { label: found.label }) + } + + /// Returns the start and end epochs of a deal proposal. + /// The deal term is a half-open range, exclusive of the end epoch. + fn get_deal_term( + rt: &mut impl Runtime, + params: GetDealTermParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealTermReturn { start: found.start_epoch, end: found.end_epoch }) + } + + /// Returns the per-epoch price of a deal proposal. + fn get_deal_epoch_price( + rt: &mut impl Runtime, + params: GetDealEpochPriceParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealEpochPriceReturn { price_per_epoch: found.storage_price_per_epoch }) + } + + /// Returns the client collateral requirement for a deal proposal. + fn get_deal_client_collateral( + rt: &mut impl Runtime, + params: GetDealClientCollateralParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealClientCollateralReturn { collateral: found.client_collateral }) + } + + /// Returns the provider collateral requirement for a deal proposal. + fn get_deal_provider_collateral( + rt: &mut impl Runtime, + params: GetDealProviderCollateralParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealProviderCollateralReturn { collateral: found.provider_collateral }) + } + + /// Returns the verified flag for a deal proposal. + /// Note that the source of truth for verified allocations and claims is + /// the verified registry actor. + fn get_deal_verified( + rt: &mut impl Runtime, + params: GetDealVerifiedParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let found = rt.state::()?.get_proposal(rt.store(), params.id)?; + Ok(GetDealVerifiedReturn { verified: found.verified_deal }) + } } fn compute_data_commitment( @@ -1240,6 +1344,43 @@ impl ActorCode for Actor { let res = Self::get_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetDealDataCommitmentExported) => { + let res = Self::get_deal_data_commitment(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealClientExported) => { + let res = Self::get_deal_client(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealProviderExported) => { + let res = Self::get_deal_provider(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealLabelExported) => { + let res = Self::get_deal_label(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealTermExported) => { + let res = Self::get_deal_term(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealEpochPriceExported) => { + let res = Self::get_deal_epoch_price(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealClientCollateralExported) => { + let res = Self::get_deal_client_collateral(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealProviderCollateralExported) => { + let res = + Self::get_deal_provider_collateral(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetDealVerifiedExported) => { + let res = Self::get_deal_verified(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index bac78d0df..afdfa2da4 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -15,6 +15,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::piece::PaddedPieceSize; use fvm_shared::ActorID; +use crate::Label; use fvm_shared::sector::RegisteredSealProof; use super::deal::{ClientDealProposal, DealProposal, DealState}; @@ -136,3 +137,71 @@ pub struct SectorDataSpec { pub deal_ids: Vec, pub sector_type: RegisteredSealProof, } + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct DealQueryParams { + pub id: DealID, +} + +pub type GetDealDataCommitmentParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealDataCommitmentReturn { + pub data: Cid, + pub size: PaddedPieceSize, +} + +pub type GetDealClientParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealClientReturn { + pub client: ActorID, +} + +pub type GetDealProviderParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealProviderReturn { + pub provider: ActorID, +} + +pub type GetDealLabelParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealLabelReturn { + pub label: Label, +} + +pub type GetDealTermParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealTermReturn { + pub start: ChainEpoch, // First epoch for the deal (inclusive) + pub end: ChainEpoch, // Epoch at which the deal expires (i.e. exclusive). +} + +pub type GetDealEpochPriceParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealEpochPriceReturn { + pub price_per_epoch: TokenAmount, +} + +pub type GetDealClientCollateralParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealClientCollateralReturn { + pub collateral: TokenAmount, +} + +pub type GetDealProviderCollateralParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealProviderCollateralReturn { + pub collateral: TokenAmount, +} + +pub type GetDealVerifiedParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct GetDealVerifiedReturn { + pub verified: bool, +} diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs new file mode 100644 index 000000000..4471f0f43 --- /dev/null +++ b/actors/market/tests/deal_api_test.rs @@ -0,0 +1,81 @@ +use fvm_ipld_encoding::RawBytes; +use fvm_shared::clock::ChainEpoch; +use serde::de::DeserializeOwned; + +use fil_actor_market::{ + Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, + GetDealDataCommitmentReturn, GetDealEpochPriceReturn, GetDealLabelReturn, + GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, + GetDealVerifiedReturn, Method, +}; +use fil_actors_runtime::network::EPOCHS_IN_DAY; +use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; +use harness::*; + +mod harness; + +#[test] +fn proposal_data() { + let start_epoch = 1000; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + let publish_epoch = ChainEpoch::from(1); + + let mut rt = setup(); + rt.set_epoch(publish_epoch); + let next_allocation_id = 1; + + let proposal = generate_deal_and_add_funds( + &mut rt, + CLIENT_ADDR, + &MinerAddresses::default(), + start_epoch, + end_epoch, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + let id = + publish_deals(&mut rt, &MinerAddresses::default(), &[proposal.clone()], next_allocation_id) + [0]; + + let data: GetDealDataCommitmentReturn = + query_deal(&mut rt, Method::GetDealDataCommitmentExported, id); + assert_eq!(proposal.piece_cid, data.data); + assert_eq!(proposal.piece_size, data.size); + + let client: GetDealClientReturn = query_deal(&mut rt, Method::GetDealClientExported, id); + assert_eq!(proposal.client.id().unwrap(), client.client); + + let provider: GetDealProviderReturn = query_deal(&mut rt, Method::GetDealProviderExported, id); + assert_eq!(proposal.provider.id().unwrap(), provider.provider); + + let label: GetDealLabelReturn = query_deal(&mut rt, Method::GetDealLabelExported, id); + assert_eq!(proposal.label, label.label); + + let term: GetDealTermReturn = query_deal(&mut rt, Method::GetDealTermExported, id); + assert_eq!(proposal.start_epoch, term.start); + assert_eq!(proposal.end_epoch, term.end); + + let price: GetDealEpochPriceReturn = query_deal(&mut rt, Method::GetDealEpochPriceExported, id); + assert_eq!(proposal.storage_price_per_epoch, price.price_per_epoch); + + let client_collateral: GetDealClientCollateralReturn = + query_deal(&mut rt, Method::GetDealClientCollateralExported, id); + assert_eq!(proposal.client_collateral, client_collateral.collateral); + + let provider_collateral: GetDealProviderCollateralReturn = + query_deal(&mut rt, Method::GetDealProviderCollateralExported, id); + assert_eq!(proposal.provider_collateral, provider_collateral.collateral); + + let verified: GetDealVerifiedReturn = query_deal(&mut rt, Method::GetDealVerifiedExported, id); + assert_eq!(proposal.verified_deal, verified.verified); + + check_state(&rt); +} + +fn query_deal(rt: &mut MockRuntime, method: Method, id: u64) -> T { + let params = DealQueryParams { id }; + rt.expect_validate_caller_any(); + rt.call::(method as u64, &RawBytes::serialize(params).unwrap()) + .unwrap() + .deserialize() + .unwrap() +} From 68aaba2f86c6d369307eb933965045e23e3d6880 Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:27:35 -0500 Subject: [PATCH 184/339] Call exported authenticate method from PSD (#829) Co-authored-by: zenground0 --- actors/account/tests/account_actor_test.rs | 4 ++-- actors/market/src/ext.rs | 3 ++- test_vm/src/util.rs | 2 +- test_vm/tests/account_authenticate_message_test.rs | 6 +++--- test_vm/tests/publish_deals_test.rs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index 4f9bdf3a0..de2792177 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -103,7 +103,7 @@ fn authenticate_message() { }); assert_eq!( RawBytes::default(), - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms).unwrap() + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms).unwrap() ); rt.verify(); @@ -118,7 +118,7 @@ fn authenticate_message() { expect_abort_contains_message( ExitCode::USR_ILLEGAL_ARGUMENT, "bad signature", - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), + rt.call::(Method::AuthenticateMessageExported as MethodNum, ¶ms), ); rt.verify(); diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index ff4082682..63d62b1d2 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -10,7 +10,8 @@ use fvm_shared::smooth::FilterEstimate; pub mod account { use super::*; - pub const AUTHENTICATE_MESSAGE_METHOD: u64 = 3; + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); #[derive(Serialize_tuple, Deserialize_tuple)] pub struct AuthenticateMessageParams { diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index c04139d0a..72ce940dc 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -1130,7 +1130,7 @@ pub fn market_publish_deal( }, ExpectInvocation { to: deal_client, - method: AccountMethod::AuthenticateMessage as u64, + method: AccountMethod::AuthenticateMessageExported as u64, ..Default::default() }, ]; diff --git a/test_vm/tests/account_authenticate_message_test.rs b/test_vm/tests/account_authenticate_message_test.rs index bc06b095a..5506ec617 100644 --- a/test_vm/tests/account_authenticate_message_test.rs +++ b/test_vm/tests/account_authenticate_message_test.rs @@ -1,5 +1,5 @@ use fil_actor_account::types::AuthenticateMessageParams; -use fil_actor_account::Method::AuthenticateMessage; +use fil_actor_account::Method::AuthenticateMessageExported; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::bigint::Zero; @@ -32,7 +32,7 @@ fn account_authenticate_message() { addr, addr, TokenAmount::zero(), - AuthenticateMessage as u64, + AuthenticateMessageExported as u64, authenticate_message_params, ); @@ -44,7 +44,7 @@ fn account_authenticate_message() { addr, addr, TokenAmount::zero(), - AuthenticateMessage as u64, + AuthenticateMessageExported as u64, authenticate_message_params, ExitCode::USR_ILLEGAL_ARGUMENT, ); diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 1e0febffb..242544165 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -553,7 +553,7 @@ fn psd_bad_sig() { }, ExpectInvocation { to: a.client1, - method: AccountMethod::AuthenticateMessage as u64, + method: AccountMethod::AuthenticateMessageExported as u64, params: Some( serialize( &AuthenticateMessageParams { From 0e186836c0123567408105ffced8b96e6a89c755 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Nov 2022 19:06:55 -0500 Subject: [PATCH 185/339] Drop CALLER_TYPES_SIGNABLE and signable caller validation (#821) --- actors/market/src/lib.rs | 12 ++-- .../market/tests/cron_tick_timedout_deals.rs | 4 +- actors/market/tests/harness.rs | 16 +++-- actors/market/tests/market_actor_test.rs | 62 +++++-------------- .../tests/publish_storage_deals_failures.rs | 37 ++--------- actors/miner/src/lib.rs | 61 +++++++----------- actors/miner/src/state.rs | 18 +++--- .../tests/miner_actor_test_construction.rs | 28 --------- actors/miner/tests/miner_actor_test_wpost.rs | 8 +-- actors/miner/tests/state_harness.rs | 5 +- actors/miner/tests/util.rs | 8 +-- actors/multisig/src/lib.rs | 8 +-- actors/multisig/tests/multisig_actor_test.rs | 20 +++--- actors/multisig/tests/util.rs | 8 +-- actors/power/src/lib.rs | 2 +- actors/power/tests/harness/mod.rs | 3 +- actors/power/tests/power_actor_tests.rs | 32 +--------- runtime/src/builtin/shared.rs | 6 -- 18 files changed, 97 insertions(+), 241 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 8d8ebb591..4e44c126b 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -32,9 +32,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, - AsActorError, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, - DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, - VERIFIED_REGISTRY_ACTOR_ADDR, + AsActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, + REWARD_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use crate::ext::verifreg::{AllocationID, AllocationRequest}; @@ -112,8 +111,7 @@ impl Actor { )); } - // only signing parties can add balance for client AND provider. - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let (nominal, _, _) = escrow_address(rt, &provider_or_client)?; @@ -183,9 +181,7 @@ impl Actor { rt: &mut impl Runtime, params: PublishStorageDealsParams, ) -> Result { - // Deal message must have a From field identical to the provider of all the deals. - // This allows us to retain and verify only the client's signature in each deal proposal itself. - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; if params.deals.is_empty() { return Err(actor_error!(illegal_argument, "Empty deals parameter")); } diff --git a/actors/market/tests/cron_tick_timedout_deals.rs b/actors/market/tests/cron_tick_timedout_deals.rs index dfdff2e45..9a5a627a1 100644 --- a/actors/market/tests/cron_tick_timedout_deals.rs +++ b/actors/market/tests/cron_tick_timedout_deals.rs @@ -6,7 +6,7 @@ use fil_actor_market::{ }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE}; +use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::signature::Signature; @@ -84,7 +84,7 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l let client_deal_proposal = ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig }; let params = PublishStorageDealsParams { deals: vec![client_deal_proposal] }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 7bccd1506..36a071c2d 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -27,8 +27,8 @@ use fil_actors_runtime::{ network::EPOCHS_IN_DAY, runtime::{builtins::Type, Policy, Runtime}, test_utils::*, - ActorError, BatchReturn, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, - CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, + ActorError, BatchReturn, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, + DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_encoding::{to_vec, RawBytes}; @@ -167,7 +167,7 @@ pub fn add_provider_funds(rt: &mut MockRuntime, amount: TokenAmount, addrs: &Min rt.set_value(amount.clone()); rt.set_address_actor_type(addrs.provider, *MINER_ACTOR_CODE_ID); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.owner); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(rt, addrs.provider, addrs.owner, addrs.worker); @@ -188,8 +188,7 @@ pub fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenA rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addr); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(addr).unwrap()) .is_ok()); @@ -440,8 +439,7 @@ pub fn publish_deals( publish_deals: &[DealProposal], next_allocation_id: AllocationID, ) -> Vec { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); let return_value = GetControlAddressesReturnParams { owner: addrs.owner, worker: addrs.worker, @@ -564,7 +562,7 @@ pub fn publish_deals_expect_abort( proposal: DealProposal, expected_exit_code: ExitCode, ) { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address( rt, miner_addresses.provider, @@ -747,7 +745,7 @@ where rt.set_epoch(current_epoch); post_setup(&mut rt, &mut deal_proposal); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index b4e0c63e0..ce6212a49 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -14,7 +14,7 @@ use fil_actors_runtime::runtime::{builtins::Type, Policy, Runtime}; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ make_empty_map, make_map_with_root_and_bitwidth, ActorError, BatchReturn, Map, SetMultimap, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, DATACAP_TOKEN_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use frc46_token::token::types::{TransferFromParams, TransferFromReturn}; @@ -195,7 +195,7 @@ fn adds_to_provider_escrow_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -378,28 +378,6 @@ fn worker_balance_after_withdrawal_must_account_for_slashed_funds() { check_state(&rt); } -#[test] -fn fails_unless_called_by_an_account_actor() { - let mut rt = setup(); - - rt.set_value(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - - rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR); - assert_eq!( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::AddBalance as u64, - &RawBytes::serialize(PROVIDER_ADDR).unwrap(), - ) - .unwrap_err() - .exit_code() - ); - - rt.verify(); - check_state(&rt); -} - #[test] fn adds_to_non_provider_funds() { struct TestCase { @@ -418,8 +396,7 @@ fn adds_to_non_provider_funds() { for tc in &test_cases { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *caller_addr); rt.set_value(TokenAmount::from_atto(tc.delta)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); assert_eq!( RawBytes::default(), rt.call::( @@ -564,7 +541,6 @@ fn fails_if_withdraw_from_provider_funds_is_not_initiated_by_the_owner_or_worker assert_eq!(get_balance(&mut rt, &PROVIDER_ADDR).balance, amount); - // only signing parties can add balance for client AND provider. rt.expect_validate_caller_addr(vec![OWNER_ADDR, WORKER_ADDR]); let params = WithdrawBalanceParams { provider_or_client: PROVIDER_ADDR, @@ -821,7 +797,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t rt.set_value(amount.clone()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, client_resolved); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(client_bls).unwrap()) .is_ok()); @@ -833,7 +809,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // add funds for provider using it's BLS address -> will be resolved and persisted rt.value_received = deal.provider_collateral.clone(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -850,7 +826,7 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t // publish deal using the BLS addresses rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, provider_resolved, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1402,7 +1378,7 @@ fn cannot_publish_the_same_deal_twice_before_a_cron_tick() { let params = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal: d2.clone(), client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -1770,7 +1746,7 @@ fn insufficient_client_balance_in_a_batch() { deal1.provider_balance_requirement().add(deal2.provider_balance_requirement()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(provider_funds); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1802,7 +1778,7 @@ fn insufficient_client_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1883,7 +1859,7 @@ fn insufficient_provider_balance_in_a_batch() { // Provider has enough for only the second deal rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); rt.set_value(deal2.provider_balance_requirement().clone()); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); assert_eq!( @@ -1919,7 +1895,7 @@ fn insufficient_provider_balance_in_a_batch() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); @@ -1990,16 +1966,12 @@ fn add_balance_restricted_correctly() { ); // can call the exported method num - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "forbidden, allowed: [Account, Multisig]", - rt.call::( - Method::AddBalanceExported as MethodNum, - &RawBytes::serialize(CLIENT_ADDR).unwrap(), - ), - ); + rt.expect_validate_caller_any(); + rt.call::( + Method::AddBalanceExported as MethodNum, + &RawBytes::serialize(CLIENT_ADDR).unwrap(), + ) + .unwrap(); rt.verify(); } diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index 38b4315d0..7cdf2492b 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -9,7 +9,6 @@ use fil_actor_market::{ use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; @@ -255,7 +254,7 @@ fn fail_when_provider_has_some_funds_but_not_enough_for_a_deal() { deals: vec![ClientDealProposal { proposal: deal1.clone(), client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -315,7 +314,7 @@ fn fail_when_deals_have_different_providers() { ], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); @@ -363,36 +362,12 @@ fn fail_when_deals_have_different_providers() { check_state(&rt); } -#[test] -fn fail_when_caller_is_not_of_signable_type() { - let start_epoch = 10; - let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; - - let mut rt = setup(); - let deal = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); - let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); - let params = PublishStorageDealsParams { - deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], - }; - let w = Address::new_id(1000); - rt.set_caller(*MINER_ACTOR_CODE_ID, w); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - expect_abort( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::PublishStorageDeals as u64, - &RawBytes::serialize(params).unwrap(), - ), - ); - check_state(&rt); -} - #[test] fn fail_when_no_deals_in_params() { let mut rt = setup(); let params = PublishStorageDealsParams { deals: vec![] }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, rt.call::( @@ -417,7 +392,7 @@ fn fail_to_resolve_provider_address() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -440,7 +415,7 @@ fn caller_is_not_the_same_as_the_worker_address_for_miner() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(999)); expect_abort( @@ -469,7 +444,7 @@ fn fails_if_provider_is_not_a_storage_miner_actor() { deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], }; - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 719554223..38a77f826 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -22,7 +22,7 @@ use fvm_shared::randomness::*; use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::*; use fvm_shared::smooth::FilterEstimate; -use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use itertools::Itertools; use log::{error, info, warn}; use multihash::Code::Blake2b256; @@ -42,8 +42,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ actor_error, cbor, restrict_internal_api, ActorContext, ActorDowncast, ActorError, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, - STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, + STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; pub use monies::*; pub use partition_state::*; @@ -151,12 +151,19 @@ impl Actor { check_peer_info(rt.policy(), ¶ms.peer_id, ¶ms.multi_addresses)?; check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?; - let owner = resolve_control_address(rt, params.owner)?; + let owner = rt.resolve_address(¶ms.owner).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner) + })?; + let worker = resolve_worker_address(rt, params.worker)?; let control_addresses: Vec<_> = params .control_addresses .into_iter() - .map(|address| resolve_control_address(rt, address)) + .map(|address| { + rt.resolve_address(&address).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve control address: {}", address) + }) + }) .collect::>()?; let policy = rt.policy(); @@ -297,11 +304,16 @@ impl Actor { ) -> Result<(), ActorError> { check_control_addresses(rt.policy(), ¶ms.new_control_addresses)?; - let new_worker = resolve_worker_address(rt, params.new_worker)?; + let new_worker = Address::new_id(resolve_worker_address(rt, params.new_worker)?); let control_addresses: Vec
= params .new_control_addresses .into_iter() - .map(|address| resolve_control_address(rt, address)) + .map(|address| { + rt.resolve_address(&address).ok_or_else(|| { + actor_error!(illegal_argument, "unable to resolve control address: {}", address) + }) + }) + .map(|id_result| id_result.map(Address::new_id)) .collect::>()?; rt.transaction(|state: &mut State, rt| { @@ -1419,7 +1431,7 @@ impl Actor { rt: &mut impl Runtime, params: DisputeWindowedPoStParams, ) -> Result<(), ActorError> { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let reporter = rt.message().caller(); { @@ -3057,7 +3069,7 @@ impl Actor { // Note: only the first report of any fault is processed because it sets the // ConsensusFaultElapsed state variable to an epoch after the fault, and reports prior to // that epoch are no longer valid - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; + rt.validate_immediate_caller_accept_any()?; let reporter = rt.message().caller(); let fault = rt @@ -4318,36 +4330,9 @@ fn request_current_total_power( Ok(power) } -/// Resolves an address to an ID address and verifies that it is address of an account or multisig actor. -fn resolve_control_address(rt: &impl Runtime, raw: Address) -> Result { - let resolved = rt - .resolve_address(&raw) - .ok_or_else(|| actor_error!(illegal_argument, "unable to resolve address: {}", raw))?; - - let owner_code = rt - .get_actor_code_cid(&resolved) - .ok_or_else(|| actor_error!(illegal_argument, "no code for address: {}", resolved))?; - - let is_principal = rt - .resolve_builtin_actor_type(&owner_code) - .as_ref() - .map(|t| CALLER_TYPES_SIGNABLE.contains(t)) - .unwrap_or(false); - - if !is_principal { - return Err(actor_error!( - illegal_argument, - "owner actor type must be a principal, was {}", - owner_code - )); - } - - Ok(Address::new_id(resolved)) -} - /// Resolves an address to an ID address and verifies that it is address of an account actor with an associated BLS key. /// The worker must be BLS since the worker key will be used alongside a BLS-VRF. -fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result { +fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result { let resolved = rt .resolve_address(&raw) .ok_or_else(|| actor_error!(illegal_argument, "unable to resolve address: {}", raw))?; @@ -4380,7 +4365,7 @@ fn resolve_worker_address(rt: &mut impl Runtime, raw: Address) -> Result
Result<(), ActorError> { diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 270cd3e4e..075ff7e47 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -24,7 +24,8 @@ use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, SectorSize, MAX_SECTOR_NUMBER}; -use fvm_shared::HAMT_BIT_WIDTH; +use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; +use itertools::Itertools; use num_traits::Zero; use super::beneficiary::*; @@ -1266,9 +1267,9 @@ pub struct MinerInfo { impl MinerInfo { pub fn new( - owner: Address, - worker: Address, - control_addresses: Vec
, + owner: ActorID, + worker: ActorID, + control_addresses: Vec, peer_id: Vec, multi_address: Vec, window_post_proof_type: RegisteredPoStProof, @@ -1282,11 +1283,12 @@ impl MinerInfo { .map_err(|e| actor_error!(illegal_argument, "invalid partition sectors: {}", e))?; Ok(Self { - owner, - worker, - control_addresses, + owner: Address::new_id(owner), + worker: Address::new_id(worker), + control_addresses: control_addresses.into_iter().map(Address::new_id).collect_vec(), + pending_worker_key: None, - beneficiary: owner, + beneficiary: Address::new_id(owner), beneficiary_term: BeneficiaryTerm::default(), pending_beneficiary_term: None, peer_id, diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index e0b09a191..af5fbde23 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -168,34 +168,6 @@ fn control_addresses_are_resolved_during_construction() { assert_eq!(control2id, info.control_addresses[1]); } -#[test] -fn fails_if_control_address_is_not_an_account_actor() { - let mut env = prepare_env(); - - let control1 = Address::new_id(501); - env.control_addrs = vec![control1]; - env.rt.actor_code_cids.insert(control1, *PAYCH_ACTOR_CODE_ID); - - let params = constructor_params(&env); - env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); - env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); - env.rt.expect_send( - env.worker, - AccountMethod::PubkeyAddress as u64, - RawBytes::default(), - TokenAmount::zero(), - RawBytes::serialize(env.worker_key).unwrap(), - ExitCode::OK, - ); - - let result = env - .rt - .call::(Method::Constructor as u64, &RawBytes::serialize(params).unwrap()) - .unwrap_err(); - assert_eq!(result.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); - env.rt.verify(); -} - #[test] fn test_construct_with_invalid_peer_id() { let mut env = prepare_env(); diff --git a/actors/miner/tests/miner_actor_test_wpost.rs b/actors/miner/tests/miner_actor_test_wpost.rs index bdbeb0620..b02d88043 100644 --- a/actors/miner/tests/miner_actor_test_wpost.rs +++ b/actors/miner/tests/miner_actor_test_wpost.rs @@ -4,7 +4,6 @@ use fil_actor_miner as miner; use fil_actor_miner::PowerPair; use fil_actors_runtime::runtime::DomainSeparationTag; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fvm_ipld_bitfield::BitField; use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; @@ -1035,7 +1034,7 @@ fn cannot_dispute_posts_when_the_challenge_window_is_open() { let params = miner::DisputeWindowedPoStParams { deadline: dlinfo.index, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); h.expect_query_network_info(&mut rt); let result = rt.call::( @@ -1096,8 +1095,7 @@ fn can_dispute_up_till_window_end_but_not_after() { // Now try to dispute. let params = miner::DisputeWindowedPoStParams { deadline: dlidx, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - + rt.expect_validate_caller_any(); h.expect_query_network_info(&mut rt); let result = rt.call::( @@ -1129,7 +1127,7 @@ fn cant_dispute_up_with_an_invalid_deadline() { let params = miner::DisputeWindowedPoStParams { deadline: 50, post_index: 0 }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, h.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let result = rt.call::( miner::Method::DisputeWindowedPoSt as u64, diff --git a/actors/miner/tests/state_harness.rs b/actors/miner/tests/state_harness.rs index 08a653086..7c982ec91 100644 --- a/actors/miner/tests/state_harness.rs +++ b/actors/miner/tests/state_harness.rs @@ -10,7 +10,6 @@ use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::BytesDe; use fvm_ipld_encoding::CborStore; use fvm_ipld_hamt::Error as HamtError; -use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{SectorNumber, SectorSize}; use fvm_shared::{clock::ChainEpoch, clock::QuantSpec, sector::RegisteredPoStProof}; @@ -34,8 +33,8 @@ impl StateHarness { // store init let store = MemoryBlockstore::default(); // state field init - let owner = Address::new_id(1); - let worker = Address::new_id(2); + let owner = 1; + let worker = 2; let test_window_post_proof_type = RegisteredPoStProof::StackedDRGWindow2KiBV1; diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 5064f596b..a13d9545a 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -45,8 +45,8 @@ use fil_actors_runtime::runtime::{DomainSeparationTag, Policy, Runtime, RuntimeP use fil_actors_runtime::{test_utils::*, BatchReturn, BatchReturnGen}; use fil_actors_runtime::{ ActorDowncast, ActorError, Array, DealWeight, MessageAccumulator, BURNT_FUNDS_ACTOR_ADDR, - CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, - STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, + INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_amt::Amt; use fvm_shared::bigint::Zero; @@ -1435,7 +1435,7 @@ impl ActorHarness { expect_success: Option, ) { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); self.expect_query_network_info(rt); @@ -1875,7 +1875,7 @@ impl ActorHarness { from: Address, fault: Option, ) -> Result<(), ActorError> { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, from); let params = ReportConsensusFaultParams { header1: vec![], header2: vec![], header_extra: vec![] }; diff --git a/actors/multisig/src/lib.rs b/actors/multisig/src/lib.rs index 8bd64b74a..c31d051b4 100644 --- a/actors/multisig/src/lib.rs +++ b/actors/multisig/src/lib.rs @@ -13,7 +13,7 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; use fil_actors_runtime::cbor::serialize_vec; -use fil_actors_runtime::runtime::{builtins::Type, ActorCode, Primitives, Runtime}; +use fil_actors_runtime::runtime::{ActorCode, Primitives, Runtime}; use fil_actors_runtime::{ actor_error, cbor, make_empty_map, make_map_with_root, resolve_to_actor_id, restrict_internal_api, ActorContext, ActorError, AsActorError, Map, INIT_ACTOR_ADDR, @@ -132,7 +132,7 @@ impl Actor { rt: &mut impl Runtime, params: ProposeParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let proposer: Address = rt.message().caller(); if params.value.is_negative() { @@ -185,7 +185,7 @@ impl Actor { rt: &mut impl Runtime, params: TxnIDParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let approver: Address = rt.message().caller(); let id = params.id; @@ -217,7 +217,7 @@ impl Actor { /// Multisig actor cancel function pub fn cancel(rt: &mut impl Runtime, params: TxnIDParams) -> Result<(), ActorError> { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let caller_addr: Address = rt.message().caller(); rt.transaction(|st: &mut State, rt| { diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index b7a768761..b340b5c04 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -6,12 +6,11 @@ use fil_actor_multisig::{ use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::{Address, BLS_PUB_LEN}; -use fil_actors_runtime::runtime::builtins::Type; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; @@ -760,8 +759,8 @@ fn test_propose_restricted_correctly() { let h = util::ActorHarness::new(); let anne = Address::new_id(101); + // We will treat Bob as having code CID b"102" let bob = Address::new_id(102); - // We will treat Chuck as having code CID b"103" let chuck = Address::new_id(103); let no_unlock_duration = 0; let start_epoch = 0; @@ -771,7 +770,7 @@ fn test_propose_restricted_correctly() { h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"103"), Address::new_id(103)); + rt.set_caller(make_identity_cid(b"102"), Address::new_id(102)); let propose_params = serialize( &ProposeParams { to: chuck, @@ -794,13 +793,8 @@ fn test_propose_restricted_correctly() { rt.verify(); // can call the exported method num - rt.expect_validate_caller_type([Type::Account, Type::Multisig].to_vec()); - // TODO: This call should succeed: See https://github.com/filecoin-project/builtin-actors/issues/806. - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "forbidden, allowed: [Account, Multisig]", - rt.call::(Method::ProposeExported as u64, &propose_params), - ); + rt.expect_validate_caller_any(); + rt.call::(Method::ProposeExported as u64, &propose_params).unwrap(); rt.verify(); } @@ -1606,7 +1600,7 @@ mod approval_tests { RawBytes::default(), ExitCode::OK, ); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let params = TxnIDParams { id: TxnID(0), proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .unwrap(); @@ -1668,7 +1662,7 @@ mod approval_tests { h.construct_and_verify(&mut rt, 1, 0, 0, signers); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, bob); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let params = TxnIDParams { id: dne_tx_id, proposal_hash: Vec::::new() }; rt.call::(Method::Approve as u64, &RawBytes::serialize(params).unwrap()) .expect_err("should fail on approve of non existent tx id"); diff --git a/actors/multisig/tests/util.rs b/actors/multisig/tests/util.rs index fdb96a94f..0dd0f8c4e 100644 --- a/actors/multisig/tests/util.rs +++ b/actors/multisig/tests/util.rs @@ -5,8 +5,8 @@ use fil_actor_multisig::{ }; use fil_actor_multisig::{ChangeNumApprovalsThresholdParams, LockBalanceParams}; use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::INIT_ACTOR_ADDR; use fil_actors_runtime::{make_map_with_root, ActorError}; -use fil_actors_runtime::{CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; @@ -125,7 +125,7 @@ impl ActorHarness { method: MethodNum, params: RawBytes, ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let propose_params = ProposeParams { to, value, method, params }; let ret = rt.call::(Method::Propose as u64, &RawBytes::serialize(propose_params).unwrap()); @@ -139,7 +139,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let approve_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = @@ -154,7 +154,7 @@ impl ActorHarness { txn_id: TxnID, proposal_hash: [u8; 32], ) -> Result { - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let cancel_params = TxnIDParams { id: txn_id, proposal_hash: Vec::::from(proposal_hash) }; let ret = diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 43437812c..9d5552094 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -87,7 +87,7 @@ impl Actor { rt: &mut impl Runtime, params: CreateMinerParams, ) -> Result { - rt.validate_immediate_caller_type(&[Type::Account, Type::Multisig])?; + rt.validate_immediate_caller_accept_any()?; let value = rt.message().value_received(); let constructor_params = RawBytes::serialize(ext::miner::MinerConstructorParams { diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index e82fa8b5d..12cccbd5f 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -13,7 +13,6 @@ use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; -use fil_actors_runtime::CALLER_TYPES_SIGNABLE; use fil_actors_runtime::CRON_ACTOR_ADDR; use fil_actors_runtime::REWARD_ACTOR_ADDR; use fvm_ipld_blockstore::Blockstore; @@ -142,7 +141,7 @@ impl Harness { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *owner); rt.set_value(value.clone()); rt.set_balance(value.clone()); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let miner_ctor_params = MinerConstructorParams { owner: *owner, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 184889025..650aefb95 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -5,7 +5,7 @@ use fil_actors_runtime::test_utils::{ expect_abort, expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; -use fil_actors_runtime::{runtime::Policy, CALLER_TYPES_SIGNABLE, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{runtime::Policy, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, RawBytes}; use fvm_shared::address::Address; use fvm_shared::bigint::bigint_ser::BigIntSer; @@ -77,34 +77,6 @@ fn create_miner() { h.check_state(&rt); } -#[test] -fn create_miner_given_caller_is_not_of_signable_type_should_fail() { - let (h, mut rt) = setup(); - - let peer = "miner".as_bytes().to_vec(); - let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; - - let create_miner_params = CreateMinerParams { - owner: *OWNER, - worker: *OWNER, - window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1, - peer, - multiaddrs, - }; - - rt.set_caller(*MINER_ACTOR_CODE_ID, *OWNER); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); - expect_abort( - ExitCode::USR_FORBIDDEN, - rt.call::( - Method::CreateMiner as u64, - &RawBytes::serialize(&create_miner_params).unwrap(), - ), - ); - rt.verify(); - h.check_state(&rt); -} - #[test] fn create_miner_given_send_to_init_actor_fails_should_fail() { let (h, mut rt) = setup(); @@ -124,7 +96,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *OWNER); rt.value_received = TokenAmount::from_atto(10); rt.set_balance(TokenAmount::from_atto(10)); - rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); + rt.expect_validate_caller_any(); let message_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 2208a45bb..c4a338d59 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -6,16 +6,10 @@ use fvm_shared::address::Address; use fvm_shared::METHOD_SEND; use fvm_shared::{ActorID, MethodNum}; -use crate::runtime::builtins::Type; use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// Types of built-in actors that can be treated as principals. -/// This distinction is legacy and should be removed prior to FVM support for -/// user-programmable actors. -pub const CALLER_TYPES_SIGNABLE: &[Type] = &[Type::Account, Type::Multisig]; - /// ResolveToActorID resolves the given address to it's actor ID. /// If an actor ID for the given address dosen't exist yet, it tries to create one by sending /// a zero balance to the given address. From fdd63039b810a8d2bad6c7615d9ca3e66c069438 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Nov 2022 19:07:10 -0500 Subject: [PATCH 186/339] Market actor: Minor improvements to two exported getters (#826) * Market actor: GetDealTermExported: Return (start_epoch, duration) * Market actor: Export getter for deal total price --- actors/market/src/lib.rs | 19 +++++++++---------- actors/market/src/types.rs | 11 ++++++----- actors/market/tests/deal_api_test.rs | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 4e44c126b..3b429d059 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -81,7 +81,7 @@ pub enum Method { GetDealProviderExported = frc42_dispatch::method_hash!("GetDealProvider"), GetDealLabelExported = frc42_dispatch::method_hash!("GetDealLabel"), GetDealTermExported = frc42_dispatch::method_hash!("GetDealTerm"), - GetDealEpochPriceExported = frc42_dispatch::method_hash!("GetDealEpochPrice"), + GetDealTotalPriceExported = frc42_dispatch::method_hash!("GetDealTotalPrice"), GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), @@ -869,25 +869,24 @@ impl Actor { Ok(GetDealLabelReturn { label: found.label }) } - /// Returns the start and end epochs of a deal proposal. - /// The deal term is a half-open range, exclusive of the end epoch. + /// Returns the start epoch and duration (in epochs) of a deal proposal. fn get_deal_term( rt: &mut impl Runtime, params: GetDealTermParams, ) -> Result { rt.validate_immediate_caller_accept_any()?; let found = rt.state::()?.get_proposal(rt.store(), params.id)?; - Ok(GetDealTermReturn { start: found.start_epoch, end: found.end_epoch }) + Ok(GetDealTermReturn { start: found.start_epoch, duration: found.duration() }) } /// Returns the per-epoch price of a deal proposal. - fn get_deal_epoch_price( + fn get_deal_total_price( rt: &mut impl Runtime, - params: GetDealEpochPriceParams, - ) -> Result { + params: GetDealTotalPriceParams, + ) -> Result { rt.validate_immediate_caller_accept_any()?; let found = rt.state::()?.get_proposal(rt.store(), params.id)?; - Ok(GetDealEpochPriceReturn { price_per_epoch: found.storage_price_per_epoch }) + Ok(GetDealTotalPriceReturn { total_price: found.total_storage_fee() }) } /// Returns the client collateral requirement for a deal proposal. @@ -1360,8 +1359,8 @@ impl ActorCode for Actor { let res = Self::get_deal_term(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::GetDealEpochPriceExported) => { - let res = Self::get_deal_epoch_price(rt, cbor::deserialize_params(params)?)?; + Some(Method::GetDealTotalPriceExported) => { + let res = Self::get_deal_total_price(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } Some(Method::GetDealClientCollateralExported) => { diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index afdfa2da4..7c28f164b 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -175,14 +175,15 @@ pub struct GetDealLabelReturn { pub type GetDealTermParams = DealQueryParams; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] pub struct GetDealTermReturn { - pub start: ChainEpoch, // First epoch for the deal (inclusive) - pub end: ChainEpoch, // Epoch at which the deal expires (i.e. exclusive). + pub start: ChainEpoch, // First epoch for the deal (inclusive) + pub duration: ChainEpoch, // Duration of the deal. } -pub type GetDealEpochPriceParams = DealQueryParams; +pub type GetDealTotalPriceParams = DealQueryParams; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] -pub struct GetDealEpochPriceReturn { - pub price_per_epoch: TokenAmount, +#[serde(transparent)] +pub struct GetDealTotalPriceReturn { + pub total_price: TokenAmount, } pub type GetDealClientCollateralParams = DealQueryParams; diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs index 4471f0f43..ca18ed999 100644 --- a/actors/market/tests/deal_api_test.rs +++ b/actors/market/tests/deal_api_test.rs @@ -4,9 +4,9 @@ use serde::de::DeserializeOwned; use fil_actor_market::{ Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, - GetDealDataCommitmentReturn, GetDealEpochPriceReturn, GetDealLabelReturn, - GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, - GetDealVerifiedReturn, Method, + GetDealDataCommitmentReturn, GetDealLabelReturn, GetDealProviderCollateralReturn, + GetDealProviderReturn, GetDealTermReturn, GetDealTotalPriceReturn, GetDealVerifiedReturn, + Method, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; @@ -52,10 +52,10 @@ fn proposal_data() { let term: GetDealTermReturn = query_deal(&mut rt, Method::GetDealTermExported, id); assert_eq!(proposal.start_epoch, term.start); - assert_eq!(proposal.end_epoch, term.end); + assert_eq!(proposal.duration(), term.duration); - let price: GetDealEpochPriceReturn = query_deal(&mut rt, Method::GetDealEpochPriceExported, id); - assert_eq!(proposal.storage_price_per_epoch, price.price_per_epoch); + let price: GetDealTotalPriceReturn = query_deal(&mut rt, Method::GetDealTotalPriceExported, id); + assert_eq!(proposal.total_storage_fee(), price.total_price); let client_collateral: GetDealClientCollateralReturn = query_deal(&mut rt, Method::GetDealClientCollateralExported, id); From b26fc74769620935817cd45e6cc2c97023a73b2c Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:22:39 +1100 Subject: [PATCH 187/339] Exported API for market deal activation state (#819) --- actors/market/src/lib.rs | 55 ++++++++++++++++ actors/market/src/types.rs | 10 +++ actors/market/tests/deal_api_test.rs | 95 +++++++++++++++++++++++++--- runtime/src/actor_error.rs | 2 +- 4 files changed, 153 insertions(+), 9 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 3b429d059..e67806100 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -59,6 +59,9 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub const NO_ALLOCATION_ID: u64 = 0; +// An exit code indicating that information about a past deal is no longer available. +pub const EX_DEAL_EXPIRED: ExitCode = ExitCode::new(32); + /// Market actor methods available #[derive(FromPrimitive)] #[repr(u64)] @@ -85,6 +88,7 @@ pub enum Method { GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"), GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"), GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"), + GetDealActivationExported = frc42_dispatch::method_hash!("GetDealActivation"), } /// Market Actor @@ -920,6 +924,53 @@ impl Actor { let found = rt.state::()?.get_proposal(rt.store(), params.id)?; Ok(GetDealVerifiedReturn { verified: found.verified_deal }) } + + /// Fetches activation state for a deal. + /// This will be available from when the proposal is published until an undefined period after + /// the deal finishes (either normally or by termination). + /// Returns USR_NOT_FOUND if the deal doesn't exist (yet), or EX_DEAL_EXPIRED if the deal + /// has been removed from state. + fn get_deal_activation( + rt: &mut impl Runtime, + params: GetDealActivationParams, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st = rt.state::()?; + let found = st.find_deal_state(rt.store(), params.id)?; + match found { + Some(state) => Ok(GetDealActivationReturn { + // If we have state, the deal has been activated. + // It may also have completed normally, or been terminated, + // but not yet been cleaned up. + activated: state.sector_start_epoch, + terminated: state.slash_epoch, + }), + None => { + // State::get_proposal will fail with USR_NOT_FOUND in either case. + let maybe_proposal = st.find_proposal(rt.store(), params.id)?; + match maybe_proposal { + Some(_) => Ok(GetDealActivationReturn { + // The proposal has been published, but not activated. + activated: EPOCH_UNDEFINED, + terminated: EPOCH_UNDEFINED, + }), + None => { + if params.id < st.next_id { + // If the deal ID has been used, it must have been cleaned up. + Err(ActorError::unchecked( + EX_DEAL_EXPIRED, + format!("deal {} expired", params.id), + )) + } else { + // We can't distinguish between failing to activate, or having been + // cleaned up after completion/termination. + Err(ActorError::not_found(format!("no such deal {}", params.id))) + } + } + } + } + } + } } fn compute_data_commitment( @@ -1376,6 +1427,10 @@ impl ActorCode for Actor { let res = Self::get_deal_verified(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetDealActivationExported) => { + let res = Self::get_deal_activation(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message, "Invalid method")), } } diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index 7c28f164b..eaa98b970 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -206,3 +206,13 @@ pub type GetDealVerifiedParams = DealQueryParams; pub struct GetDealVerifiedReturn { pub verified: bool, } + +pub type GetDealActivationParams = DealQueryParams; +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +pub struct GetDealActivationReturn { + /// Epoch at which the deal was activated, or -1. + /// This may be before the proposed start epoch. + pub activated: ChainEpoch, + /// Epoch at which the deal was terminated abnormally, or -1. + pub terminated: ChainEpoch, +} diff --git a/actors/market/tests/deal_api_test.rs b/actors/market/tests/deal_api_test.rs index ca18ed999..91584188b 100644 --- a/actors/market/tests/deal_api_test.rs +++ b/actors/market/tests/deal_api_test.rs @@ -1,15 +1,22 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; +use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; use serde::de::DeserializeOwned; use fil_actor_market::{ - Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn, - GetDealDataCommitmentReturn, GetDealLabelReturn, GetDealProviderCollateralReturn, - GetDealProviderReturn, GetDealTermReturn, GetDealTotalPriceReturn, GetDealVerifiedReturn, - Method, + Actor as MarketActor, DealQueryParams, GetDealActivationReturn, GetDealClientCollateralReturn, + GetDealClientReturn, GetDealDataCommitmentReturn, GetDealLabelReturn, + GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn, + GetDealTotalPriceReturn, GetDealVerifiedReturn, Method, EX_DEAL_EXPIRED, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; -use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID}; +use fil_actors_runtime::runtime::policy_constants::DEAL_UPDATES_INTERVAL; +use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, MockRuntime, ACCOUNT_ACTOR_CODE_ID, +}; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use harness::*; mod harness; @@ -71,11 +78,83 @@ fn proposal_data() { check_state(&rt); } +#[test] +fn activation() { + let start_epoch = 10; + let end_epoch = start_epoch + 180 * EPOCHS_IN_DAY; + let publish_epoch = ChainEpoch::from(1); + + let mut rt = setup(); + rt.set_epoch(publish_epoch); + let next_allocation_id = 1; + + let proposal = generate_deal_and_add_funds( + &mut rt, + CLIENT_ADDR, + &MinerAddresses::default(), + start_epoch, + end_epoch, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + let id = + publish_deals(&mut rt, &MinerAddresses::default(), &[proposal.clone()], next_allocation_id) + [0]; + + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(-1, activation.activated); + assert_eq!(-1, activation.terminated); + + // activate the deal + let activate_epoch = start_epoch - 2; + rt.set_epoch(activate_epoch); + activate_deals(&mut rt, end_epoch + 1, PROVIDER_ADDR, activate_epoch, &[id]); + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(activate_epoch, activation.activated); + assert_eq!(-1, activation.terminated); + + // terminate early + let terminate_epoch = activate_epoch + 100; + rt.set_epoch(terminate_epoch); + terminate_deals(&mut rt, PROVIDER_ADDR, &[id]); + let activation: GetDealActivationReturn = + query_deal(&mut rt, Method::GetDealActivationExported, id); + assert_eq!(activate_epoch, activation.activated); + assert_eq!(terminate_epoch, activation.terminated); + + // Clean up state + let clean_epoch = terminate_epoch + DEAL_UPDATES_INTERVAL; + rt.set_epoch(clean_epoch); + rt.expect_send( + BURNT_FUNDS_ACTOR_ADDR, + METHOD_SEND, + RawBytes::default(), + proposal.provider_collateral, + RawBytes::default(), + ExitCode::OK, + ); + cron_tick(&mut rt); + expect_abort_contains_message( + EX_DEAL_EXPIRED, + "expired", + query_deal_raw(&mut rt, Method::GetDealActivationExported, id), + ); + + // Non-existent deal is NOT FOUND + expect_abort_contains_message( + ExitCode::USR_NOT_FOUND, + "no such deal", + query_deal_raw(&mut rt, Method::GetDealActivationExported, id + 1), + ); +} + fn query_deal(rt: &mut MockRuntime, method: Method, id: u64) -> T { + query_deal_raw(rt, method, id).unwrap().deserialize().unwrap() +} + +fn query_deal_raw(rt: &mut MockRuntime, method: Method, id: u64) -> Result { let params = DealQueryParams { id }; rt.expect_validate_caller_any(); rt.call::(method as u64, &RawBytes::serialize(params).unwrap()) - .unwrap() - .deserialize() - .unwrap() } diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 8ef17f147..b7c408987 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -8,7 +8,7 @@ use thiserror::Error; #[error("ActorError(exit_code: {exit_code:?}, msg: {msg})")] pub struct ActorError { /// The exit code for this invocation. - /// Codes less than `FIRST_ACTOR_EXIT_CODE` are prohibited and will be overwritten by the VM. + /// Codes less than `FIRST_USER_EXIT_CODE` are prohibited and will be overwritten by the VM. exit_code: ExitCode, /// Message for debugging purposes, msg: String, From cd331323c564a63798351690f116536a3fbc01e2 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 16 Nov 2022 17:21:11 -0500 Subject: [PATCH 188/339] Paych actor: Drop account req, use AuthenticateMessage to verify sigs (#824) * Paych actor: Drop account req, use AuthenticateMessage to verify sigs * Address review * Address review --- Cargo.lock | 1 + actors/paych/Cargo.toml | 1 + actors/paych/src/ext.rs | 17 +++ actors/paych/src/lib.rs | 60 ++++---- actors/paych/src/state.rs | 2 + actors/paych/tests/paych_actor_test.rs | 196 +++++++------------------ runtime/src/builtin/shared.rs | 4 +- 7 files changed, 109 insertions(+), 172 deletions(-) create mode 100644 actors/paych/src/ext.rs diff --git a/Cargo.lock b/Cargo.lock index 7fe3049e3..6b696588b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,6 +717,7 @@ dependencies = [ "cid", "derive_builder", "fil_actors_runtime", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_blockstore", "fvm_ipld_encoding", diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 6cd02512e..042e21ab3 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,6 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +frc42_dispatch = "1.0.0" fvm_shared = { version = "2.0.0-alpha.2", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/paych/src/ext.rs b/actors/paych/src/ext.rs new file mode 100644 index 000000000..cea045d3d --- /dev/null +++ b/actors/paych/src/ext.rs @@ -0,0 +1,17 @@ +use fvm_ipld_encoding::serde_bytes; +use fvm_ipld_encoding::tuple::*; + +pub mod account { + use super::*; + + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); + + #[derive(Serialize_tuple, Deserialize_tuple)] + pub struct AuthenticateMessageParams { + #[serde(with = "serde_bytes")] + pub signature: Vec, + #[serde(with = "serde_bytes")] + pub message: Vec, + } +} diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index 75d9a5091..2ee125557 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -4,7 +4,8 @@ use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, Array, + actor_error, cbor, resolve_to_actor_id, restrict_internal_api, ActorDowncast, ActorError, + Array, AsActorError, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -22,6 +23,7 @@ pub use self::types::*; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); +pub mod ext; mod state; pub mod testing; mod types; @@ -42,6 +44,7 @@ pub const ERR_CHANNEL_STATE_UPDATE_AFTER_SETTLED: ExitCode = ExitCode::new(32); /// Payment Channel actor pub struct Actor; + impl Actor { /// Constructor for Payment channel actor pub fn constructor(rt: &mut impl Runtime, params: ConstructorParams) -> Result<(), ActorError> { @@ -49,10 +52,16 @@ impl Actor { // behalf of the payer/payee. rt.validate_immediate_caller_type(std::iter::once(&Type::Init))?; - // Check both parties are capable of signing vouchers - let to = Self::resolve_account(rt, ¶ms.to)?; + // Resolve both parties, confirming they exist in the state tree. + let to = Self::resolve_address(rt, ¶ms.to) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("to address not found {}", params.to) + })?; - let from = Self::resolve_account(rt, ¶ms.from)?; + let from = Self::resolve_address(rt, ¶ms.from) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("to address not found {}", params.to) + })?; let empty_arr_cid = Array::<(), _>::new_with_bit_width(rt.store(), LANE_STATES_AMT_BITWIDTH) @@ -65,26 +74,14 @@ impl Actor { Ok(()) } - /// Resolves an address to a canonical ID address and requires it to address an account actor. - fn resolve_account(rt: &mut impl Runtime, raw: &Address) -> Result { + /// Resolves an address to a canonical ID address and confirms it exists in the state tree. + fn resolve_address(rt: &mut impl Runtime, raw: &Address) -> Result { let resolved = resolve_to_actor_id(rt, raw)?; - let code_cid = rt - .get_actor_code_cid(&resolved) - .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", resolved))?; - - let typ = rt.resolve_builtin_actor_type(&code_cid); - if typ != Some(Type::Account) { - Err(actor_error!( - forbidden, - "actor {} must be an account, was {} ({:?})", - raw, - code_cid, - typ - )) - } else { - Ok(Address::new_id(resolved)) - } + // so long as we can find code for this, return `resolved` + rt.get_actor_code_cid(&resolved) + .map(|_| Address::new_id(resolved)) + .ok_or_else(|| actor_error!(illegal_argument, "no code for address {}", resolved)) } pub fn update_channel_state( @@ -98,10 +95,11 @@ impl Actor { let sv = params.sv; // Pull signature from signed voucher - let sig = sv + let sig = &sv .signature .as_ref() - .ok_or_else(|| actor_error!(illegal_argument, "voucher has no signature"))?; + .ok_or_else(|| actor_error!(illegal_argument, "voucher has no signature"))? + .bytes; if st.settling_at != 0 && rt.curr_epoch() >= st.settling_at { return Err(ActorError::unchecked( @@ -120,9 +118,17 @@ impl Actor { })?; // Validate signature - rt.verify_signature(sig, &signer, &sv_bz).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "voucher signature invalid") - })?; + + rt.send( + &signer, + ext::account::AUTHENTICATE_MESSAGE_METHOD, + RawBytes::serialize(ext::account::AuthenticateMessageParams { + signature: sig.to_vec(), + message: sv_bz, + })?, + TokenAmount::zero(), + ) + .map_err(|e| e.wrap("voucher sig authentication failed"))?; let pch_addr = rt.message().receiver(); let svpch_id = rt.resolve_address(&sv.channel_addr).ok_or_else(|| { diff --git a/actors/paych/src/state.rs b/actors/paych/src/state.rs index 8692940cd..5c892c977 100644 --- a/actors/paych/src/state.rs +++ b/actors/paych/src/state.rs @@ -57,5 +57,7 @@ pub struct Merge { } impl Cbor for State {} + impl Cbor for LaneState {} + impl Cbor for Merge {} diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index 969033e02..f0fb56342 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -4,14 +4,15 @@ use std::cell::RefCell; use std::collections::HashMap; -use anyhow::anyhow; use cid::Cid; use derive_builder::Builder; +use fil_actor_paych::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD}; use fil_actor_paych::testing::check_state_invariants; use fil_actor_paych::{ Actor as PaychActor, ConstructorParams, LaneState, Merge, Method, ModVerifyParams, SignedVoucher, State as PState, UpdateChannelStateParams, MAX_LANE, SETTLE_DELAY, }; + use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; @@ -69,15 +70,12 @@ fn check_state(rt: &MockRuntime) { mod paych_constructor { use fil_actors_runtime::runtime::builtins::Type; - use fvm_shared::METHOD_CONSTRUCTOR; - use fvm_shared::METHOD_SEND; - use num_traits::Zero; + use fvm_shared::{METHOD_CONSTRUCTOR, METHOD_SEND}; use super::*; const TEST_PAYCH_ADDR: u64 = 100; const TEST_PAYER_ADDR: u64 = 101; - const TEST_PAYEE_ADDR: u64 = 102; const TEST_CALLER_ADDR: u64 = 102; fn construct_runtime() -> MockRuntime { @@ -112,7 +110,7 @@ mod paych_constructor { rt.expect_validate_caller_type(vec![Type::Init]); let params = ConstructorParams { to: Address::new_id(TEST_PAYCH_ADDR), - from: Address::new_id(TEST_PAYER_ADDR), + from: Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(), }; expect_abort( &mut rt, @@ -140,65 +138,6 @@ mod paych_constructor { check_state(&rt); } - #[test] - fn actor_constructor_fails() { - let paych_addr = Address::new_id(TEST_PAYCH_ADDR); - let payer_addr = Address::new_id(TEST_PAYER_ADDR); - let payee_addr = Address::new_id(TEST_PAYEE_ADDR); - let caller_addr = Address::new_id(TEST_CALLER_ADDR); - - struct TestCase { - from_code: Cid, - from_addr: Address, - to_code: Cid, - to_addr: Address, - expected_exit_code: ExitCode, - } - - let test_cases: Vec = vec![ - // fails if target (to) is not account actor - TestCase { - from_code: *ACCOUNT_ACTOR_CODE_ID, - from_addr: payer_addr, - to_code: *MULTISIG_ACTOR_CODE_ID, - to_addr: payee_addr, - expected_exit_code: ExitCode::USR_FORBIDDEN, - }, - // fails if sender (from) is not account actor - TestCase { - from_code: *MULTISIG_ACTOR_CODE_ID, - from_addr: payer_addr, - to_code: *ACCOUNT_ACTOR_CODE_ID, - to_addr: payee_addr, - expected_exit_code: ExitCode::USR_FORBIDDEN, - }, - ]; - - for test_case in test_cases { - let mut actor_code_cids = HashMap::default(); - actor_code_cids.insert(paych_addr, *PAYCH_ACTOR_CODE_ID); - actor_code_cids.insert(test_case.to_addr, test_case.to_code); - actor_code_cids.insert(test_case.from_addr, test_case.from_code); - - let mut rt = MockRuntime { - receiver: paych_addr, - caller: caller_addr, - caller_type: *INIT_ACTOR_CODE_ID, - actor_code_cids, - ..Default::default() - }; - - rt.expect_validate_caller_type(vec![Type::Init]); - let params = ConstructorParams { to: test_case.to_addr, from: test_case.from_addr }; - expect_abort( - &mut rt, - METHOD_CONSTRUCTOR, - &RawBytes::serialize(params).unwrap(), - test_case.expected_exit_code, - ); - } - } - #[test] fn sendr_addr_not_resolvable_to_id_addr() { const TO_ADDR: u64 = 101; @@ -447,17 +386,15 @@ mod create_lane_tests { rt.expect_validate_caller_addr(vec![payer_addr, payee_addr]); if test_case.sig.is_some() && test_case.secret_preimage.is_empty() { - let exp_exit_code = - if !test_case.verify_sig { Err(anyhow!("bad signature")) } else { Ok(()) }; - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: exp_exit_code, - }); + let exp_exit_code = if !test_case.verify_sig { + ExitCode::USR_ILLEGAL_ARGUMENT + } else { + ExitCode::OK + }; + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), exp_exit_code); } - if test_case.exp_exit_code == ExitCode::OK { + if test_case.exp_exit_code.is_success() { call( &mut rt, Method::UpdateChannelState as u64, @@ -502,12 +439,7 @@ mod update_channel_state_redeem { let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -545,12 +477,7 @@ mod update_channel_state_redeem { sv.nonce = ls_to_update.nonce + 1; let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -585,12 +512,7 @@ mod update_channel_state_redeem { let payer_addr = Address::new_id(PAYER_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payer_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payer_addr, sv.clone(), ExitCode::OK); expect_abort( &mut rt, @@ -617,12 +539,8 @@ mod merge_tests { fn failure_end(rt: &mut MockRuntime, sv: SignedVoucher, exp_exit_code: ExitCode) { let payee_addr = Address::new_id(PAYEE_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(rt, payee_addr, sv.clone(), ExitCode::OK); + expect_abort( rt, Method::UpdateChannelState as u64, @@ -645,12 +563,7 @@ mod merge_tests { sv.merges = vec![Merge { lane: 1, nonce: merge_nonce }]; let payee_addr = Address::new_id(PAYEE_ID); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, payee_addr, sv.clone(), ExitCode::OK); call( &mut rt, @@ -770,12 +683,7 @@ mod update_channel_state_extra { method: Method::UpdateChannelState as u64, data: fake_params.clone(), }); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv.clone(), ExitCode::OK); rt.expect_send( other_addr, @@ -848,16 +756,13 @@ fn update_channel_settling() { }, ]; - let mut ucp = UpdateChannelStateParams::from(sv.clone()); + let mut ucp = UpdateChannelStateParams::from(sv); for tc in test_cases { ucp.sv.min_settle_height = tc.min_settle; rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: ucp.sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(&mut rt, state.to, ucp.sv.clone(), ExitCode::OK); + call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(&ucp).unwrap()); let new_state: PState = rt.get_state(); assert_eq!(tc.exp_settling_at, new_state.settling_at); @@ -878,12 +783,7 @@ mod secret_preimage { let ucp = UpdateChannelStateParams::from(sv.clone()); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.clone().signature.unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv, ExitCode::OK); call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(ucp).unwrap()); @@ -897,18 +797,15 @@ mod secret_preimage { let state: PState = rt.get_state(); - let mut ucp = UpdateChannelStateParams { secret: b"Profesr".to_vec(), sv: sv.clone() }; + let mut ucp = UpdateChannelStateParams { secret: b"Profesr".to_vec(), sv }; let mut mag = b"Magneto".to_vec(); mag.append(&mut vec![0; 25]); ucp.sv.secret_pre_image = mag; rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: sv.signature.unwrap(), - signer: state.to, - plaintext: ucp.sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(&mut rt, state.to, ucp.sv.clone(), ExitCode::OK); + expect_abort( &mut rt, Method::UpdateChannelState as u64, @@ -971,12 +868,8 @@ mod actor_settle { let ucp = UpdateChannelStateParams::from(sv.clone()); rt.expect_validate_caller_addr(vec![state.from, state.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig: ucp.sv.signature.clone().unwrap(), - signer: state.to, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + expect_authenticate_message(&mut rt, state.to, sv, ExitCode::OK); + call(&mut rt, Method::UpdateChannelState as u64, &RawBytes::serialize(&ucp).unwrap()); state = rt.get_state(); @@ -1174,7 +1067,7 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche lane: param.lane, nonce: param.nonce, amount: param.amt.clone(), - signature: Some(sig.clone()), + signature: Some(sig), secret_pre_image: Default::default(), channel_addr: Address::new_id(PAYCH_ID), extra: Default::default(), @@ -1183,12 +1076,9 @@ fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVouche }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, param.from); rt.expect_validate_caller_addr(vec![param.from, param.to]); - rt.expect_verify_signature(ExpectedVerifySig { - sig, - signer: payee_addr, - plaintext: sv.signing_bytes().unwrap(), - result: Ok(()), - }); + + expect_authenticate_message(rt, payee_addr, sv.clone(), ExitCode::OK); + call( rt, Method::UpdateChannelState as u64, @@ -1238,3 +1128,23 @@ fn assert_lane_states_length(rt: &MockRuntime, cid: &Cid, l: u64) { let arr = Amt::::load(cid, &rt.store).unwrap(); assert_eq!(arr.count(), l); } + +fn expect_authenticate_message( + rt: &mut MockRuntime, + payer_addr: Address, + sv: SignedVoucher, + exp_exit_code: ExitCode, +) { + rt.expect_send( + payer_addr, + AUTHENTICATE_MESSAGE_METHOD, + RawBytes::serialize(AuthenticateMessageParams { + signature: sv.clone().signature.unwrap().bytes, + message: sv.signing_bytes().unwrap(), + }) + .unwrap(), + TokenAmount::zero(), + RawBytes::default(), + exp_exit_code, + ) +} diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index c4a338d59..4293c75a9 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -10,8 +10,8 @@ use crate::runtime::Runtime; pub const HAMT_BIT_WIDTH: u32 = 5; -/// ResolveToActorID resolves the given address to it's actor ID. -/// If an actor ID for the given address dosen't exist yet, it tries to create one by sending +/// ResolveToActorID resolves the given address to its actor ID. +/// If an actor ID for the given address doesn't exist yet, it tries to create one by sending /// a zero balance to the given address. pub fn resolve_to_actor_id( rt: &mut impl Runtime, From d6048c08b7cb41ce864090bac1a5df671fb7e049 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 17 Nov 2022 13:41:00 -0500 Subject: [PATCH 189/339] Account actor: Deprecate AuthenticateMessage (#856) --- actors/account/src/lib.rs | 5 +++-- actors/account/tests/account_actor_test.rs | 8 -------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index 2f27a5208..0f23d9944 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -32,7 +32,8 @@ fil_actors_runtime::wasm_trampoline!(Actor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, PubkeyAddress = 2, - AuthenticateMessage = 3, + // Deprecated in v10 + // AuthenticateMessage = 3, AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } @@ -121,7 +122,7 @@ impl ActorCode for Actor { let addr = Self::pubkey_address(rt)?; Ok(RawBytes::serialize(addr)?) } - Some(Method::AuthenticateMessage) | Some(Method::AuthenticateMessageExported) => { + Some(Method::AuthenticateMessageExported) => { Self::authenticate_message(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index de2792177..c1a631314 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -122,14 +122,6 @@ fn authenticate_message() { ); rt.verify(); - // Invalid caller of internal method number - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::AuthenticateMessage as MethodNum, ¶ms), - ); - // Ok to call exported method number rt.expect_validate_caller_any(); rt.expect_verify_signature(ExpectedVerifySig { From 904bd6ab0dcb48141ea1a1f2d68ce9b89f51d4e5 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 19 Nov 2022 15:26:00 -0500 Subject: [PATCH 190/339] Market actor: Export PublishStorageDeals (#857) --- actors/market/src/lib.rs | 3 +- actors/market/tests/market_actor_test.rs | 92 +++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index e67806100..2138aa695 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -78,6 +78,7 @@ pub enum Method { // Method numbers derived from FRC-0042 standards AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"), WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + PublishStorageDealsExported = frc42_dispatch::method_hash!("PublishStorageDeals"), GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"), GetDealDataCommitmentExported = frc42_dispatch::method_hash!("GetDealDataCommitment"), GetDealClientExported = frc42_dispatch::method_hash!("GetDealClient"), @@ -1362,7 +1363,7 @@ impl ActorCode for Actor { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::PublishStorageDeals) => { + Some(Method::PublishStorageDeals) | Some(Method::PublishStorageDealsExported) => { let res = Self::publish_storage_deals(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index ce6212a49..be3ebd637 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -1953,7 +1953,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(1000)); + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); // cannot call the unexported method num expect_abort_contains_message( @@ -1975,3 +1975,93 @@ fn add_balance_restricted_correctly() { rt.verify(); } + +#[test] +fn psd_restricted_correctly() { + let mut rt = setup(); + + let deal = generate_deal_proposal( + CLIENT_ADDR, + PROVIDER_ADDR, + ChainEpoch::from(1), + 200 * EPOCHS_IN_DAY, + ); + + // Client gets enough funds + add_participant_funds(&mut rt, CLIENT_ADDR, deal.client_balance_requirement()); + + // Provider has enough funds + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, OWNER_ADDR); + rt.set_value(deal.provider_balance_requirement().clone()); + rt.expect_validate_caller_any(); + expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); + + assert_eq!( + RawBytes::default(), + rt.call::( + Method::AddBalance as u64, + &RawBytes::serialize(PROVIDER_ADDR).unwrap(), + ) + .unwrap() + ); + + rt.verify(); + + // Prep the message + + let buf = RawBytes::serialize(&deal).expect("failed to marshal deal proposal"); + + let sig = Signature::new_bls(buf.to_vec()); + + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal.clone(), client_signature: sig }], + }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), WORKER_ADDR); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::PublishStorageDeals as MethodNum, + &RawBytes::serialize(params.clone()).unwrap(), + ), + ); + + // can call the exported method num + + let authenticate_param1 = RawBytes::serialize(AuthenticateMessageParams { + signature: buf.to_vec(), + message: buf.to_vec(), + }) + .unwrap(); + + rt.expect_validate_caller_any(); + expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); + expect_query_network_info(&mut rt); + + rt.expect_send( + deal.client, + AUTHENTICATE_MESSAGE_METHOD as u64, + authenticate_param1, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + let ret: PublishStorageDealsReturn = rt + .call::( + Method::PublishStorageDealsExported as MethodNum, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert!(ret.valid_deals.get(0)); + + rt.verify(); + check_state(&rt); +} From d5f8048072fa49df5e4a40671d09778bd337e253 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 22 Nov 2022 13:00:28 -0500 Subject: [PATCH 191/339] Miner: Export several more methods (#863) * Miner: Export ChangeWorkerAddress * Miner: Export ChangePeerID * Miner: Export WithdrawBalance * Miner: Export ChangeMultiaddrs * Miner: Export ConfirmUpdateWorkerKey * Miner: Export RepayDebt * Miner: Export ChangeOwnerAddress * Miner: Add exported getters for PeerID & multiaddrs * Miner: Refactor: Rename ConfirmUpdateWorkerKey to ConfirmChangeWorkerAddress --- actors/miner/src/lib.rs | 52 +++++++++--- actors/miner/src/types.rs | 11 +++ .../miner/tests/change_owner_address_test.rs | 46 ++++++++++- actors/miner/tests/change_peer_id_test.rs | 46 ++++++++++- .../miner/tests/change_worker_address_test.rs | 80 ++++++++++++++++++- .../tests/confirm_update_worker_key_test.rs | 6 +- .../miner/tests/miner_actor_test_peer_info.rs | 49 +++++++++++- actors/miner/tests/repay_debts.rs | 53 +++++++++++- actors/miner/tests/util.rs | 52 +++++++----- actors/miner/tests/withdraw_balance.rs | 55 ++++++++++++- 10 files changed, 409 insertions(+), 41 deletions(-) diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 38a77f826..48cef69ce 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -112,7 +112,7 @@ pub enum Method { ChangeMultiaddrs = 18, CompactPartitions = 19, CompactSectorNumbers = 20, - ConfirmUpdateWorkerKey = 21, + ConfirmChangeWorkerAddress = 21, RepayDebt = 22, ChangeOwnerAddress = 23, DisputeWindowedPoSt = 24, @@ -125,6 +125,13 @@ pub enum Method { GetBeneficiary = 31, ExtendSectorExpiration2 = 32, // Method numbers derived from FRC-0042 standards + ChangeWorkerAddressExported = frc42_dispatch::method_hash!("ChangeWorkerAddress"), + ChangePeerIDExported = frc42_dispatch::method_hash!("ChangePeerID"), + WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"), + ChangeMultiaddrsExported = frc42_dispatch::method_hash!("ChangeMultiaddrs"), + ConfirmChangeWorkerAddressExported = frc42_dispatch::method_hash!("ConfirmChangeWorkerAddress"), + RepayDebtExported = frc42_dispatch::method_hash!("RepayDebt"), + ChangeOwnerAddressExported = frc42_dispatch::method_hash!("ChangeOwnerAddress"), ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), GetOwnerExported = frc42_dispatch::method_hash!("GetOwner"), @@ -132,6 +139,8 @@ pub enum Method { GetSectorSizeExported = frc42_dispatch::method_hash!("GetSectorSize"), GetAvailableBalanceExported = frc42_dispatch::method_hash!("GetAvailableBalance"), GetVestingFundsExported = frc42_dispatch::method_hash!("GetVestingFunds"), + GetPeerIDExported = frc42_dispatch::method_hash!("GetPeerID"), + GetMultiaddrsExported = frc42_dispatch::method_hash!("GetMultiaddrs"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -344,7 +353,7 @@ impl Actor { } /// Triggers a worker address change if a change has been requested and its effective epoch has arrived. - fn confirm_update_worker_key(rt: &mut impl Runtime) -> Result<(), ActorError> { + fn confirm_change_worker_address(rt: &mut impl Runtime) -> Result<(), ActorError> { rt.transaction(|state: &mut State, rt| { let mut info = get_miner_info(rt.store(), state)?; @@ -413,6 +422,13 @@ impl Actor { }) } + fn get_peer_id(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let peer_id = get_miner_info(rt.store(), &state)?.peer_id; + Ok(GetPeerIDReturn { peer_id }) + } + fn change_peer_id(rt: &mut impl Runtime, params: ChangePeerIDParams) -> Result<(), ActorError> { let policy = rt.policy(); check_peer_info(policy, ¶ms.new_id, &[])?; @@ -434,6 +450,13 @@ impl Actor { Ok(()) } + fn get_multiaddresses(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let multi_addrs = get_miner_info(rt.store(), &state)?.multi_address; + Ok(GetMultiaddrsReturn { multi_addrs }) + } + fn change_multiaddresses( rt: &mut impl Runtime, params: ChangeMultiaddrsParams, @@ -4936,11 +4959,11 @@ impl ActorCode for Actor { let res = Self::control_addresses(rt)?; Ok(RawBytes::serialize(&res)?) } - Some(Method::ChangeWorkerAddress) => { + Some(Method::ChangeWorkerAddress) | Some(Method::ChangeWorkerAddressExported) => { Self::change_worker_address(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangePeerID) => { + Some(Method::ChangePeerID) | Some(Method::ChangePeerIDExported) => { Self::change_peer_id(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -4988,7 +5011,7 @@ impl ActorCode for Actor { Self::report_consensus_fault(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::WithdrawBalance) => { + Some(Method::WithdrawBalance) | Some(Method::WithdrawBalanceExported) => { let res = Self::withdraw_balance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(&res)?) } @@ -4996,7 +5019,7 @@ impl ActorCode for Actor { Self::confirm_sector_proofs_valid(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ChangeMultiaddrs) => { + Some(Method::ChangeMultiaddrs) | Some(Method::ChangeMultiaddrsExported) => { Self::change_multiaddresses(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -5008,15 +5031,16 @@ impl ActorCode for Actor { Self::compact_sector_numbers(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::ConfirmUpdateWorkerKey) => { - Self::confirm_update_worker_key(rt)?; + Some(Method::ConfirmChangeWorkerAddress) + | Some(Method::ConfirmChangeWorkerAddressExported) => { + Self::confirm_change_worker_address(rt)?; Ok(RawBytes::default()) } - Some(Method::RepayDebt) => { + Some(Method::RepayDebt) | Some(Method::RepayDebtExported) => { Self::repay_debt(rt)?; Ok(RawBytes::default()) } - Some(Method::ChangeOwnerAddress) => { + Some(Method::ChangeOwnerAddress) | Some(Method::ChangeOwnerAddressExported) => { Self::change_owner_address(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -5077,6 +5101,14 @@ impl ActorCode for Actor { let res = Self::get_vesting_funds(rt)?; Ok(RawBytes::serialize(res)?) } + Some(Method::GetPeerIDExported) => { + let res = Self::get_peer_id(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetMultiaddrsExported) => { + let res = Self::get_multiaddresses(rt)?; + Ok(RawBytes::serialize(res)?) + } } } } diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 49c8290d7..1f92e6eb6 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -529,3 +529,14 @@ pub struct GetVestingFundsReturn { } impl Cbor for GetVestingFundsReturn {} + +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] +pub struct GetPeerIDReturn { + #[serde(with = "serde_bytes")] + pub peer_id: Vec, +} + +#[derive(Debug, Default, PartialEq, Clone, Serialize_tuple, Deserialize_tuple)] +pub struct GetMultiaddrsReturn { + pub multi_addrs: Vec, +} diff --git a/actors/miner/tests/change_owner_address_test.rs b/actors/miner/tests/change_owner_address_test.rs index 2fc1e5f58..e04d5c3e9 100644 --- a/actors/miner/tests/change_owner_address_test.rs +++ b/actors/miner/tests/change_owner_address_test.rs @@ -1,6 +1,9 @@ +use fil_actor_miner::{Actor, Method}; use fil_actors_runtime::test_utils::{ - expect_abort, new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, make_identity_cid, new_bls_addr, MockRuntime, + ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; +use fvm_ipld_encoding::RawBytes; use fvm_shared::econ::TokenAmount; use fvm_shared::{address::Address, error::ExitCode}; @@ -52,6 +55,47 @@ fn successful_change() { h.check_state(&rt); } +#[test] +fn change_owner_address_restricted_correctly() { + let (h, mut rt) = setup(); + + let params = &RawBytes::serialize(NEW_ADDRESS).unwrap(); + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeOwnerAddress as u64, params), + ); + + // can call the exported method + + rt.expect_validate_caller_addr(vec![h.owner]); + rt.call::(Method::ChangeOwnerAddressExported as u64, params).unwrap(); + + rt.verify(); + + let info = h.get_info(&rt); + assert_eq!(h.owner, info.owner); + assert_eq!(NEW_ADDRESS, info.pending_owner_address.unwrap()); + + // 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.call::(Method::ChangeOwnerAddressExported as u64, params).unwrap(); + + rt.verify(); + let info = h.get_info(&rt); + assert_eq!(NEW_ADDRESS, info.owner); + assert_eq!(NEW_ADDRESS, info.beneficiary); + assert!(info.pending_owner_address.is_none()); + + h.check_state(&rt); +} + #[test] fn successful_keep_beneficiary_when_change_owner() { let (mut h, mut rt) = setup(); diff --git a/actors/miner/tests/change_peer_id_test.rs b/actors/miner/tests/change_peer_id_test.rs index 1fd3cbeb2..1b704b277 100644 --- a/actors/miner/tests/change_peer_id_test.rs +++ b/actors/miner/tests/change_peer_id_test.rs @@ -1,4 +1,9 @@ -use fil_actors_runtime::test_utils::MockRuntime; +use fil_actor_miner::{Actor, ChangePeerIDParams, GetPeerIDReturn, Method}; +use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, make_identity_cid, MockRuntime, +}; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::error::ExitCode; mod util; use util::*; @@ -24,3 +29,42 @@ fn successfully_change_peer_id() { h.check_state(&rt); } + +#[test] +fn change_peer_id_restricted_correctly() { + let (h, mut rt) = setup(); + + let new_id = b"cthulhu".to_vec(); + + let params = RawBytes::serialize(ChangePeerIDParams { new_id: new_id.clone() }).unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.worker); + + // fail to call the unexported setter + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangePeerID as u64, ¶ms), + ); + + // call the exported setter + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.call::(Method::ChangePeerIDExported as u64, ¶ms).unwrap(); + + // call the exported getter + + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(new_id, ret.peer_id); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/change_worker_address_test.rs b/actors/miner/tests/change_worker_address_test.rs index d476543c3..d4a72adb0 100644 --- a/actors/miner/tests/change_worker_address_test.rs +++ b/actors/miner/tests/change_worker_address_test.rs @@ -11,6 +11,7 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; mod util; +use fil_actors_runtime::test_utils::make_identity_cid; use itertools::Itertools; use num_traits::Zero; use util::*; @@ -50,7 +51,7 @@ fn successfully_change_only_the_worker_address() { rt.set_epoch(deadline.period_end()); assert!(deadline.period_end() < effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let info = h.get_info(&rt); assert_eq!(info.pending_worker_key.unwrap().new_worker, new_worker); @@ -60,7 +61,7 @@ fn successfully_change_only_the_worker_address() { rt.set_epoch(effective_epoch); // enact worker change - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert address has changed let info = h.get_info(&rt); @@ -73,6 +74,77 @@ fn successfully_change_only_the_worker_address() { h.check_state(&rt); } +#[test] +fn change_and_confirm_worker_address_restricted_correctly() { + let (h, mut rt) = setup(); + + let original_control_addresses = h.control_addrs.clone(); + let new_worker = Address::new_id(999); + + rt.set_address_actor_type(new_worker, *ACCOUNT_ACTOR_CODE_ID); + + let params = RawBytes::serialize(ChangeWorkerAddressParams { + new_worker, + new_control_addresses: original_control_addresses, + }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeWorkerAddress as u64, ¶ms), + ); + + // call the exported method + rt.expect_send( + new_worker, + AccountMethod::PubkeyAddress as u64, + RawBytes::default(), + TokenAmount::zero(), + RawBytes::serialize(h.worker_key).unwrap(), + ExitCode::OK, + ); + + rt.expect_validate_caller_addr(vec![h.owner]); + + rt.call::(Method::ChangeWorkerAddressExported as u64, ¶ms).unwrap(); + + rt.verify(); + + // assert change has been made in state + let pending_worker_key = h.get_info(&rt).pending_worker_key.unwrap(); + assert_eq!(pending_worker_key.new_worker, new_worker); + + // confirmation time + + // move to deadline containing effective epoch + rt.set_epoch(rt.epoch + rt.policy.worker_key_change_delay); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ConfirmChangeWorkerAddress as u64, &RawBytes::default()), + ); + + // call the exported method + rt.expect_validate_caller_addr(vec![h.owner]); + rt.call::(Method::ConfirmChangeWorkerAddressExported as u64, &RawBytes::default()) + .unwrap(); + rt.verify(); + + // assert address has changed + let info = h.get_info(&rt); + assert_eq!(new_worker, info.worker); + + h.check_state(&rt); +} + #[test] fn change_cannot_be_overridden() { let (h, mut rt) = setup(); @@ -100,7 +172,7 @@ fn change_cannot_be_overridden() { assert_eq!(pending_worker_key.effective_at, effective_epoch); rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert original change is effected assert_eq!(new_worker_1, h.get_info(&rt).worker); @@ -146,7 +218,7 @@ fn successfully_change_both_worker_and_control_addresses() { // set current epoch and update worker key rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); // assert both worker and control addresses have changed let info = h.get_info(&rt); diff --git a/actors/miner/tests/confirm_update_worker_key_test.rs b/actors/miner/tests/confirm_update_worker_key_test.rs index d19ff4600..97f720ecd 100644 --- a/actors/miner/tests/confirm_update_worker_key_test.rs +++ b/actors/miner/tests/confirm_update_worker_key_test.rs @@ -32,7 +32,7 @@ fn successfully_changes_the_worker_address() { // confirm at effective epoch rt.set_epoch(effective_epoch); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); @@ -52,7 +52,7 @@ fn does_nothing_before_the_effective_date() { // confirm right before the effective epoch rt.set_epoch(effective_epoch - 1); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); @@ -70,7 +70,7 @@ fn does_nothing_before_the_effective_date() { fn does_nothing_when_no_update_is_set() { let (h, mut rt) = setup(); - h.confirm_update_worker_key(&mut rt).unwrap(); + h.confirm_change_worker_address(&mut rt).unwrap(); let state: State = rt.get_state(); let info = state.get_info(rt.store()).unwrap(); diff --git a/actors/miner/tests/miner_actor_test_peer_info.rs b/actors/miner/tests/miner_actor_test_peer_info.rs index 55c4a5250..478a9859a 100644 --- a/actors/miner/tests/miner_actor_test_peer_info.rs +++ b/actors/miner/tests/miner_actor_test_peer_info.rs @@ -1,6 +1,8 @@ use fil_actors_runtime::test_utils::*; -use fvm_ipld_encoding::BytesDe; +use fil_actor_miner::{Actor, ChangeMultiaddrsParams, GetMultiaddrsReturn, Method}; +use fvm_ipld_encoding::{BytesDe, RawBytes}; +use fvm_shared::error::ExitCode; mod util; @@ -109,3 +111,48 @@ fn cant_set_large_multiaddrs() { h.check_state(&rt); } + +#[test] +fn get_and_change_multiaddrs_restricted_correctly() { + let mut rt = MockRuntime::default(); + let h = util::ActorHarness::new(0); + + h.construct_and_verify(&mut rt); + + let new_multiaddrs = vec![BytesDe(vec![1, 3, 3, 7])]; + + let params = + &RawBytes::serialize(ChangeMultiaddrsParams { new_multi_addrs: new_multiaddrs.clone() }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.worker); + + // fail to call the unexported setter + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::ChangeMultiaddrs as u64, params), + ); + + // call the exported setter + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.call::(Method::ChangeMultiaddrsExported as u64, params).unwrap(); + rt.verify(); + + // call the exported getter + + rt.expect_validate_caller_any(); + let ret: GetMultiaddrsReturn = rt + .call::(Method::GetMultiaddrsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(new_multiaddrs, ret.multi_addrs); + + h.check_state(&rt); +} diff --git a/actors/miner/tests/repay_debts.rs b/actors/miner/tests/repay_debts.rs index d4dba48cd..740daabe5 100644 --- a/actors/miner/tests/repay_debts.rs +++ b/actors/miner/tests/repay_debts.rs @@ -1,7 +1,12 @@ -use fil_actor_miner::locked_reward_from_reward; +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::BURNT_FUNDS_ACTOR_ADDR; +use fvm_ipld_encoding::RawBytes; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; mod util; use util::*; @@ -48,6 +53,52 @@ fn pay_debt_entirely_from_balance() { h.check_state(&rt); } +#[test] +fn repay_debt_restricted_correctly() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + h.construct_and_verify(&mut rt); + + // introduce fee debt + let mut st = h.get_state(&rt); + let fee_debt: TokenAmount = 4 * &*BIG_BALANCE; + st.fee_debt = fee_debt.clone(); + rt.replace_state(&st); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::RepayDebt as u64, &RawBytes::default()), + ); + + // can call the exported method + + rt.expect_validate_caller_addr(h.caller_addrs()); + + rt.add_balance(fee_debt.clone()); + rt.set_received(fee_debt.clone()); + + rt.expect_send( + BURNT_FUNDS_ACTOR_ADDR, + METHOD_SEND, + RawBytes::default(), + fee_debt, + RawBytes::default(), + ExitCode::OK, + ); + + rt.call::(Method::RepayDebtExported as u64, &RawBytes::default()).unwrap(); + + rt.verify(); + + let st = h.get_state(&rt); + assert!(st.fee_debt.is_zero()); + h.check_state(&rt); +} + #[test] fn partially_repay_debt() { let h = ActorHarness::new(PERIOD_OFFSET); diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index a13d9545a..62fa9dae9 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -23,13 +23,14 @@ use fil_actor_miner::{ DeclareFaultsRecoveredParams, DeferredCronEventParams, DisputeWindowedPoStParams, ExpirationQueue, ExpirationSet, ExtendSectorExpiration2Params, ExtendSectorExpirationParams, FaultDeclaration, GetAvailableBalanceReturn, GetBeneficiaryReturn, GetControlAddressesReturn, - Method, MinerConstructorParams as ConstructorParams, MinerInfo, Partition, - PendingBeneficiaryChange, PoStPartition, PowerPair, PreCommitSectorBatchParams, - PreCommitSectorBatchParams2, PreCommitSectorParams, ProveCommitSectorParams, - RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, SectorPreCommitInfo, - SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, TerminateSectorsParams, - TerminationDeclaration, VestingFunds, WindowedPoSt, WithdrawBalanceParams, - WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, SECTORS_AMT_BITWIDTH, + GetMultiaddrsReturn, GetPeerIDReturn, Method, MinerConstructorParams as ConstructorParams, + MinerInfo, Partition, PendingBeneficiaryChange, PoStPartition, PowerPair, + PreCommitSectorBatchParams, PreCommitSectorBatchParams2, PreCommitSectorParams, + ProveCommitSectorParams, RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, + SectorPreCommitInfo, SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, + TerminateSectorsParams, TerminationDeclaration, VestingFunds, WindowedPoSt, + WithdrawBalanceParams, WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, + SECTORS_AMT_BITWIDTH, }; use fil_actor_miner::{Method as MinerMethod, ProveCommitAggregateParams}; use fil_actor_power::{ @@ -288,10 +289,15 @@ impl ActorHarness { expect_empty(result); rt.verify(); - let state = self.get_state(rt); - let info = state.get_info(&rt.store).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_id, info.peer_id); + assert_eq!(new_id, ret.peer_id); } pub fn set_peer_id_fail(&self, rt: &mut MockRuntime, new_id: Vec) { @@ -318,10 +324,15 @@ impl ActorHarness { expect_empty(result); rt.verify(); - let state = self.get_state(rt); - let info = state.get_info(&rt.store).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetMultiaddrsReturn = rt + .call::(Method::GetMultiaddrsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_multiaddrs, info.multi_address); + assert_eq!(new_multiaddrs, ret.multi_addrs); } pub fn set_multiaddr_fail(&self, rt: &mut MockRuntime, new_multiaddrs: Vec) { @@ -2084,10 +2095,15 @@ impl ActorHarness { .unwrap(); rt.verify(); - let state: State = rt.get_state(); - let info = state.get_info(rt.store()).unwrap(); + rt.expect_validate_caller_any(); + let ret: GetPeerIDReturn = rt + .call::(Method::GetPeerIDExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); - assert_eq!(new_id, info.peer_id); + assert_eq!(new_id, ret.peer_id); } pub fn repay_debts( @@ -2244,10 +2260,10 @@ impl ActorHarness { ret } - pub fn confirm_update_worker_key(&self, rt: &mut MockRuntime) -> Result<(), ActorError> { + pub fn confirm_change_worker_address(&self, rt: &mut MockRuntime) -> Result<(), ActorError> { rt.expect_validate_caller_addr(vec![self.owner]); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.owner); - rt.call::(Method::ConfirmUpdateWorkerKey as u64, &RawBytes::default())?; + rt.call::(Method::ConfirmChangeWorkerAddress as u64, &RawBytes::default())?; rt.verify(); Ok(()) diff --git a/actors/miner/tests/withdraw_balance.rs b/actors/miner/tests/withdraw_balance.rs index 0b4d79107..656f52fea 100644 --- a/actors/miner/tests/withdraw_balance.rs +++ b/actors/miner/tests/withdraw_balance.rs @@ -1,10 +1,16 @@ -use fil_actor_miner::BeneficiaryTerm; -use fil_actors_runtime::test_utils::{expect_abort, expect_abort_contains_message}; +use fil_actor_miner::{ + Actor, BeneficiaryTerm, Method, WithdrawBalanceParams, WithdrawBalanceReturn, +}; +use fil_actors_runtime::test_utils::{ + expect_abort, expect_abort_contains_message, make_identity_cid, +}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; mod util; use util::*; @@ -29,6 +35,51 @@ fn happy_path_withdraws_funds() { h.check_state(&rt); } +#[test] +fn withdraw_funds_restricted_correctly() { + let (h, mut rt) = setup(); + rt.set_balance(BIG_BALANCE.clone()); + let amount_requested = ONE_PERCENT_BALANCE.clone(); + + let params = + &RawBytes::serialize(WithdrawBalanceParams { amount_requested: amount_requested.clone() }) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), h.owner); + + // fail to call the unexported method + + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::WithdrawBalance as u64, params), + ); + + // call the exported method + + rt.expect_validate_caller_addr(vec![h.owner, h.beneficiary]); + rt.expect_send( + h.beneficiary, + METHOD_SEND, + RawBytes::default(), + amount_requested.clone(), + RawBytes::default(), + ExitCode::OK, + ); + + let ret = rt + .call::(Method::WithdrawBalanceExported as u64, params) + .unwrap() + .deserialize::() + .unwrap(); + + assert_eq!(amount_requested, ret.amount_withdrawn,); + + rt.verify(); + + h.check_state(&rt); +} + #[test] fn fails_if_miner_cant_repay_fee_debt() { let h = ActorHarness::new(PERIOD_OFFSET); From 4a566de941adc7dde4669dda7f68d858efff3e7e Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 23 Nov 2022 17:56:44 -0500 Subject: [PATCH 192/339] Power actor: Export methods to CreateMiner and get miner counts (#868) * Power: Export CreateMiner * Power Actor: Export MinerCount and MinerConsensusCount * Update actors/power/src/lib.rs Co-authored-by: Alex <445306+anorth@users.noreply.github.com> Co-authored-by: Alex <445306+anorth@users.noreply.github.com> --- actors/power/src/lib.rs | 33 +++++++++- actors/power/src/types.rs | 12 ++++ actors/power/tests/harness/mod.rs | 29 ++++++--- actors/power/tests/power_actor_tests.rs | 80 +++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 9d5552094..5eeac63a0 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -62,8 +62,11 @@ pub enum Method { SubmitPoRepForBulkVerify = 8, CurrentTotalPower = 9, // Method numbers derived from FRC-0042 standards + CreateMinerExported = frc42_dispatch::method_hash!("CreateMiner"), NetworkRawPowerExported = frc42_dispatch::method_hash!("NetworkRawPower"), MinerRawPowerExported = frc42_dispatch::method_hash!("MinerRawPower"), + MinerCountExported = frc42_dispatch::method_hash!("MinerCount"), + MinerConsensusCountExported = frc42_dispatch::method_hash!("MinerConsensusCount"), } pub const ERR_TOO_MANY_PROVE_COMMITS: ExitCode = ExitCode::new(32); @@ -396,6 +399,26 @@ impl Actor { Ok(MinerRawPowerReturn { raw_byte_power, meets_consensus_minimum }) } + /// Returns the total number of miners created, regardless of whether or not + /// they have any pledged storage. + fn miner_count(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerCountReturn { miner_count: st.miner_count }) + } + + /// Returns the total number of miners that have more than the consensus minimum amount of storage active. + /// Active means that the storage must not be faulty. + fn miner_consensus_count( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerConsensusCountReturn { miner_consensus_count: st.miner_above_min_power_count }) + } + fn process_batch_proof_verifies( rt: &mut impl Runtime, rewret: &ThisEpochRewardReturn, @@ -663,7 +686,7 @@ impl ActorCode for Actor { Self::constructor(rt)?; Ok(RawBytes::default()) } - Some(Method::CreateMiner) => { + Some(Method::CreateMiner) | Some(Method::CreateMinerExported) => { let res = Self::create_miner(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -700,6 +723,14 @@ impl ActorCode for Actor { let res = Self::miner_raw_power(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::MinerCountExported) => { + let res = Self::miner_count(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::MinerConsensusCountExported) => { + let res = Self::miner_consensus_count(rt)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 26f63b88d..1d23a29c9 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -93,3 +93,15 @@ pub struct MinerRawPowerReturn { } impl Cbor for MinerRawPowerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerCountReturn { + pub miner_count: i64, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerConsensusCountReturn { + pub miner_consensus_count: i64, +} diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 12cccbd5f..305c60861 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -1,15 +1,15 @@ use cid::Cid; use fil_actor_power::detail::GAS_ON_SUBMIT_VERIFY_SEAL; -use fil_actor_power::epoch_key; use fil_actor_power::ext::miner::ConfirmSectorProofsParams; use fil_actor_power::ext::miner::CONFIRM_SECTOR_PROOFS_VALID_METHOD; use fil_actor_power::ext::reward::Method::ThisEpochReward; use fil_actor_power::ext::reward::UPDATE_NETWORK_KPI; use fil_actor_power::testing::check_state_invariants; -use fil_actor_power::CronEvent; use fil_actor_power::EnrollCronEventParams; use fil_actor_power::CRON_QUEUE_AMT_BITWIDTH; use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; +use fil_actor_power::{epoch_key, MinerCountReturn}; +use fil_actor_power::{CronEvent, MinerConsensusCountReturn}; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; @@ -210,9 +210,15 @@ impl Harness { keys.iter().map(|k| Address::from_bytes(k).unwrap()).collect::>() } - pub fn miner_count(&self, rt: &MockRuntime) -> i64 { - let st: State = rt.get_state(); - st.miner_count + pub fn miner_count(&self, rt: &mut MockRuntime) -> i64 { + rt.expect_validate_caller_any(); + let ret: MinerCountReturn = rt + .call::(Method::MinerCountExported as MethodNum, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + ret.miner_count } pub fn this_epoch_baseline_power(&self) -> &StoragePower { @@ -366,8 +372,17 @@ impl Harness { } pub fn expect_miners_above_min_power(&self, rt: &mut MockRuntime, count: i64) { - let st: State = rt.get_state(); - assert_eq!(count, st.miner_above_min_power_count); + rt.expect_validate_caller_any(); + let ret: MinerConsensusCountReturn = rt + .call::( + Method::MinerConsensusCountExported as MethodNum, + &RawBytes::default(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(count, ret.miner_consensus_count); } pub fn expect_query_network_info(&self, rt: &mut MockRuntime) { diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 650aefb95..0d95a8dd5 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -13,11 +13,12 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; +use fvm_shared::MethodNum; use num_traits::Zero; use std::ops::Neg; use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, CreateMinerReturn, EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, }; @@ -326,8 +327,7 @@ fn new_miner_updates_miner_above_min_power_count() { h.window_post_proof = test.proof; h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap(); - let st: State = rt.get_state(); - assert_eq!(test.expected_miners, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, test.expected_miners); h.check_state(&rt); } } @@ -372,8 +372,7 @@ fn power_accounting_crossing_threshold() { let expected_total_above = &(power_unit * 4); h.expect_total_power_eager(&mut rt, expected_total_above, &(expected_total_above * 10)); - let st: State = rt.get_state(); - assert_eq!(4, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, 4); // Less than 4 miners above threshold again small miner power is counted again h.update_claimed_power(&mut rt, MINER4, &delta.neg(), &(delta.neg() * 10)); @@ -1011,7 +1010,7 @@ mod cron_tests { assert!(h.get_claim(&rt, &miner1).is_none()); // miner count has been reduced to 1 - assert_eq!(h.miner_count(&rt), 1); + assert_eq!(h.miner_count(&mut rt), 1); // next epoch, only the reward actor is invoked rt.set_epoch(3); @@ -1441,3 +1440,72 @@ mod submit_porep_for_bulk_verify_tests { h.check_state(&rt); } } + +#[test] +fn create_miner_restricted_correctly() { + let (h, mut rt) = setup(); + + let peer = "miner".as_bytes().to_vec(); + let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; + + let params = serialize( + &CreateMinerParams { + owner: *OWNER, + worker: *OWNER, + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer: peer.clone(), + multiaddrs: multiaddrs.clone(), + }, + "create miner params", + ) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), *OWNER); + + // cannot call the unexported method + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::CreateMiner as MethodNum, ¶ms), + ); + + // can call the exported method + + rt.expect_validate_caller_any(); + let expected_init_params = ExecParams { + code_cid: *MINER_ACTOR_CODE_ID, + constructor_params: serialize( + &MinerConstructorParams { + owner: *OWNER, + worker: *OWNER, + control_addresses: vec![], + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer_id: peer, + multi_addresses: multiaddrs, + }, + "minerctor params", + ) + .unwrap(), + }; + let create_miner_ret = CreateMinerReturn { id_address: *MINER, robust_address: *ACTOR }; + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC_METHOD, + RawBytes::serialize(expected_init_params).unwrap(), + TokenAmount::zero(), + RawBytes::serialize(create_miner_ret).unwrap(), + ExitCode::OK, + ); + + let ret: CreateMinerReturn = rt + .call::(Method::CreateMinerExported as MethodNum, ¶ms) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(ret.id_address, *MINER); + assert_eq!(ret.robust_address, *ACTOR); + + h.check_state(&rt); +} From 6e04569e0ad7027c7439467663c2b6e0def6edcb Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 28 Nov 2022 17:31:35 -0600 Subject: [PATCH 193/339] Verifreg: Export AddVerifiedClient and GetClaims (#873) * Verifreg: Rename AddVerifierClientParams to AddVerifiedClientParams * Verifreg: Export AddVerifiedClient and GetClaims --- actors/verifreg/src/lib.rs | 8 +- actors/verifreg/src/types.rs | 2 +- actors/verifreg/tests/harness/mod.rs | 4 +- actors/verifreg/tests/verifreg_actor_test.rs | 106 ++++++++++++++++-- test_vm/src/util.rs | 4 +- test_vm/tests/publish_deals_test.rs | 4 +- test_vm/tests/verifreg_remove_datacap_test.rs | 4 +- 7 files changed, 112 insertions(+), 20 deletions(-) diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index d7962cfd2..ded09f416 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -61,7 +61,9 @@ pub enum Method { ExtendClaimTerms = 11, RemoveExpiredClaims = 12, // Method numbers derived from FRC-0042 standards + AddVerifiedClientExported = frc42_dispatch::method_hash!("AddVerifiedClient"), RemoveExpiredAllocationsExported = frc42_dispatch::method_hash!("RemoveExpiredAllocations"), + GetClaimsExported = frc42_dispatch::method_hash!("GetClaims"), ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"), RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"), UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), @@ -145,7 +147,7 @@ impl Actor { pub fn add_verified_client( rt: &mut impl Runtime, - params: AddVerifierClientParams, + params: AddVerifiedClientParams, ) -> Result<(), ActorError> { // The caller will be verified by checking table below rt.validate_immediate_caller_accept_any()?; @@ -1085,7 +1087,7 @@ impl ActorCode for Actor { Self::remove_verifier(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::AddVerifiedClient) => { + Some(Method::AddVerifiedClient) | Some(Method::AddVerifiedClientExported) => { Self::add_verified_client(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } @@ -1107,7 +1109,7 @@ impl ActorCode for Actor { let res = Self::extend_claim_terms(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } - Some(Method::GetClaims) => { + Some(Method::GetClaims) | Some(Method::GetClaimsExported) => { let res = Self::get_claims(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } diff --git a/actors/verifreg/src/types.rs b/actors/verifreg/src/types.rs index a888fc029..bc4a595e5 100644 --- a/actors/verifreg/src/types.rs +++ b/actors/verifreg/src/types.rs @@ -30,7 +30,7 @@ impl Cbor for VerifierParams {} pub type AddVerifierParams = VerifierParams; -pub type AddVerifierClientParams = VerifierParams; +pub type AddVerifiedClientParams = VerifierParams; /// DataCap is an integer number of bytes. /// We can introduce policy changes and replace this in the future. diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index d608197af..2fe45ff82 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -14,7 +14,7 @@ use num_traits::{ToPrimitive, Zero}; use fil_actor_verifreg::testing::check_state_invariants; use fil_actor_verifreg::{ - ext, Actor as VerifregActor, AddVerifierClientParams, AddVerifierParams, Allocation, + ext, Actor as VerifregActor, AddVerifiedClientParams, AddVerifierParams, Allocation, AllocationID, AllocationRequest, AllocationRequests, AllocationsResponse, Claim, ClaimAllocationsParams, ClaimAllocationsReturn, ClaimExtensionRequest, ClaimID, DataCap, ExtendClaimTermsParams, ExtendClaimTermsReturn, GetClaimsParams, GetClaimsReturn, Method, @@ -187,7 +187,7 @@ impl Harness { ExitCode::OK, ); - let params = AddVerifierClientParams { address: *client, allowance: allowance.clone() }; + let params = AddVerifiedClientParams { address: *client, allowance: allowance.clone() }; let ret = rt.call::( Method::AddVerifiedClient as MethodNum, &RawBytes::serialize(params).unwrap(), diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index 642cd690f..c0660a029 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -246,9 +246,14 @@ mod clients { use fvm_shared::{MethodNum, METHOD_SEND}; use num_traits::Zero; - use fil_actor_verifreg::{Actor as VerifregActor, AddVerifierClientParams, DataCap, Method}; + use fil_actor_verifreg::{ + ext, Actor as VerifregActor, AddVerifiedClientParams, DataCap, Method, + }; use fil_actors_runtime::test_utils::*; + use fil_actors_runtime::{DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR}; + use harness::*; + use num_traits::ToPrimitive; use util::*; use crate::*; @@ -373,7 +378,7 @@ mod clients { let caller = Address::new_id(209); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, caller); rt.expect_validate_caller_any(); - let params = AddVerifierClientParams { address: *CLIENT, allowance: allowance_client }; + let params = AddVerifiedClientParams { address: *CLIENT, allowance: allowance_client }; expect_abort( ExitCode::USR_NOT_FOUND, rt.call::( @@ -384,6 +389,59 @@ mod clients { h.check_state(&rt); } + #[test] + fn add_verified_client_restricted_correctly() { + let (h, mut rt) = new_harness(); + let allowance_verifier = verifier_allowance(&rt); + let allowance_client = client_allowance(&rt); + h.add_verifier(&mut rt, &VERIFIER, &allowance_verifier).unwrap(); + + let params = + AddVerifiedClientParams { address: *CLIENT, allowance: allowance_client.clone() }; + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), *VERIFIER); + + // cannot call the unexported method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::AddVerifiedClient as MethodNum, + &RawBytes::serialize(params.clone()).unwrap(), + ), + ); + + rt.verify(); + + // can call the exported method num + + let mint_params = ext::datacap::MintParams { + to: *CLIENT, + amount: TokenAmount::from_whole(allowance_client.to_i64().unwrap()), + operators: vec![STORAGE_MARKET_ACTOR_ADDR], + }; + rt.expect_send( + DATACAP_TOKEN_ACTOR_ADDR, + ext::datacap::Method::Mint as MethodNum, + RawBytes::serialize(&mint_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + rt.expect_validate_caller_any(); + rt.call::( + Method::AddVerifiedClientExported as MethodNum, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap(); + + rt.verify(); + + h.check_state(&rt); + } + #[test] fn rejects_allowance_greater_than_verifier_cap() { let (h, mut rt) = new_harness(); @@ -445,7 +503,8 @@ mod allocs_claims { use std::str::FromStr; use fil_actor_verifreg::{ - Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, Method, State, + Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, GetClaimsParams, + GetClaimsReturn, Method, State, }; use fil_actor_verifreg::{Claim, ExtendClaimTermsReturn}; use fil_actors_runtime::cbor::serialize; @@ -909,7 +968,7 @@ mod allocs_claims { } #[test] - fn extend_claims_restricted_correctly() { + fn claims_restricted_correctly() { let (h, mut rt) = new_harness(); let size = MINIMUM_VERIFIED_ALLOCATION_SIZE as u64; let sector = 0; @@ -921,7 +980,8 @@ mod allocs_claims { let id1 = h.create_claim(&mut rt, &claim1).unwrap(); - // Extend claim terms and verify return value. + // First, let's extend some claims + let params = ExtendClaimTermsParams { terms: vec![ClaimTerm { provider: PROVIDER1, claim_id: id1, term_max: max_term + 1 }], }; @@ -929,7 +989,7 @@ mod allocs_claims { // set caller to not-builtin rt.set_caller(make_identity_cid(b"1234"), Address::new_id(CLIENT1)); - // cannot call the unexported method num + // cannot call the unexported extend method num expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "must be built-in", @@ -952,8 +1012,38 @@ mod allocs_claims { assert_eq!(ret.codes(), vec![ExitCode::OK]); - // Verify state directly. - assert_claim(&rt, PROVIDER1, id1, &Claim { term_max: max_term + 1, ..claim1 }); + // Now let's Get those Claims, and check them + + let params = GetClaimsParams { claim_ids: vec![id1], provider: PROVIDER1 }; + + // cannot call the unexported extend method num + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::( + Method::GetClaims as MethodNum, + &serialize(¶ms, "get claims params").unwrap(), + ), + ); + + rt.verify(); + + // can call the exported method num + rt.expect_validate_caller_any(); + let ret: GetClaimsReturn = rt + .call::( + Method::GetClaimsExported as MethodNum, + &serialize(¶ms, "get claims params").unwrap(), + ) + .unwrap() + .deserialize() + .expect("failed to deserialize get claims return"); + + rt.verify(); + + assert_eq!(ret.batch_info.codes(), vec![ExitCode::OK]); + assert_eq!(ret.claims, vec![Claim { term_max: max_term + 1, ..claim1 }]); + h.check_state(&rt); } } diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 72ce940dc..7ba03842d 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -42,7 +42,7 @@ use fil_actor_power::{ }; use fil_actor_reward::Method as RewardMethod; use fil_actor_verifreg::{ - AddVerifierClientParams, AllocationID, ClaimID, ClaimTerm, ExtendClaimTermsParams, + AddVerifiedClientParams, AllocationID, ClaimID, ClaimTerm, ExtendClaimTermsParams, GetClaimsParams, Method as VerifregMethod, RemoveExpiredAllocationsParams, VerifierParams, }; use fil_actors_runtime::cbor::deserialize; @@ -868,7 +868,7 @@ pub fn verifreg_add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) pub fn verifreg_add_client(v: &VM, verifier: Address, client: Address, allowance: StoragePower) { let add_client_params = - AddVerifierClientParams { address: client, allowance: allowance.clone() }; + AddVerifiedClientParams { address: client, allowance: allowance.clone() }; apply_ok( v, verifier, diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 242544165..39238d1a0 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -11,7 +11,7 @@ use fil_actor_miner::Method as MinerMethod; use fil_actor_power::Method as PowerMethod; use fil_actor_reward::Method as RewardMethod; -use fil_actor_verifreg::{AddVerifierClientParams, Method as VerifregMethod}; +use fil_actor_verifreg::{AddVerifiedClientParams, Method as VerifregMethod}; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; @@ -74,7 +74,7 @@ fn setup(store: &'_ MemoryBlockstore) -> (VM<'_>, Addrs, ChainEpoch) { // setup verified client verifreg_add_verifier(&v, verifier, StoragePower::from((32_u64 << 40) as u128)); - let add_client_params = AddVerifierClientParams { + let add_client_params = AddVerifiedClientParams { address: verified_client, allowance: StoragePower::from((1_u64 << 32) as u64), }; diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 8f098b298..24194ed43 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -15,7 +15,7 @@ use fil_actor_datacap::{ DestroyParams, Method as DataCapMethod, MintParams, State as DataCapState, }; use fil_actor_verifreg::{ - AddVerifierClientParams, DataCap, RemoveDataCapParams, RemoveDataCapRequest, + AddVerifiedClientParams, DataCap, RemoveDataCapParams, RemoveDataCapRequest, RemoveDataCapReturn, SIGNATURE_DOMAIN_SEPARATION_REMOVE_DATA_CAP, }; use fil_actor_verifreg::{AddrPairKey, Method as VerifregMethod}; @@ -47,7 +47,7 @@ fn remove_datacap_simple_successful_path() { // register the verified client let add_verified_client_params = - AddVerifierClientParams { address: verified_client, allowance: verifier_allowance.clone() }; + AddVerifiedClientParams { address: verified_client, allowance: verifier_allowance.clone() }; let mint_params = MintParams { to: verified_client, amount: TokenAmount::from_whole(verifier_allowance.to_i64().unwrap()), From fe6320c48f87acfb83b995ddb36279b776a30b06 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 5 Dec 2022 17:55:55 -0500 Subject: [PATCH 194/339] Datacap actor: Modify exported methods (#909) * Datacap: Export Mint and Destroy * Datacap actor: Deprecate all internal methods * Datacap actor: Rename BalanceOf to Balance * Datacap actor: Add Granularity method --- actors/datacap/src/lib.rs | 77 +++++++++++-------- actors/datacap/src/types.rs | 6 ++ actors/datacap/tests/datacap_actor_test.rs | 65 ++++++---------- actors/datacap/tests/harness/mod.rs | 34 +++++--- actors/market/src/ext.rs | 2 +- actors/verifreg/src/ext.rs | 20 ++--- actors/verifreg/src/lib.rs | 10 +-- actors/verifreg/tests/harness/mod.rs | 2 +- test_vm/src/util.rs | 16 ++-- test_vm/tests/datacap_tests.rs | 24 +++--- test_vm/tests/verifreg_remove_datacap_test.rs | 6 +- 11 files changed, 134 insertions(+), 128 deletions(-) diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index f17a805c3..8c0f1dd32 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -51,27 +51,29 @@ lazy_static! { #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - // Non-standard. - Mint = 2, - Destroy = 3, - // Static method numbers for token standard methods, for private use. - Name = 10, - Symbol = 11, - TotalSupply = 12, - BalanceOf = 13, - Transfer = 14, - TransferFrom = 15, - IncreaseAllowance = 16, - DecreaseAllowance = 17, - RevokeAllowance = 18, - Burn = 19, - BurnFrom = 20, - Allowance = 21, + // Deprecated in v10 + // Mint = 2, + // Destroy = 3, + // Name = 10, + // Symbol = 11, + // TotalSupply = 12, + // BalanceOf = 13, + // Transfer = 14, + // TransferFrom = 15, + // IncreaseAllowance = 16, + // DecreaseAllowance = 17, + // RevokeAllowance = 18, + // Burn = 19, + // BurnFrom = 20, + // Allowance = 21, // Method numbers derived from FRC-0042 standards + MintExported = frc42_dispatch::method_hash!("Mint"), + DestroyExported = frc42_dispatch::method_hash!("Destroy"), NameExported = frc42_dispatch::method_hash!("Name"), SymbolExported = frc42_dispatch::method_hash!("Symbol"), + GranularityExported = frc42_dispatch::method_hash!("GranularityExported"), TotalSupplyExported = frc42_dispatch::method_hash!("TotalSupply"), - BalanceOfExported = frc42_dispatch::method_hash!("BalanceOf"), + BalanceExported = frc42_dispatch::method_hash!("Balance"), TransferExported = frc42_dispatch::method_hash!("Transfer"), TransferFromExported = frc42_dispatch::method_hash!("TransferFrom"), IncreaseAllowanceExported = frc42_dispatch::method_hash!("IncreaseAllowance"), @@ -108,6 +110,11 @@ impl Actor { Ok("DCAP".to_string()) } + pub fn granularity(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + Ok(GranularityReturn { granularity: DATACAP_GRANULARITY }) + } + pub fn total_supply(rt: &mut impl Runtime, _: ()) -> Result { rt.validate_immediate_caller_accept_any()?; let mut st: State = rt.state()?; @@ -116,7 +123,7 @@ impl Actor { Ok(token.total_supply()) } - pub fn balance_of(rt: &mut impl Runtime, address: Address) -> Result { + pub fn balance(rt: &mut impl Runtime, address: Address) -> Result { // NOTE: mutability and method caller here are awkward for a read-only call rt.validate_immediate_caller_accept_any()?; let mut st: State = rt.state()?; @@ -473,59 +480,63 @@ impl ActorCode for Actor { Self::constructor(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Mint) => { + Some(Method::MintExported) => { let ret = Self::mint(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "mint result") } - Some(Method::Destroy) => { + Some(Method::DestroyExported) => { let ret = Self::destroy(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "destroy result") } - Some(Method::Name) | Some(Method::NameExported) => { + Some(Method::NameExported) => { let ret = Self::name(rt)?; serialize(&ret, "name result") } - Some(Method::Symbol) | Some(Method::SymbolExported) => { + Some(Method::SymbolExported) => { let ret = Self::symbol(rt)?; serialize(&ret, "symbol result") } - Some(Method::TotalSupply) | Some(Method::TotalSupplyExported) => { + Some(Method::GranularityExported) => { + let ret = Self::granularity(rt)?; + serialize(&ret, "granularity result") + } + Some(Method::TotalSupplyExported) => { let ret = Self::total_supply(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "total_supply result") } - Some(Method::BalanceOf) | Some(Method::BalanceOfExported) => { - let ret = Self::balance_of(rt, cbor::deserialize_params(params)?)?; + Some(Method::BalanceExported) => { + let ret = Self::balance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "balance_of result") } - Some(Method::Transfer) | Some(Method::TransferExported) => { + Some(Method::TransferExported) => { let ret = Self::transfer(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer result") } - Some(Method::TransferFrom) | Some(Method::TransferFromExported) => { + Some(Method::TransferFromExported) => { let ret = Self::transfer_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "transfer_from result") } - Some(Method::IncreaseAllowance) | Some(Method::IncreaseAllowanceExported) => { + Some(Method::IncreaseAllowanceExported) => { let ret = Self::increase_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "increase_allowance result") } - Some(Method::DecreaseAllowance) | Some(Method::DecreaseAllowanceExported) => { + Some(Method::DecreaseAllowanceExported) => { let ret = Self::decrease_allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "decrease_allowance result") } - Some(Method::RevokeAllowance) | Some(Method::RevokeAllowanceExported) => { + Some(Method::RevokeAllowanceExported) => { Self::revoke_allowance(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::default()) } - Some(Method::Burn) | Some(Method::BurnExported) => { + Some(Method::BurnExported) => { let ret = Self::burn(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn result") } - Some(Method::BurnFrom) | Some(Method::BurnFromExported) => { + Some(Method::BurnFromExported) => { let ret = Self::burn_from(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "burn_from result") } - Some(Method::Allowance) | Some(Method::AllowanceExported) => { + Some(Method::AllowanceExported) => { let ret = Self::allowance(rt, cbor::deserialize_params(params)?)?; serialize(&ret, "allowance result") } diff --git a/actors/datacap/src/types.rs b/actors/datacap/src/types.rs index 4e9ded1a2..de4e87b0b 100644 --- a/actors/datacap/src/types.rs +++ b/actors/datacap/src/types.rs @@ -22,3 +22,9 @@ pub struct DestroyParams { } impl Cbor for DestroyParams {} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] +#[serde(transparent)] +pub struct GranularityReturn { + pub granularity: u64, +} diff --git a/actors/datacap/tests/datacap_actor_test.rs b/actors/datacap/tests/datacap_actor_test.rs index 2c90367b0..c672e825a 100644 --- a/actors/datacap/tests/datacap_actor_test.rs +++ b/actors/datacap/tests/datacap_actor_test.rs @@ -16,7 +16,10 @@ lazy_static! { mod construction { use crate::*; + use fil_actor_datacap::{Actor, GranularityReturn, Method, DATACAP_GRANULARITY}; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; + use fvm_ipld_encoding::RawBytes; + use fvm_shared::MethodNum; #[test] fn construct_with_verified() { @@ -24,6 +27,15 @@ mod construction { let h = Harness { governor: VERIFIED_REGISTRY_ACTOR_ADDR }; h.construct_and_verify(&mut rt, &h.governor); h.check_state(&rt); + + rt.expect_validate_caller_any(); + let ret: GranularityReturn = rt + .call::(Method::GranularityExported as MethodNum, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + assert_eq!(ret.granularity, DATACAP_GRANULARITY) } } @@ -34,9 +46,7 @@ mod mint { use fil_actor_datacap::{Actor, Method, MintParams, INFINITE_ALLOWANCE}; use fil_actors_runtime::cbor::serialize; - use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, MARKET_ACTOR_CODE_ID, - }; + use fil_actors_runtime::test_utils::{expect_abort_contains_message, MARKET_ACTOR_CODE_ID}; use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use std::ops::Sub; @@ -53,32 +63,17 @@ mod mint { assert_eq!(amt, ret.supply); assert_eq!(amt, ret.balance); assert_eq!(amt, h.get_supply(&rt)); - assert_eq!(amt, h.get_balance(&rt, &*ALICE)); + assert_eq!(amt, h.get_balance(&mut rt, &*ALICE)); let ret = h.mint(&mut rt, &*BOB, &amt, vec![]).unwrap(); assert_eq!(&amt * 2, ret.supply); assert_eq!(amt, ret.balance); assert_eq!(&amt * 2, h.get_supply(&rt)); - assert_eq!(amt, h.get_balance(&rt, &*BOB)); + assert_eq!(amt, h.get_balance(&mut rt, &*BOB)); h.check_state(&rt); } - #[test] - fn requires_builtin_caller() { - let (mut rt, h) = make_harness(); - let amt = TokenAmount::from_whole(1); - let params = MintParams { to: *ALICE, amount: amt, operators: vec![] }; - - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), - ); - h.check_state(&rt); - } - #[test] fn requires_verifreg_caller() { let (mut rt, h) = make_harness(); @@ -90,7 +85,10 @@ mod mint { expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "caller address", - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params").unwrap()), + rt.call::( + Method::MintExported as MethodNum, + &serialize(¶ms, "params").unwrap(), + ), ); h.check_state(&rt); } @@ -206,11 +204,8 @@ mod transfer { mod destroy { use crate::{make_harness, ALICE, BOB}; use fil_actor_datacap::DestroyParams; - use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, - }; + use fil_actors_runtime::test_utils::{expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID}; use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR; - use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; @@ -218,21 +213,6 @@ mod destroy { use fil_actors_runtime::cbor::serialize; use fvm_shared::error::ExitCode; - #[test] - fn requires_builtin_caller() { - let (mut rt, h) = make_harness(); - let amt = TokenAmount::from_whole(1); - let params = DestroyParams { owner: *ALICE, amount: amt }; - - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); - expect_abort_contains_message( - ExitCode::USR_FORBIDDEN, - "must be built-in", - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), - ); - h.check_state(&rt); - } - #[test] fn only_governor_allowed() { let (mut rt, h) = make_harness(); @@ -248,7 +228,10 @@ mod destroy { expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "caller address", - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params").unwrap()), + rt.call::( + Method::DestroyExported as MethodNum, + &serialize(¶ms, "params").unwrap(), + ), ); // Destroying from 0 allowance having governor works diff --git a/actors/datacap/tests/harness/mod.rs b/actors/datacap/tests/harness/mod.rs index ee3e7e506..8f8f35b7f 100644 --- a/actors/datacap/tests/harness/mod.rs +++ b/actors/datacap/tests/harness/mod.rs @@ -93,8 +93,10 @@ impl Harness { let params = MintParams { to: *to, amount: amount.clone(), operators }; rt.set_caller(*VERIFREG_ACTOR_CODE_ID, VERIFIED_REGISTRY_ACTOR_ADDR); - let ret = - rt.call::(Method::Mint as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::MintExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -111,8 +113,10 @@ impl Harness { let params = DestroyParams { owner: *owner, amount: amount.clone() }; rt.set_caller(*VERIFREG_ACTOR_CODE_ID, VERIFIED_REGISTRY_ACTOR_ADDR); - let ret = - rt.call::(Method::Destroy as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::DestroyExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -155,8 +159,10 @@ impl Harness { ); let params = TransferParams { to: *to, amount: amount.clone(), operator_data }; - let ret = - rt.call::(Method::Transfer as MethodNum, &serialize(¶ms, "params")?)?; + let ret = rt.call::( + Method::TransferExported as MethodNum, + &serialize(¶ms, "params")?, + )?; rt.verify(); Ok(ret.deserialize().unwrap()) @@ -202,7 +208,7 @@ impl Harness { let params = TransferFromParams { to: *to, from: *from, amount: amount.clone(), operator_data }; let ret = rt.call::( - Method::TransferFrom as MethodNum, + Method::TransferFromExported as MethodNum, &serialize(¶ms, "params")?, )?; @@ -216,8 +222,18 @@ impl Harness { } // Reads a balance from state directly. - pub fn get_balance(&self, rt: &MockRuntime, address: &Address) -> TokenAmount { - rt.get_state::().token.get_balance(rt.store(), address.id().unwrap()).unwrap() + pub fn get_balance(&self, rt: &mut MockRuntime, address: &Address) -> TokenAmount { + rt.expect_validate_caller_any(); + let ret = rt + .call::( + Method::BalanceExported as MethodNum, + &serialize(&address, "params").unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + ret } // Reads allowance from state directly diff --git a/actors/market/src/ext.rs b/actors/market/src/ext.rs index 63d62b1d2..da9099ef2 100644 --- a/actors/market/src/ext.rs +++ b/actors/market/src/ext.rs @@ -80,7 +80,7 @@ pub mod verifreg { } pub mod datacap { - pub const TRANSFER_FROM_METHOD: u64 = 15; + pub const TRANSFER_FROM_METHOD: u64 = frc42_dispatch::method_hash!("TransferFrom"); } pub mod reward { diff --git a/actors/verifreg/src/ext.rs b/actors/verifreg/src/ext.rs index d70d74c46..1d9aa1425 100644 --- a/actors/verifreg/src/ext.rs +++ b/actors/verifreg/src/ext.rs @@ -8,21 +8,11 @@ pub mod datacap { #[repr(u64)] pub enum Method { - // Non-standard. - Mint = 2, - Destroy = 3, - // Static method numbers for token standard methods, for private use. - // Name = 10, - // Symbol = 11, - // TotalSupply = 12, - BalanceOf = 13, - Transfer = 14, - // TransferFrom = 15, - // IncreaseAllowance = 16, - // DecreaseAllowance = 17, - // RevokeAllowance = 18, - Burn = 19, - // BurnFrom = 20, + Mint = frc42_dispatch::method_hash!("Mint"), + Destroy = frc42_dispatch::method_hash!("Destroy"), + Balance = frc42_dispatch::method_hash!("Balance"), + Transfer = frc42_dispatch::method_hash!("Transfer"), + Burn = frc42_dispatch::method_hash!("Burn"), } #[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index ded09f416..fa45d4b21 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -114,7 +114,7 @@ impl Actor { } // Disallow existing clients as verifiers. - let token_balance = balance_of(rt, &verifier)?; + let token_balance = balance(rt, &verifier)?; if token_balance.is_positive() { return Err(actor_error!( illegal_argument, @@ -288,7 +288,7 @@ impl Actor { })?; // Burn the client's data cap tokens. - let balance = balance_of(rt, &client).context("failed to fetch balance")?; + let balance = balance(rt, &client).context("failed to fetch balance")?; let burnt = std::cmp::min(balance, params.data_cap_amount_to_remove); destroy(rt, &client, &burnt) .context(format!("failed to destroy {} from allowance for {}", &burnt, &client))?; @@ -719,13 +719,13 @@ fn is_verifier(rt: &impl Runtime, st: &State, address: Address) -> Result Result { +// Invokes Balance on the data cap token actor, and converts the result to whole units of data cap. +fn balance(rt: &mut impl Runtime, owner: &Address) -> Result { let params = serialize(owner, "owner address")?; let ret = rt .send( &DATACAP_TOKEN_ACTOR_ADDR, - ext::datacap::Method::BalanceOf as u64, + ext::datacap::Method::Balance as u64, params, TokenAmount::zero(), ) diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 2fe45ff82..0ed78c9e4 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -102,7 +102,7 @@ impl Harness { // Expect checking the verifier's token balance. rt.expect_send( DATACAP_TOKEN_ACTOR_ADDR, - ext::datacap::Method::BalanceOf as MethodNum, + ext::datacap::Method::Balance as MethodNum, RawBytes::serialize(&verifier_resolved).unwrap(), TokenAmount::zero(), serialize(&BigIntSer(&(cap * TOKEN_PRECISION)), "").unwrap(), diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 7ba03842d..5eaa7ce79 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -854,7 +854,7 @@ pub fn verifreg_add_verifier(v: &VM, verifier: Address, data_cap: StoragePower) params: Some(serialize(&add_verifier_params, "verifreg add verifier params").unwrap()), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::BalanceOf as u64, + method: DataCapMethod::BalanceExported as u64, params: Some(serialize(&verifier, "balance of params").unwrap()), code: Some(ExitCode::OK), ..Default::default() @@ -882,7 +882,7 @@ pub fn verifreg_add_client(v: &VM, verifier: Address, client: Address, allowance method: VerifregMethod::AddVerifiedClient as u64, subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Mint as u64, + method: DataCapMethod::MintExported as u64, params: Some( serialize( &MintParams { @@ -949,7 +949,7 @@ pub fn verifreg_remove_expired_allocations( method: VerifregMethod::RemoveExpiredAllocations as u64, subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Transfer as u64, + method: DataCapMethod::TransferExported as u64, code: Some(ExitCode::OK), params: Some( serialize( @@ -975,7 +975,7 @@ pub fn datacap_get_balance(v: &VM, address: Address) -> TokenAmount { address, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::BalanceOf as u64, + DataCapMethod::BalanceExported as u64, address, ); deserialize(&ret, "balance of return value").unwrap() @@ -1006,13 +1006,13 @@ pub fn datacap_extend_claim( client, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Transfer as u64, + DataCapMethod::TransferExported as u64, transfer_params, ); ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Transfer as u64, + method: DataCapMethod::TransferExported as u64, subinvocs: Some(vec![ExpectInvocation { to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::UniversalReceiverHook as u64, @@ -1040,7 +1040,7 @@ pub fn datacap_extend_claim( ), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Burn as u64, + method: DataCapMethod::BurnExported as u64, code: Some(ExitCode::OK), params: Some( serialize(&BurnParams { amount: token_amount }, "burn params").unwrap(), @@ -1153,7 +1153,7 @@ pub fn market_publish_deal( }; expect_publish_invocs.push(ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::TransferFrom as u64, + method: DataCapMethod::TransferFromExported as u64, params: Some( RawBytes::serialize(&TransferFromParams { from: deal_client, diff --git a/test_vm/tests/datacap_tests.rs b/test_vm/tests/datacap_tests.rs index e0361a264..83eea93d3 100644 --- a/test_vm/tests/datacap_tests.rs +++ b/test_vm/tests/datacap_tests.rs @@ -48,7 +48,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Mint as u64, + DataCapMethod::MintExported as u64, mint_params.clone(), ExitCode::USR_FORBIDDEN, ); @@ -59,7 +59,7 @@ fn datacap_transfer_scenario() { VERIFIED_REGISTRY_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Mint as u64, + DataCapMethod::MintExported as u64, mint_params, ); @@ -70,7 +70,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Allowance as u64, + DataCapMethod::AllowanceExported as u64, GetAllowanceParams { owner: client, operator }, ); @@ -116,7 +116,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_piece_too_small, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -130,7 +130,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_mismatched_datacap, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -149,7 +149,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, params_bad_term, ExitCode::USR_ILLEGAL_ARGUMENT, ); @@ -162,7 +162,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(¶ms_bad_receiver), ExitCode::USR_FORBIDDEN, // ExitCode(19) because non-operator has insufficient allowance ); @@ -173,7 +173,7 @@ fn datacap_transfer_scenario() { owner, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(&transfer_from_params), ExitCode::USR_INSUFFICIENT_FUNDS, // ExitCode(19) because non-operator has insufficient allowance ); @@ -183,7 +183,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, clone_params(&transfer_from_params), ); @@ -193,7 +193,7 @@ fn datacap_transfer_scenario() { operator, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::TransferFrom as u64, + DataCapMethod::TransferFromExported as u64, transfer_from_params, ExitCode::USR_INSUFFICIENT_FUNDS, ); @@ -212,7 +212,7 @@ fn call_name_symbol() { sender, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Name as u64, + DataCapMethod::NameExported as u64, RawBytes::default(), ) .deserialize() @@ -224,7 +224,7 @@ fn call_name_symbol() { sender, DATACAP_TOKEN_ACTOR_ADDR, TokenAmount::zero(), - DataCapMethod::Symbol as u64, + DataCapMethod::SymbolExported as u64, RawBytes::default(), ) .deserialize() diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 24194ed43..98012fd35 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -68,7 +68,7 @@ fn remove_datacap_simple_successful_path() { params: Some(serialize(&add_verified_client_params, "add verifier params").unwrap()), subinvocs: Some(vec![ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Mint as u64, + method: DataCapMethod::MintExported as u64, params: Some(serialize(&mint_params, "mint params").unwrap()), subinvocs: None, ..Default::default() @@ -334,7 +334,7 @@ fn expect_remove_datacap(params: &RemoveDataCapParams) -> ExpectInvocation { subinvocs: Some(vec![ ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::BalanceOf as u64, + method: DataCapMethod::BalanceExported as u64, params: Some( serialize(¶ms.verified_client_to_remove, "balance_of params").unwrap(), ), @@ -344,7 +344,7 @@ fn expect_remove_datacap(params: &RemoveDataCapParams) -> ExpectInvocation { }, ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::Destroy as u64, + method: DataCapMethod::DestroyExported as u64, params: Some( serialize( &DestroyParams { From 37cb754864bda333ef3989ac0b2d94cc065f502a Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 5 Dec 2022 18:36:20 -0500 Subject: [PATCH 195/339] fix: comments on newly exported methods (#910) --- actors/market/src/lib.rs | 2 +- actors/miner/src/lib.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index 2138aa695..f3afaa882 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -884,7 +884,7 @@ impl Actor { Ok(GetDealTermReturn { start: found.start_epoch, duration: found.duration() }) } - /// Returns the per-epoch price of a deal proposal. + /// Returns the total price that will be paid from the client to the provider for this deal. fn get_deal_total_price( rt: &mut impl Runtime, params: GetDealTotalPriceParams, diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 48cef69ce..eb3a3fe45 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -239,7 +239,7 @@ impl Actor { }) } - /// Returns the owner address + /// Returns the owner address. fn get_owner(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -270,7 +270,7 @@ impl Actor { Ok(IsControllingAddressReturn { is_controlling }) } - /// Returns the miner's sector size + /// Returns the miner's sector size. fn get_sector_size(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -422,6 +422,7 @@ impl Actor { }) } + /// Returns the Peer ID for this miner. fn get_peer_id(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -450,6 +451,7 @@ impl Actor { Ok(()) } + /// Returns the multiaddresses set for this miner. fn get_multiaddresses(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; From b35e0e5abb67ead4aacd91745b368ce2e926ee1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sun, 11 Dec 2022 18:55:20 +0000 Subject: [PATCH 196/339] add Hyperspace testnet parameters and build config. --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- Makefile | 3 +++ build.rs | 2 ++ runtime/build.rs | 2 +- runtime/src/runtime/chainid.rs | 5 ++--- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f27745098..c1304439e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] + 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/.github/workflows/release.yml b/.github/workflows/release.yml index 9b007046e..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', 'wallaby', 'devnet-wasm' ] + 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/Makefile b/Makefile index ee3907a13..f6dd1a99c 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,9 @@ bundle-devnet: deps-build 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 diff --git a/build.rs b/build.rs index d232d4aa7..20f1fd442 100644 --- a/build.rs +++ b/build.rs @@ -57,6 +57,8 @@ fn network_name() -> String { 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") { diff --git a/runtime/build.rs b/runtime/build.rs index e2193242a..a8040bfb8 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -13,7 +13,7 @@ static NETWORKS: &[(&[&str], &[&str])] = &[ ), (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), ( - &["wallaby"], + &["wallaby", "hyperspace"], &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"], ), (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), diff --git a/runtime/src/runtime/chainid.rs b/runtime/src/runtime/chainid.rs index 2237366b2..a1cbb8c2a 100644 --- a/runtime/src/runtime/chainid.rs +++ b/runtime/src/runtime/chainid.rs @@ -1,9 +1,8 @@ #[cfg(feature = "mainnet")] pub const CHAINID: u64 = 314; -// TODO buildernet -// #[cfg(feature = "buildernet")] -// pub const CHAINID: u64 = 3141; +#[cfg(feature = "hyperspace")] +pub const CHAINID: u64 = 3141; #[cfg(feature = "wallaby")] pub const CHAINID: u64 = 31415; From 4f52c6baed1b01d158a02628543ee56fdfae15e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sun, 11 Dec 2022 18:56:18 +0000 Subject: [PATCH 197/339] Revert "add Hyperspace testnet parameters and build config." This reverts commit b35e0e5abb67ead4aacd91745b368ce2e926ee1b. --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- Makefile | 3 --- build.rs | 2 -- runtime/build.rs | 2 +- runtime/src/runtime/chainid.rs | 5 +++-- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1304439e..f27745098 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'hyperspace', 'devnet-wasm' ] + network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b5824d99..9b007046e 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', 'wallaby', 'hyperspace', 'devnet-wasm' ] + network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 diff --git a/Makefile b/Makefile index f6dd1a99c..ee3907a13 100644 --- a/Makefile +++ b/Makefile @@ -65,9 +65,6 @@ bundle-devnet: deps-build 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 diff --git a/build.rs b/build.rs index 20f1fd442..d232d4aa7 100644 --- a/build.rs +++ b/build.rs @@ -57,8 +57,6 @@ fn network_name() -> String { 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") { diff --git a/runtime/build.rs b/runtime/build.rs index a8040bfb8..e2193242a 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -13,7 +13,7 @@ static NETWORKS: &[(&[&str], &[&str])] = &[ ), (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), ( - &["wallaby", "hyperspace"], + &["wallaby"], &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"], ), (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), diff --git a/runtime/src/runtime/chainid.rs b/runtime/src/runtime/chainid.rs index a1cbb8c2a..2237366b2 100644 --- a/runtime/src/runtime/chainid.rs +++ b/runtime/src/runtime/chainid.rs @@ -1,8 +1,9 @@ #[cfg(feature = "mainnet")] pub const CHAINID: u64 = 314; -#[cfg(feature = "hyperspace")] -pub const CHAINID: u64 = 3141; +// TODO buildernet +// #[cfg(feature = "buildernet")] +// pub const CHAINID: u64 = 3141; #[cfg(feature = "wallaby")] pub const CHAINID: u64 = 31415; From 9fee1176729374364621f41aa0af00a8fa207c87 Mon Sep 17 00:00:00 2001 From: Aayush Date: Sun, 11 Dec 2022 11:29:10 -0500 Subject: [PATCH 198/339] Runtime: Restrict internal API away from EVM actors --- actors/account/tests/account_actor_test.rs | 2 +- actors/init/tests/init_actor_test.rs | 2 +- actors/market/tests/harness.rs | 2 +- actors/market/tests/market_actor_test.rs | 4 ++-- actors/miner/tests/change_beneficiary_test.rs | 5 +++-- actors/miner/tests/change_owner_address_test.rs | 9 +++++---- actors/miner/tests/change_peer_id_test.rs | 5 +++-- actors/miner/tests/change_worker_address_test.rs | 5 +++-- actors/miner/tests/exported_getters.rs | 4 ++-- actors/miner/tests/miner_actor_test_peer_info.rs | 2 +- actors/miner/tests/repay_debts.rs | 5 +++-- actors/miner/tests/util.rs | 2 +- actors/miner/tests/withdraw_balance.rs | 5 +++-- actors/multisig/tests/multisig_actor_test.rs | 2 +- actors/power/tests/power_actor_tests.rs | 6 +++--- actors/verifreg/tests/verifreg_actor_test.rs | 6 +++--- runtime/src/builtin/shared.rs | 16 +++++++++++----- 17 files changed, 47 insertions(+), 35 deletions(-) diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index c1a631314..3dfe80abb 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -66,7 +66,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::( Method::UniversalReceiverHook as MethodNum, diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 949d772be..4b0025c35 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -290,7 +290,7 @@ fn exec_restricted_correctly() { construct_and_verify(&mut rt); // 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 let fake_constructor_params = diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 36a071c2d..311fd6fe0 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -124,7 +124,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::(Method::GetBalanceExported as u64, &RawBytes::serialize(addr).unwrap()) diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index be3ebd637..97813e113 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -1953,7 +1953,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( @@ -2018,7 +2018,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/tests/change_beneficiary_test.rs b/actors/miner/tests/change_beneficiary_test.rs index 7d1c9fb3b..398e86e5b 100644 --- a/actors/miner/tests/change_beneficiary_test.rs +++ b/actors/miner/tests/change_beneficiary_test.rs @@ -1,6 +1,6 @@ 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_ipld_encoding::RawBytes; use fvm_shared::clock::ChainEpoch; @@ -8,6 +8,7 @@ use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, MethodNum use num_traits::Zero; mod util; + use util::*; fn setup() -> (ActorHarness, MockRuntime) { @@ -450,7 +451,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 e04d5c3e9..7e079d51f 100644 --- a/actors/miner/tests/change_owner_address_test.rs +++ b/actors/miner/tests/change_owner_address_test.rs @@ -1,13 +1,14 @@ use fil_actor_miner::{Actor, 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::RawBytes; use fvm_shared::econ::TokenAmount; use fvm_shared::{address::Address, error::ExitCode}; mod util; + use util::*; const NEW_ADDRESS: Address = Address::new_id(1001); @@ -60,7 +61,7 @@ fn change_owner_address_restricted_correctly() { let (h, mut rt) = setup(); let params = &RawBytes::serialize(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 @@ -84,7 +85,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 1b704b277..bdf325fb2 100644 --- a/actors/miner/tests/change_peer_id_test.rs +++ b/actors/miner/tests/change_peer_id_test.rs @@ -1,11 +1,12 @@ 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::RawBytes; use fvm_shared::error::ExitCode; mod util; + use util::*; fn setup() -> (ActorHarness, MockRuntime) { @@ -38,7 +39,7 @@ fn change_peer_id_restricted_correctly() { let params = RawBytes::serialize(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 d4a72adb0..660a6218f 100644 --- a/actors/miner/tests/change_worker_address_test.rs +++ b/actors/miner/tests/change_worker_address_test.rs @@ -11,7 +11,8 @@ use fvm_ipld_encoding::RawBytes; 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 itertools::Itertools; use num_traits::Zero; use util::*; @@ -89,7 +90,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 459ec768d..eb87fa55b 100644 --- a/actors/miner/tests/exported_getters.rs +++ b/actors/miner/tests/exported_getters.rs @@ -3,7 +3,7 @@ use fil_actor_miner::{ IsControllingAddressParam, IsControllingAddressReturn, Method, }; use fil_actors_runtime::cbor::serialize; -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::RawBytes; use fvm_shared::address::Address; @@ -28,7 +28,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 478a9859a..5b9752e1b 100644 --- a/actors/miner/tests/miner_actor_test_peer_info.rs +++ b/actors/miner/tests/miner_actor_test_peer_info.rs @@ -125,7 +125,7 @@ fn get_and_change_multiaddrs_restricted_correctly() { &RawBytes::serialize(ChangeMultiaddrsParams { new_multi_addrs: new_multiaddrs.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/repay_debts.rs b/actors/miner/tests/repay_debts.rs index 740daabe5..e3b8c057a 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_ipld_encoding::RawBytes; use fvm_shared::bigint::Zero; @@ -9,6 +9,7 @@ use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; mod util; + use util::*; const PERIOD_OFFSET: ChainEpoch = 100; @@ -65,7 +66,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 fe8b8e1d3..236e0bdd6 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -2552,7 +2552,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, &RawBytes::default())? diff --git a/actors/miner/tests/withdraw_balance.rs b/actors/miner/tests/withdraw_balance.rs index 656f52fea..e3ed26c8d 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::RawBytes; use fvm_shared::address::Address; @@ -13,6 +13,7 @@ use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; mod util; + use util::*; const PERIOD_OFFSET: ChainEpoch = 100; @@ -45,7 +46,7 @@ fn withdraw_funds_restricted_correctly() { &RawBytes::serialize(WithdrawBalanceParams { amount_requested: amount_requested.clone() }) .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/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index b340b5c04..3e495811e 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -770,7 +770,7 @@ fn test_propose_restricted_correctly() { h.construct_and_verify(&mut rt, 2, no_unlock_duration, start_epoch, signers); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"102"), Address::new_id(102)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(102)); let propose_params = serialize( &ProposeParams { to: chuck, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 0d95a8dd5..56b8db4d1 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}; @@ -581,7 +581,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 @@ -1460,7 +1460,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/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index c0660a029..361f2b5e7 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -400,7 +400,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( @@ -513,7 +513,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::*; @@ -987,7 +987,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/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 4293c75a9..4596303d9 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_shared::address::Address; use fvm_shared::METHOD_SEND; @@ -53,14 +54,19 @@ where None => { return Err( actor_error!(forbidden; "no code for caller {} of method {}", caller, method), - ) + ); } 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(_) => {} } } } From a796fe88402c2b2886c414e3748eb621df6025c6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 12 Dec 2022 20:46:07 +0200 Subject: [PATCH 199/339] EVM reverts when logging in static mode (#938) --- actors/evm/src/interpreter/instructions/log.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log.rs index d6f0d8d02..3da15b2f0 100644 --- a/actors/evm/src/interpreter/instructions/log.rs +++ b/actors/evm/src/interpreter/instructions/log.rs @@ -21,6 +21,10 @@ pub fn log( size: U256, topics: &[U256], ) -> Result<(), StatusCode> { + if system.readonly { + return Err(StatusCode::StaticModeViolation); + } + // 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, From e6458c6b8b0e6da7ca53a4cba0a28aeb23ad6e9f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 12 Dec 2022 11:11:56 -0800 Subject: [PATCH 200/339] fix: decode revert data (#935) Also, always don't abort/revert if we fail to decode the revert data. Instead, just return the data without decoding it. This shouldn't happen if we call an EVM contract, but may happen if we call another contract. --- .../evm/src/interpreter/instructions/call.rs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 0038f20d7..6277c9c2a 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -297,19 +297,21 @@ pub fn call_generic( "unsupported opcode".to_string(), )), }; - match call_result { - Ok(result) => { - // Support the "empty" result. We often use this to mean "returned nothing" and - // it's important to support, e.g., sending to accounts. - if result.is_empty() { - (1, Vec::new()) - } else { - // TODO: support IPLD codecs #758 - let BytesDe(result) = result.deserialize()?; - (1, result) - } - } - Err(mut ae) => (0, ae.take_data().into()), + let (code, data) = match call_result { + Ok(result) => (1, result), + Err(mut ae) => (0, ae.take_data()), + }; + // Support the "empty" result. We often use this to mean "returned nothing" and + // it's important to support, e.g., sending to accounts. + if data.is_empty() { + (code, Vec::new()) + } else { + // TODO: support IPLD codecs #758 + // 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". + let result = data.deserialize().map(|BytesDe(d)| d).unwrap_or_else(|_| data.into()); + (code, result) } } }; From 9d02326b7a78b5dcc6dfb5c8fe6f267ccea84bd6 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 8 Dec 2022 09:38:49 -0500 Subject: [PATCH 201/339] Miner: Export method to GetPendingOwner --- actors/miner/src/lib.rs | 6 +++--- actors/miner/src/types.rs | 8 +++++--- actors/miner/tests/change_owner_address_test.rs | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index eb3a3fe45..ee593a5e8 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -239,12 +239,12 @@ impl Actor { }) } - /// Returns the owner address. + /// Returns the owner address, as well as the proposed new owner (if any). fn get_owner(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let owner = get_miner_info(rt.store(), &state)?.owner; - Ok(GetOwnerReturn { owner }) + let info = get_miner_info(rt.store(), &state)?; + Ok(GetOwnerReturn { owner: info.owner, proposed: info.pending_owner_address }) } /// Returns whether the provided address is "controlling". diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 1f92e6eb6..72651b113 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -170,7 +170,8 @@ impl Cbor for SectorClaim {} pub struct ExpirationExtension2 { pub deadline: u64, pub partition: u64, - pub sectors: BitField, // IDs of sectors without FIL+ claims + // IDs of sectors without FIL+ claims + pub sectors: BitField, pub sectors_with_claims: Vec, pub new_expiration: ChainEpoch, } @@ -314,6 +315,7 @@ impl Cbor for PreCommitSectorParams {} pub struct PreCommitSectorBatchParams { pub sectors: Vec, } + impl Cbor for PreCommitSectorBatchParams {} #[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] @@ -483,10 +485,10 @@ pub struct GetBeneficiaryReturn { impl Cbor for GetBeneficiaryReturn {} -#[derive(Serialize_tuple, Deserialize_tuple)] -#[serde(transparent)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple)] pub struct GetOwnerReturn { pub owner: Address, + pub proposed: Option
, } impl Cbor for GetOwnerReturn {} diff --git a/actors/miner/tests/change_owner_address_test.rs b/actors/miner/tests/change_owner_address_test.rs index e04d5c3e9..4323b85f9 100644 --- a/actors/miner/tests/change_owner_address_test.rs +++ b/actors/miner/tests/change_owner_address_test.rs @@ -1,4 +1,4 @@ -use fil_actor_miner::{Actor, Method}; +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, @@ -40,9 +40,17 @@ fn successful_change() { rt.set_caller(*MULTISIG_ACTOR_CODE_ID, h.owner); h.change_owner_address(&mut rt, NEW_ADDRESS).unwrap(); - let info = h.get_info(&rt); - assert_eq!(h.owner, info.owner); - assert_eq!(NEW_ADDRESS, info.pending_owner_address.unwrap()); + // Set to non-builtin caller to confirm exported correctly + rt.set_caller(make_identity_cid(b"1234"), OTHER_ADDRESS); + rt.expect_validate_caller_any(); + let ret: GetOwnerReturn = rt + .call::(Method::GetOwnerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(h.owner, ret.owner); + assert_eq!(NEW_ADDRESS, ret.proposed.unwrap()); rt.set_caller(*MULTISIG_ACTOR_CODE_ID, NEW_ADDRESS); h.change_owner_address(&mut rt, NEW_ADDRESS).unwrap(); From 8d2d9d52753ebd58af7eef324cd4aae548485813 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 12 Dec 2022 12:29:38 -0800 Subject: [PATCH 202/339] EVM: Dont revert on invalid call to exthash (#921) * dont revert on invalid call to exthash * EXT opcode fixes: - add GetBytecodeHash method - store bytecode hash, keccak256(bytecode) in state - extcodehash return empty hash for precompiles and keccak256([0xfe]) for native actors * update storage benchmark outputs * return empy hash for accounts, newtype for evm bytecode hash values * re-update benchmarks --- .../evm/src/interpreter/instructions/call.rs | 8 +- .../evm/src/interpreter/instructions/ext.rs | 62 ++- actors/evm/src/interpreter/system.rs | 53 ++- actors/evm/src/lib.rs | 13 + actors/evm/src/state.rs | 3 + actors/evm/tests/ext_opcodes.rs | 65 ++- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../measurements/array_read_n100.jsonline | 200 ++++----- .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../measurements/inc_one_vs_all.jsonline | 40 +- .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../measurements/mapping_read_n100.jsonline | 200 ++++----- 17 files changed, 1784 insertions(+), 1660 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 6277c9c2a..cfb4f51d2 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -5,7 +5,7 @@ use fvm_shared::{address::Address, sys::SendFlags, METHOD_SEND}; use crate::interpreter::precompiles::PrecompileContext; -use super::ext::{get_cid_type, get_evm_bytecode_cid, ContractType}; +use super::ext::{get_contract_type, get_evm_bytecode_cid, ContractType}; use { super::memory::{copy_to_memory, get_memory_region}, @@ -268,7 +268,7 @@ pub fn call_generic( system.send(&dst_addr, method, params, value, gas_limit, send_flags) } } - CallKind::DelegateCall => match get_cid_type(system.rt, dst) { + CallKind::DelegateCall => match get_contract_type(system.rt, dst) { ContractType::EVM(dst_addr) => { // If we're calling an actual EVM actor, get it's code. let code = get_evm_bytecode_cid(system.rt, &dst_addr)?; @@ -291,6 +291,10 @@ pub fn call_generic( ContractType::Native(_) => { Err(ActorError::forbidden("cannot delegate-call to native actors".into())) } + ContractType::Precompile => Err(ActorError::assertion_failed( + "Reached a precompile address when a precompile should've been caught earlier in the system" + .to_string(), + )), }, CallKind::CallCode => Err(ActorError::unchecked( EVM_CONTRACT_EXECUTION_ERROR, diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index c448d8754..12f46b9b4 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -1,17 +1,26 @@ -use crate::interpreter::address::EthAddress; use crate::interpreter::instructions::memory::copy_to_memory; +use crate::interpreter::{address::EthAddress, precompiles::Precompiles}; use crate::U256; use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::ActorError; use fvm_ipld_blockstore::Blockstore; use fvm_shared::{address::Address, econ::TokenAmount}; +use multihash::Multihash; use num_traits::Zero; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, }; +/// Keccak256 hash of `[0xfe]`, "native bytecode" +const NATIVE_BYTECODE_HASH: [u8; 32] = + hex_literal::hex!("bcc90f2d6dada5b18e155c17a1c0a55920aae94f39857d39d0d8ed07ae8f228b"); + +/// Keccak256 hash of `[]`, empty bytecode +const EMPTY_EVM_HASH: [u8; 32] = + hex_literal::hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + pub fn extcodesize( _state: &mut ExecutionState, system: &System, @@ -20,12 +29,12 @@ pub fn extcodesize( // 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_cid_type(system.rt, addr) { + let len = match get_contract_type(system.rt, addr) { ContractType::EVM(addr) => { get_evm_bytecode(system.rt, &addr).map(|bytecode| bytecode.len())? } ContractType::Native(_) => 1, - // account and not found are flattened to 0 size + // account, not found, and precompiles are 0 size _ => 0, }; @@ -37,15 +46,33 @@ pub fn extcodehash( system: &System, addr: U256, ) -> Result { - let addr = match get_cid_type(system.rt, addr) { - ContractType::EVM(a) => Ok(a), - _ => Err(StatusCode::InvalidArgument( - "Cannot invoke EXTCODEHASH for non-EVM actor.".to_string(), - )), - }?; - let bytecode_cid = get_evm_bytecode_cid(system.rt, &addr)?; + let addr = match get_contract_type(system.rt, addr) { + ContractType::EVM(a) => a, + // _Technically_ since we have native "bytecode" set as 0xfe this is valid, though we cant differentiate between + ContractType::Native(_) => return Ok(NATIVE_BYTECODE_HASH.into()), + // Precompiles "exist" and therefore aren't empty (although spec-wise they can be either 0 or keccak("") ). + ContractType::Precompile => return Ok(EMPTY_EVM_HASH.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(EMPTY_EVM_HASH.into()), + // Everything else is flattened to 0 + _ => return Ok(U256::zero()), + }; + + // multihash { keccak256(bytecode) } + let bytecode_hash: Multihash = system + .rt + .send( + &addr, + crate::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + )? + .deserialize()?; - let digest = bytecode_cid.hash().digest(); + let digest = bytecode_hash.digest(); // Take the first 32 bytes of the Multihash let digest_len = digest.len().min(32); @@ -60,9 +87,9 @@ pub fn extcodecopy( data_offset: U256, size: U256, ) -> Result<(), StatusCode> { - let bytecode = match get_cid_type(system.rt, addr) { + let bytecode = match get_contract_type(system.rt, addr) { ContractType::EVM(addr) => get_evm_bytecode(system.rt, &addr)?, - ContractType::NotFound | ContractType::Account => Vec::new(), + 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], @@ -73,7 +100,8 @@ pub fn extcodecopy( #[derive(Debug)] pub enum ContractType { - /// EVM Address and the CID of the actor (not the bytecode) + Precompile, + /// EVM ID Address and the CID of the actor (not the bytecode) EVM(Address), Native(Cid), Account, @@ -81,8 +109,12 @@ pub enum ContractType { } /// Resolves an address to the address type -pub fn get_cid_type(rt: &impl Runtime, addr: U256) -> ContractType { +pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { let addr: EthAddress = addr.into(); + // precompiles cant be resolved by the FVM + if Precompiles::::is_precompile(&addr.as_evm_word()) { + return ContractType::Precompile; + } addr.try_into() .ok() // into filecoin address diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 905a8e46a..54409af6c 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -8,12 +8,13 @@ use fvm_ipld_encoding::{CborStore, RawBytes}; use fvm_ipld_kamt::HashedKey; use fvm_shared::{ address::{Address, Payload}, + crypto::hash::SupportedHashes, econ::TokenAmount, error::ExitCode, sys::SendFlags, MethodNum, IPLD_RAW, }; -use multihash::Code; +use multihash::{Code, Multihash}; use crate::state::State; @@ -76,13 +77,28 @@ pub type StateKamt = Kamt; /// 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: Multihash, +} + +impl EvmBytecode { + fn new(cid: Cid, evm_hash: Multihash) -> 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. - bytecode: Option, + /// (blake2b256(ipld_raw(bytecode)), keccak256(bytecode)) + bytecode: Option, /// The contract's EVM storage slots. slots: StateKamt, /// The contracts "nonce" (incremented when creating new actors). @@ -134,7 +150,7 @@ impl<'r, RT: Runtime> System<'r, RT> { .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?, nonce: state.nonce, saved_state_root: Some(state_root), - bytecode: Some(state.bytecode), + bytecode: Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)), readonly: read_only, }) } @@ -174,8 +190,9 @@ impl<'r, RT: Runtime> System<'r, RT> { return Err(ActorError::forbidden("contract invocation is read only".to_string())); } - let bytecode_cid = match self.bytecode { + let EvmBytecode { cid, evm_hash } = match self.bytecode { Some(cid) => cid, + // set empty bytecode hashes None => self.set_bytecode(&[])?, }; let new_root = self @@ -183,7 +200,8 @@ impl<'r, RT: Runtime> System<'r, RT> { .store() .put_cbor( &State { - bytecode: bytecode_cid, + bytecode: cid, + bytecode_hash: evm_hash, contract_state: self.slots.flush().context_code( ExitCode::USR_ILLEGAL_STATE, "failed to flush contract state", @@ -222,17 +240,22 @@ impl<'r, RT: Runtime> System<'r, RT> { .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?; self.nonce = state.nonce; self.saved_state_root = Some(root); - self.bytecode = Some(state.bytecode); + self.bytecode = Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)); Ok(()) } /// Load the bytecode. pub fn load_bytecode(&self) -> Result, ActorError> { - Ok(self.bytecode.as_ref().map(|k| load_bytecode(self.rt.store(), k)).transpose()?.flatten()) + Ok(self + .bytecode + .as_ref() + .map(|EvmBytecode { cid, .. }| load_bytecode(self.rt.store(), cid)) + .transpose()? + .flatten()) } /// Set the bytecode. - pub fn set_bytecode(&mut self, bytecode: &[u8]) -> Result { + 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!( @@ -245,13 +268,21 @@ impl<'r, RT: Runtime> System<'r, RT> { "EIP-3541: Contract code starting with the 0xEF byte is disallowed.".into(), )); } - let k = self + + let code_hash = multihash::Multihash::wrap( + SupportedHashes::Keccak256 as u64, + &self.rt.hash(SupportedHashes::Keccak256, bytecode), + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to hash bytecode with keccak")?; + + let cid = self .rt .store() .put(Code::Blake2b256, &Block::new(IPLD_RAW, bytecode)) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to write bytecode")?; - self.bytecode = Some(k); - Ok(k) + let bytecode = EvmBytecode::new(cid, code_hash); + self.bytecode = Some(bytecode); + Ok(bytecode) } /// Get value of a storage key. diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 1d28794c9..e41793001 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -55,6 +55,7 @@ pub enum Method { GetBytecode = 3, GetStorageAt = 4, InvokeContractDelegate = 5, + GetBytecodeHash = 6, } pub struct EvmContractActor; @@ -211,6 +212,14 @@ impl EvmContractActor { Ok(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()?; + + let state: State = rt.state()?; + Ok(state.bytecode_hash) + } + pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result where RT: Runtime, @@ -278,6 +287,10 @@ impl ActorCode for EvmContractActor { let cid = Self::bytecode(rt)?; Ok(RawBytes::serialize(cid)?) } + Some(Method::GetBytecodeHash) => { + let cid = Self::bytecode(rt)?; + Ok(RawBytes::serialize(cid)?) + } Some(Method::GetStorageAt) => { let value = Self::storage_at(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(value)?) diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index fa9358c06..94f25be55 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -13,6 +13,9 @@ pub struct State { /// initialization code by the constructor. pub bytecode: Cid, + /// The EVM contract bytecode hash keccak256(bytecode) + pub bytecode_hash: multihash::Multihash, + /// The EVM contract state dictionary. /// All eth contract state is a map of U256 -> U256 values. /// diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 0df406098..516c16168 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -3,11 +3,13 @@ mod asm; use cid::Cid; use evm::interpreter::U256; use fil_actor_evm as evm; +use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::*; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; 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; @@ -141,6 +143,7 @@ fn test_extcodehash() { %dispatch_begin() %dispatch(0x00, evm_contract) %dispatch(0x01, native_actor) +%dispatch(0x02, non_exist) %dispatch_end() evm_contract: @@ -157,6 +160,13 @@ native_actor: extcodehash %return_stack_word() +non_exist: + jumpdest + # get code hash of address 0xff + push20 0xff000000000000000000000000000000000000ff + extcodehash + %return_stack_word() + "#; asm::new_contract("extcodehash", init, body).unwrap() @@ -172,31 +182,43 @@ 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("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu").unwrap(); + // a random hash value + let bytecode = b"foo bar boxy"; + let bytecode_hash = Multihash::wrap( + SupportedHashes::Keccak256 as u64, + rt.hash(SupportedHashes::Keccak256, bytecode).as_slice(), + ) + .unwrap(); rt.expect_send( evm_target, - evm::Method::GetBytecode as u64, + evm::Method::GetBytecodeHash as u64, Default::default(), TokenAmount::zero(), - RawBytes::serialize(&bytecode_cid).unwrap(), + RawBytes::serialize(&bytecode_hash).unwrap(), ExitCode::OK, ); + // 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_cid.hash().digest()[..32])); + assert_eq!(U256::from_big_endian(&result), U256::from(bytecode_hash.digest())); rt.reset(); - util::invoke_contract_expect_abort( - &mut rt, - &util::dispatch_num_word(1), - evm::interpreter::StatusCode::InvalidArgument( - "Cannot invoke EXTCODEHASH for non-EVM actor.".to_string(), - ), + // 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 accounts 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(); } #[test] @@ -209,6 +231,7 @@ fn test_extcodecopy() { %dispatch(0x00, evm_contract) %dispatch(0x01, native_actor) %dispatch(0x02, invalid_address) +%dispatch(0x03, precompile) %dispatch_end() evm_contract: @@ -247,6 +270,19 @@ invalid_address: 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() @@ -291,4 +327,9 @@ invalid_address: 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)); } diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index a13e75cf1..881e1fc66 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2243,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2152,"get_count":3,"put_bytes":148,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2156,"get_count":3,"put_bytes":152,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2160,"get_count":3,"put_bytes":156,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2164,"get_count":3,"put_bytes":160,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2168,"get_count":3,"put_bytes":164,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2172,"get_count":3,"put_bytes":169,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2177,"get_count":3,"put_bytes":173,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2181,"get_count":3,"put_bytes":347,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2355,"get_count":4,"put_bytes":351,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2359,"get_count":4,"put_bytes":355,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":363,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2371,"get_count":4,"put_bytes":367,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":372,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2380,"get_count":4,"put_bytes":376,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2384,"get_count":4,"put_bytes":342,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":346,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2354,"get_count":4,"put_bytes":350,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2358,"get_count":4,"put_bytes":354,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2362,"get_count":4,"put_bytes":358,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2366,"get_count":4,"put_bytes":362,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2370,"get_count":4,"put_bytes":367,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":371,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2379,"get_count":4,"put_bytes":480,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2488,"get_count":5,"put_bytes":484,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2492,"get_count":5,"put_bytes":488,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2496,"get_count":5,"put_bytes":492,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2500,"get_count":5,"put_bytes":496,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2504,"get_count":5,"put_bytes":500,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2508,"get_count":5,"put_bytes":505,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2513,"get_count":5,"put_bytes":509,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2373,"get_count":4,"put_bytes":406,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2414,"get_count":4,"put_bytes":410,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2418,"get_count":4,"put_bytes":414,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2422,"get_count":4,"put_bytes":418,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2426,"get_count":4,"put_bytes":422,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":426,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2434,"get_count":4,"put_bytes":431,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2439,"get_count":4,"put_bytes":435,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2443,"get_count":4,"put_bytes":544,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2552,"get_count":5,"put_bytes":548,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":552,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2560,"get_count":5,"put_bytes":556,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2564,"get_count":5,"put_bytes":560,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2568,"get_count":5,"put_bytes":564,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2572,"get_count":5,"put_bytes":569,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2577,"get_count":5,"put_bytes":573,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2437,"get_count":4,"put_bytes":470,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":474,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2482,"get_count":4,"put_bytes":478,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2486,"get_count":4,"put_bytes":482,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":486,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2494,"get_count":4,"put_bytes":490,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2498,"get_count":4,"put_bytes":495,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2503,"get_count":4,"put_bytes":499,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2507,"get_count":4,"put_bytes":608,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":620,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":628,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2636,"get_count":5,"put_bytes":633,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2641,"get_count":5,"put_bytes":637,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":534,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2542,"get_count":4,"put_bytes":538,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2546,"get_count":4,"put_bytes":542,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2550,"get_count":4,"put_bytes":546,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2554,"get_count":4,"put_bytes":550,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2558,"get_count":4,"put_bytes":554,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2562,"get_count":4,"put_bytes":559,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2567,"get_count":4,"put_bytes":563,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2571,"get_count":4,"put_bytes":672,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2680,"get_count":5,"put_bytes":676,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2684,"get_count":5,"put_bytes":680,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2688,"get_count":5,"put_bytes":684,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2692,"get_count":5,"put_bytes":688,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2696,"get_count":5,"put_bytes":692,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2700,"get_count":5,"put_bytes":697,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2565,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":343,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2351,"get_count":4,"put_bytes":347,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2355,"get_count":4,"put_bytes":351,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2359,"get_count":4,"put_bytes":355,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":364,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2372,"get_count":4,"put_bytes":368,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2376,"get_count":4,"put_bytes":478,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2486,"get_count":5,"put_bytes":482,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2490,"get_count":5,"put_bytes":486,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2494,"get_count":5,"put_bytes":490,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2498,"get_count":5,"put_bytes":494,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2502,"get_count":5,"put_bytes":498,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2506,"get_count":5,"put_bytes":503,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2511,"get_count":5,"put_bytes":507,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2515,"get_count":5,"put_bytes":471,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2479,"get_count":5,"put_bytes":475,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2483,"get_count":5,"put_bytes":479,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2487,"get_count":5,"put_bytes":483,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2236,"get_count":3,"put_bytes":379,"put_count":3}} -{"i":1,"series":2,"stats":{"get_bytes":2387,"get_count":4,"put_bytes":383,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":2391,"get_count":4,"put_bytes":387,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":2395,"get_count":4,"put_bytes":391,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":2399,"get_count":4,"put_bytes":395,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":2403,"get_count":4,"put_bytes":399,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":2407,"get_count":4,"put_bytes":404,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":2412,"get_count":4,"put_bytes":408,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":2416,"get_count":4,"put_bytes":580,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2588,"get_count":5,"put_bytes":584,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":2592,"get_count":5,"put_bytes":588,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":2596,"get_count":5,"put_bytes":592,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":2600,"get_count":5,"put_bytes":596,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":605,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":573,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":2581,"get_count":5,"put_bytes":577,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":2585,"get_count":5,"put_bytes":581,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":2589,"get_count":5,"put_bytes":585,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":2593,"get_count":5,"put_bytes":589,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":598,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":2606,"get_count":5,"put_bytes":602,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":2610,"get_count":5,"put_bytes":711,"put_count":5}} -{"i":25,"series":2,"stats":{"get_bytes":2719,"get_count":6,"put_bytes":715,"put_count":5}} -{"i":26,"series":2,"stats":{"get_bytes":2723,"get_count":6,"put_bytes":719,"put_count":5}} -{"i":27,"series":2,"stats":{"get_bytes":2727,"get_count":6,"put_bytes":723,"put_count":5}} -{"i":28,"series":2,"stats":{"get_bytes":2731,"get_count":6,"put_bytes":727,"put_count":5}} -{"i":29,"series":2,"stats":{"get_bytes":2735,"get_count":6,"put_bytes":731,"put_count":5}} -{"i":30,"series":2,"stats":{"get_bytes":2739,"get_count":6,"put_bytes":736,"put_count":5}} -{"i":31,"series":2,"stats":{"get_bytes":2744,"get_count":6,"put_bytes":740,"put_count":5}} -{"i":32,"series":2,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":637,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":2645,"get_count":5,"put_bytes":641,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":2649,"get_count":5,"put_bytes":645,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":2653,"get_count":5,"put_bytes":649,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":2657,"get_count":5,"put_bytes":653,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":2661,"get_count":5,"put_bytes":657,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":2665,"get_count":5,"put_bytes":662,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":2670,"get_count":5,"put_bytes":666,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":2674,"get_count":5,"put_bytes":775,"put_count":5}} -{"i":41,"series":2,"stats":{"get_bytes":2783,"get_count":6,"put_bytes":779,"put_count":5}} -{"i":42,"series":2,"stats":{"get_bytes":2787,"get_count":6,"put_bytes":783,"put_count":5}} -{"i":43,"series":2,"stats":{"get_bytes":2791,"get_count":6,"put_bytes":787,"put_count":5}} -{"i":44,"series":2,"stats":{"get_bytes":2795,"get_count":6,"put_bytes":791,"put_count":5}} -{"i":45,"series":2,"stats":{"get_bytes":2799,"get_count":6,"put_bytes":795,"put_count":5}} -{"i":46,"series":2,"stats":{"get_bytes":2803,"get_count":6,"put_bytes":800,"put_count":5}} -{"i":47,"series":2,"stats":{"get_bytes":2808,"get_count":6,"put_bytes":804,"put_count":5}} -{"i":48,"series":2,"stats":{"get_bytes":2668,"get_count":5,"put_bytes":701,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":705,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":2713,"get_count":5,"put_bytes":709,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":2717,"get_count":5,"put_bytes":713,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":717,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":2725,"get_count":5,"put_bytes":721,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":2729,"get_count":5,"put_bytes":726,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":730,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":2738,"get_count":5,"put_bytes":839,"put_count":5}} -{"i":57,"series":2,"stats":{"get_bytes":2847,"get_count":6,"put_bytes":843,"put_count":5}} -{"i":58,"series":2,"stats":{"get_bytes":2851,"get_count":6,"put_bytes":847,"put_count":5}} -{"i":59,"series":2,"stats":{"get_bytes":2855,"get_count":6,"put_bytes":851,"put_count":5}} -{"i":60,"series":2,"stats":{"get_bytes":2859,"get_count":6,"put_bytes":855,"put_count":5}} -{"i":61,"series":2,"stats":{"get_bytes":2863,"get_count":6,"put_bytes":859,"put_count":5}} -{"i":62,"series":2,"stats":{"get_bytes":2867,"get_count":6,"put_bytes":864,"put_count":5}} -{"i":63,"series":2,"stats":{"get_bytes":2872,"get_count":6,"put_bytes":868,"put_count":5}} -{"i":64,"series":2,"stats":{"get_bytes":2732,"get_count":5,"put_bytes":765,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":2773,"get_count":5,"put_bytes":769,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":2777,"get_count":5,"put_bytes":773,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":2781,"get_count":5,"put_bytes":777,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":2785,"get_count":5,"put_bytes":781,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":2789,"get_count":5,"put_bytes":785,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":2793,"get_count":5,"put_bytes":790,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":2798,"get_count":5,"put_bytes":794,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":2802,"get_count":5,"put_bytes":903,"put_count":5}} -{"i":73,"series":2,"stats":{"get_bytes":2911,"get_count":6,"put_bytes":907,"put_count":5}} -{"i":74,"series":2,"stats":{"get_bytes":2915,"get_count":6,"put_bytes":911,"put_count":5}} -{"i":75,"series":2,"stats":{"get_bytes":2919,"get_count":6,"put_bytes":915,"put_count":5}} -{"i":76,"series":2,"stats":{"get_bytes":2923,"get_count":6,"put_bytes":919,"put_count":5}} -{"i":77,"series":2,"stats":{"get_bytes":2927,"get_count":6,"put_bytes":923,"put_count":5}} -{"i":78,"series":2,"stats":{"get_bytes":2931,"get_count":6,"put_bytes":928,"put_count":5}} -{"i":79,"series":2,"stats":{"get_bytes":2936,"get_count":6,"put_bytes":932,"put_count":5}} -{"i":80,"series":2,"stats":{"get_bytes":2796,"get_count":5,"put_bytes":829,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":2837,"get_count":5,"put_bytes":833,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":2841,"get_count":5,"put_bytes":837,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":2845,"get_count":5,"put_bytes":841,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":2849,"get_count":5,"put_bytes":845,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":2853,"get_count":5,"put_bytes":849,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":2857,"get_count":5,"put_bytes":854,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":2862,"get_count":5,"put_bytes":858,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":2866,"get_count":5,"put_bytes":967,"put_count":5}} -{"i":89,"series":2,"stats":{"get_bytes":2975,"get_count":6,"put_bytes":971,"put_count":5}} -{"i":90,"series":2,"stats":{"get_bytes":2979,"get_count":6,"put_bytes":975,"put_count":5}} -{"i":91,"series":2,"stats":{"get_bytes":2983,"get_count":6,"put_bytes":979,"put_count":5}} -{"i":92,"series":2,"stats":{"get_bytes":2987,"get_count":6,"put_bytes":983,"put_count":5}} -{"i":93,"series":2,"stats":{"get_bytes":2991,"get_count":6,"put_bytes":987,"put_count":5}} -{"i":94,"series":2,"stats":{"get_bytes":2995,"get_count":6,"put_bytes":992,"put_count":5}} -{"i":95,"series":2,"stats":{"get_bytes":3000,"get_count":6,"put_bytes":996,"put_count":5}} -{"i":96,"series":2,"stats":{"get_bytes":2860,"get_count":5,"put_bytes":893,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":2901,"get_count":5,"put_bytes":897,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":2905,"get_count":5,"put_bytes":901,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":2909,"get_count":5,"put_bytes":905,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2477,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2269,"get_count":3,"put_bytes":265,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2273,"get_count":3,"put_bytes":269,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2277,"get_count":3,"put_bytes":273,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2281,"get_count":3,"put_bytes":277,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2285,"get_count":3,"put_bytes":281,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2289,"get_count":3,"put_bytes":286,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2294,"get_count":3,"put_bytes":290,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2298,"get_count":3,"put_bytes":464,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":484,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":489,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":493,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":459,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2467,"get_count":4,"put_bytes":463,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2471,"get_count":4,"put_bytes":467,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2475,"get_count":4,"put_bytes":471,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2479,"get_count":4,"put_bytes":475,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2483,"get_count":4,"put_bytes":479,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2487,"get_count":4,"put_bytes":484,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":488,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2496,"get_count":4,"put_bytes":597,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":605,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":613,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2621,"get_count":5,"put_bytes":617,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2625,"get_count":5,"put_bytes":622,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2630,"get_count":5,"put_bytes":626,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":523,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2531,"get_count":4,"put_bytes":527,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2535,"get_count":4,"put_bytes":531,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2539,"get_count":4,"put_bytes":535,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2543,"get_count":4,"put_bytes":539,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2547,"get_count":4,"put_bytes":543,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2551,"get_count":4,"put_bytes":548,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2556,"get_count":4,"put_bytes":552,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2560,"get_count":4,"put_bytes":661,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2669,"get_count":5,"put_bytes":665,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2673,"get_count":5,"put_bytes":669,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2677,"get_count":5,"put_bytes":673,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2681,"get_count":5,"put_bytes":677,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2685,"get_count":5,"put_bytes":681,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2689,"get_count":5,"put_bytes":686,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2694,"get_count":5,"put_bytes":690,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2554,"get_count":4,"put_bytes":587,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":591,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2599,"get_count":4,"put_bytes":595,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2603,"get_count":4,"put_bytes":599,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2607,"get_count":4,"put_bytes":603,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2611,"get_count":4,"put_bytes":607,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2615,"get_count":4,"put_bytes":612,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2620,"get_count":4,"put_bytes":616,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2624,"get_count":4,"put_bytes":725,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2733,"get_count":5,"put_bytes":729,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2737,"get_count":5,"put_bytes":733,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2741,"get_count":5,"put_bytes":737,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2745,"get_count":5,"put_bytes":741,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2749,"get_count":5,"put_bytes":745,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2753,"get_count":5,"put_bytes":750,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2758,"get_count":5,"put_bytes":754,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2618,"get_count":4,"put_bytes":651,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2659,"get_count":4,"put_bytes":655,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2663,"get_count":4,"put_bytes":659,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2667,"get_count":4,"put_bytes":663,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2671,"get_count":4,"put_bytes":667,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2675,"get_count":4,"put_bytes":671,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2679,"get_count":4,"put_bytes":676,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2684,"get_count":4,"put_bytes":680,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2688,"get_count":4,"put_bytes":789,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2797,"get_count":5,"put_bytes":793,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2801,"get_count":5,"put_bytes":797,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2805,"get_count":5,"put_bytes":801,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2809,"get_count":5,"put_bytes":805,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2813,"get_count":5,"put_bytes":809,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2817,"get_count":5,"put_bytes":814,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2822,"get_count":5,"put_bytes":818,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2682,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":460,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2468,"get_count":4,"put_bytes":464,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":481,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2489,"get_count":4,"put_bytes":485,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":595,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":603,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2611,"get_count":5,"put_bytes":607,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2615,"get_count":5,"put_bytes":611,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2619,"get_count":5,"put_bytes":615,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2623,"get_count":5,"put_bytes":620,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":588,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2596,"get_count":5,"put_bytes":592,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2600,"get_count":5,"put_bytes":596,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2353,"get_count":3,"put_bytes":496,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2504,"get_count":4,"put_bytes":500,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2508,"get_count":4,"put_bytes":504,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2512,"get_count":4,"put_bytes":508,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2516,"get_count":4,"put_bytes":512,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2520,"get_count":4,"put_bytes":516,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2524,"get_count":4,"put_bytes":521,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2529,"get_count":4,"put_bytes":525,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2533,"get_count":4,"put_bytes":697,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":705,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2713,"get_count":5,"put_bytes":709,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2717,"get_count":5,"put_bytes":713,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":717,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2725,"get_count":5,"put_bytes":722,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2730,"get_count":5,"put_bytes":726,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":690,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2698,"get_count":5,"put_bytes":694,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2702,"get_count":5,"put_bytes":698,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2706,"get_count":5,"put_bytes":702,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2710,"get_count":5,"put_bytes":706,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2714,"get_count":5,"put_bytes":710,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2718,"get_count":5,"put_bytes":715,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2723,"get_count":5,"put_bytes":719,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2727,"get_count":5,"put_bytes":828,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2836,"get_count":6,"put_bytes":832,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2840,"get_count":6,"put_bytes":836,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2844,"get_count":6,"put_bytes":840,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2848,"get_count":6,"put_bytes":844,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2852,"get_count":6,"put_bytes":848,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2856,"get_count":6,"put_bytes":853,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2861,"get_count":6,"put_bytes":857,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":754,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2762,"get_count":5,"put_bytes":758,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2766,"get_count":5,"put_bytes":762,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2770,"get_count":5,"put_bytes":766,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2774,"get_count":5,"put_bytes":770,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2778,"get_count":5,"put_bytes":774,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2782,"get_count":5,"put_bytes":779,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2787,"get_count":5,"put_bytes":783,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2791,"get_count":5,"put_bytes":892,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2900,"get_count":6,"put_bytes":896,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2904,"get_count":6,"put_bytes":900,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2908,"get_count":6,"put_bytes":904,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2912,"get_count":6,"put_bytes":908,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2916,"get_count":6,"put_bytes":912,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2920,"get_count":6,"put_bytes":917,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2925,"get_count":6,"put_bytes":921,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2785,"get_count":5,"put_bytes":818,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2826,"get_count":5,"put_bytes":822,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2830,"get_count":5,"put_bytes":826,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2834,"get_count":5,"put_bytes":830,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2838,"get_count":5,"put_bytes":834,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2842,"get_count":5,"put_bytes":838,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2846,"get_count":5,"put_bytes":843,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2851,"get_count":5,"put_bytes":847,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2855,"get_count":5,"put_bytes":956,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2964,"get_count":6,"put_bytes":960,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2968,"get_count":6,"put_bytes":964,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2972,"get_count":6,"put_bytes":968,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2976,"get_count":6,"put_bytes":972,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2980,"get_count":6,"put_bytes":976,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2984,"get_count":6,"put_bytes":981,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2989,"get_count":6,"put_bytes":985,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2849,"get_count":5,"put_bytes":882,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2890,"get_count":5,"put_bytes":886,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2894,"get_count":5,"put_bytes":890,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2898,"get_count":5,"put_bytes":894,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2902,"get_count":5,"put_bytes":898,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2906,"get_count":5,"put_bytes":902,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2910,"get_count":5,"put_bytes":907,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2915,"get_count":5,"put_bytes":911,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2919,"get_count":5,"put_bytes":1020,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":3028,"get_count":6,"put_bytes":1024,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":3032,"get_count":6,"put_bytes":1028,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":3036,"get_count":6,"put_bytes":1032,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":3040,"get_count":6,"put_bytes":1036,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":3044,"get_count":6,"put_bytes":1040,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":3048,"get_count":6,"put_bytes":1045,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":3053,"get_count":6,"put_bytes":1049,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2913,"get_count":5,"put_bytes":946,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2954,"get_count":5,"put_bytes":950,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2958,"get_count":5,"put_bytes":954,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2962,"get_count":5,"put_bytes":958,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2966,"get_count":5,"put_bytes":962,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2970,"get_count":5,"put_bytes":966,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2974,"get_count":5,"put_bytes":971,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2979,"get_count":5,"put_bytes":975,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2983,"get_count":5,"put_bytes":1084,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":3092,"get_count":6,"put_bytes":1088,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":3096,"get_count":6,"put_bytes":1092,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":3100,"get_count":6,"put_bytes":1096,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":3104,"get_count":6,"put_bytes":1100,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":3108,"get_count":6,"put_bytes":1104,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":3112,"get_count":6,"put_bytes":1109,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":3117,"get_count":6,"put_bytes":1113,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2977,"get_count":5,"put_bytes":1010,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":3018,"get_count":5,"put_bytes":1014,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":3022,"get_count":5,"put_bytes":1018,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":3026,"get_count":5,"put_bytes":1022,"put_count":4}} diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index a5d6f7af6..187669961 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":3773,"put_count":14}} -{"i":1,"series":1,"stats":{"get_bytes":2491,"get_count":5,"put_bytes":1748,"put_count":10}} -{"i":2,"series":1,"stats":{"get_bytes":2892,"get_count":5,"put_bytes":2255,"put_count":11}} -{"i":3,"series":1,"stats":{"get_bytes":3399,"get_count":6,"put_bytes":2657,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":3657,"get_count":5,"put_bytes":2952,"put_count":10}} -{"i":5,"series":1,"stats":{"get_bytes":4096,"get_count":5,"put_bytes":3353,"put_count":10}} -{"i":6,"series":1,"stats":{"get_bytes":2441,"get_count":4,"put_bytes":1870,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":3014,"get_count":6,"put_bytes":2272,"put_count":11}} -{"i":8,"series":1,"stats":{"get_bytes":3272,"get_count":5,"put_bytes":2566,"put_count":10}} -{"i":9,"series":1,"stats":{"get_bytes":3710,"get_count":5,"put_bytes":2969,"put_count":10}} -{"i":10,"series":1,"stats":{"get_bytes":4113,"get_count":5,"put_bytes":3474,"put_count":11}} -{"i":11,"series":1,"stats":{"get_bytes":4618,"get_count":6,"put_bytes":3943,"put_count":12}} -{"i":12,"series":1,"stats":{"get_bytes":2887,"get_count":5,"put_bytes":2181,"put_count":10}} -{"i":13,"series":1,"stats":{"get_bytes":3325,"get_count":5,"put_bytes":2583,"put_count":10}} -{"i":14,"series":1,"stats":{"get_bytes":3727,"get_count":5,"put_bytes":3090,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":4234,"get_count":6,"put_bytes":3491,"put_count":11}} -{"i":16,"series":1,"stats":{"get_bytes":4491,"get_count":5,"put_bytes":3852,"put_count":11}} -{"i":17,"series":1,"stats":{"get_bytes":2940,"get_count":5,"put_bytes":2198,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":3342,"get_count":5,"put_bytes":2704,"put_count":11}} -{"i":19,"series":1,"stats":{"get_bytes":3848,"get_count":6,"put_bytes":3106,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":4106,"get_count":5,"put_bytes":3400,"put_count":10}} -{"i":21,"series":1,"stats":{"get_bytes":4544,"get_count":5,"put_bytes":3870,"put_count":11}} -{"i":22,"series":1,"stats":{"get_bytes":2958,"get_count":5,"put_bytes":2320,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":3464,"get_count":6,"put_bytes":2722,"put_count":11}} -{"i":24,"series":1,"stats":{"get_bytes":3722,"get_count":5,"put_bytes":3016,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":4160,"get_count":5,"put_bytes":3418,"put_count":10}} -{"i":26,"series":1,"stats":{"get_bytes":4562,"get_count":5,"put_bytes":3991,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":3079,"get_count":6,"put_bytes":2337,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":3337,"get_count":5,"put_bytes":2631,"put_count":10}} -{"i":29,"series":1,"stats":{"get_bytes":3775,"get_count":5,"put_bytes":3032,"put_count":10}} -{"i":30,"series":1,"stats":{"get_bytes":4176,"get_count":5,"put_bytes":3539,"put_count":11}} -{"i":31,"series":1,"stats":{"get_bytes":4683,"get_count":6,"put_bytes":4008,"put_count":12}} -{"i":32,"series":1,"stats":{"get_bytes":2952,"get_count":5,"put_bytes":2246,"put_count":10}} -{"i":33,"series":1,"stats":{"get_bytes":3390,"get_count":5,"put_bytes":2647,"put_count":10}} -{"i":34,"series":1,"stats":{"get_bytes":3791,"get_count":5,"put_bytes":3153,"put_count":11}} -{"i":35,"series":1,"stats":{"get_bytes":4297,"get_count":6,"put_bytes":3556,"put_count":11}} -{"i":36,"series":1,"stats":{"get_bytes":4556,"get_count":5,"put_bytes":3917,"put_count":11}} -{"i":37,"series":1,"stats":{"get_bytes":3005,"get_count":5,"put_bytes":2263,"put_count":10}} -{"i":38,"series":1,"stats":{"get_bytes":3407,"get_count":5,"put_bytes":2768,"put_count":11}} -{"i":39,"series":1,"stats":{"get_bytes":3912,"get_count":6,"put_bytes":3170,"put_count":11}} -{"i":40,"series":1,"stats":{"get_bytes":4170,"get_count":5,"put_bytes":3465,"put_count":10}} -{"i":41,"series":1,"stats":{"get_bytes":4609,"get_count":5,"put_bytes":3934,"put_count":11}} -{"i":42,"series":1,"stats":{"get_bytes":3022,"get_count":5,"put_bytes":2383,"put_count":11}} -{"i":43,"series":1,"stats":{"get_bytes":3527,"get_count":6,"put_bytes":2785,"put_count":11}} -{"i":44,"series":1,"stats":{"get_bytes":3785,"get_count":5,"put_bytes":3079,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":4223,"get_count":5,"put_bytes":3482,"put_count":10}} -{"i":46,"series":1,"stats":{"get_bytes":4626,"get_count":5,"put_bytes":3988,"put_count":11}} -{"i":47,"series":1,"stats":{"get_bytes":3076,"get_count":5,"put_bytes":2400,"put_count":11}} -{"i":48,"series":1,"stats":{"get_bytes":3400,"get_count":5,"put_bytes":2694,"put_count":10}} -{"i":49,"series":1,"stats":{"get_bytes":3838,"get_count":5,"put_bytes":3096,"put_count":10}} -{"i":50,"series":1,"stats":{"get_bytes":4240,"get_count":5,"put_bytes":3603,"put_count":11}} -{"i":51,"series":1,"stats":{"get_bytes":4747,"get_count":6,"put_bytes":4004,"put_count":11}} -{"i":52,"series":1,"stats":{"get_bytes":2948,"get_count":4,"put_bytes":2309,"put_count":10}} -{"i":53,"series":1,"stats":{"get_bytes":3453,"get_count":5,"put_bytes":2711,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":3855,"get_count":5,"put_bytes":3217,"put_count":11}} -{"i":55,"series":1,"stats":{"get_bytes":4361,"get_count":6,"put_bytes":3620,"put_count":11}} -{"i":56,"series":1,"stats":{"get_bytes":4620,"get_count":5,"put_bytes":3913,"put_count":10}} -{"i":57,"series":1,"stats":{"get_bytes":5057,"get_count":5,"put_bytes":4382,"put_count":11}} -{"i":58,"series":1,"stats":{"get_bytes":3470,"get_count":5,"put_bytes":2832,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":3976,"get_count":6,"put_bytes":3234,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":4234,"get_count":5,"put_bytes":3529,"put_count":10}} -{"i":61,"series":1,"stats":{"get_bytes":4673,"get_count":5,"put_bytes":3930,"put_count":10}} -{"i":62,"series":1,"stats":{"get_bytes":5074,"get_count":5,"put_bytes":4572,"put_count":13}} -{"i":63,"series":1,"stats":{"get_bytes":2821,"get_count":6,"put_bytes":2079,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":3079,"get_count":5,"put_bytes":2373,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":3517,"get_count":5,"put_bytes":2775,"put_count":10}} -{"i":66,"series":1,"stats":{"get_bytes":3919,"get_count":5,"put_bytes":3281,"put_count":11}} -{"i":67,"series":1,"stats":{"get_bytes":4425,"get_count":6,"put_bytes":3817,"put_count":13}} -{"i":68,"series":1,"stats":{"get_bytes":2761,"get_count":6,"put_bytes":2055,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":3199,"get_count":6,"put_bytes":2457,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":3601,"get_count":6,"put_bytes":2962,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":4106,"get_count":7,"put_bytes":3365,"put_count":12}} -{"i":72,"series":1,"stats":{"get_bytes":4365,"get_count":6,"put_bytes":3726,"put_count":12}} -{"i":73,"series":1,"stats":{"get_bytes":2814,"get_count":6,"put_bytes":2072,"put_count":11}} -{"i":74,"series":1,"stats":{"get_bytes":3216,"get_count":6,"put_bytes":2577,"put_count":12}} -{"i":75,"series":1,"stats":{"get_bytes":3721,"get_count":7,"put_bytes":2979,"put_count":12}} -{"i":76,"series":1,"stats":{"get_bytes":3979,"get_count":6,"put_bytes":3274,"put_count":11}} -{"i":77,"series":1,"stats":{"get_bytes":4418,"get_count":6,"put_bytes":3743,"put_count":12}} -{"i":78,"series":1,"stats":{"get_bytes":2831,"get_count":6,"put_bytes":2193,"put_count":12}} -{"i":79,"series":1,"stats":{"get_bytes":3337,"get_count":7,"put_bytes":2594,"put_count":12}} -{"i":80,"series":1,"stats":{"get_bytes":3594,"get_count":6,"put_bytes":2888,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":4032,"get_count":6,"put_bytes":3291,"put_count":11}} -{"i":82,"series":1,"stats":{"get_bytes":4435,"get_count":6,"put_bytes":3864,"put_count":13}} -{"i":83,"series":1,"stats":{"get_bytes":2952,"get_count":7,"put_bytes":2209,"put_count":12}} -{"i":84,"series":1,"stats":{"get_bytes":3209,"get_count":6,"put_bytes":2503,"put_count":11}} -{"i":85,"series":1,"stats":{"get_bytes":3647,"get_count":6,"put_bytes":2905,"put_count":11}} -{"i":86,"series":1,"stats":{"get_bytes":4049,"get_count":6,"put_bytes":3412,"put_count":12}} -{"i":87,"series":1,"stats":{"get_bytes":4556,"get_count":7,"put_bytes":3814,"put_count":12}} -{"i":88,"series":1,"stats":{"get_bytes":2902,"get_count":6,"put_bytes":2118,"put_count":11}} -{"i":89,"series":1,"stats":{"get_bytes":3262,"get_count":6,"put_bytes":2520,"put_count":11}} -{"i":90,"series":1,"stats":{"get_bytes":3664,"get_count":6,"put_bytes":3026,"put_count":12}} -{"i":91,"series":1,"stats":{"get_bytes":4170,"get_count":7,"put_bytes":3429,"put_count":12}} -{"i":92,"series":1,"stats":{"get_bytes":4429,"get_count":6,"put_bytes":3722,"put_count":11}} -{"i":93,"series":1,"stats":{"get_bytes":2810,"get_count":5,"put_bytes":2135,"put_count":11}} -{"i":94,"series":1,"stats":{"get_bytes":3279,"get_count":6,"put_bytes":2641,"put_count":12}} -{"i":95,"series":1,"stats":{"get_bytes":3785,"get_count":7,"put_bytes":3043,"put_count":12}} -{"i":96,"series":1,"stats":{"get_bytes":4043,"get_count":6,"put_bytes":3338,"put_count":11}} -{"i":97,"series":1,"stats":{"get_bytes":4482,"get_count":6,"put_bytes":3739,"put_count":11}} -{"i":98,"series":1,"stats":{"get_bytes":4883,"get_count":6,"put_bytes":4312,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":3400,"get_count":7,"put_bytes":2658,"put_count":12}} -{"i":0,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":1770,"put_count":10}} -{"i":1,"series":2,"stats":{"get_bytes":2914,"get_count":5,"put_bytes":2172,"put_count":10}} -{"i":2,"series":2,"stats":{"get_bytes":3316,"get_count":5,"put_bytes":2679,"put_count":11}} -{"i":3,"series":2,"stats":{"get_bytes":3823,"get_count":6,"put_bytes":3081,"put_count":11}} -{"i":4,"series":2,"stats":{"get_bytes":4081,"get_count":5,"put_bytes":1901,"put_count":11}} -{"i":5,"series":2,"stats":{"get_bytes":3045,"get_count":6,"put_bytes":2303,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":3447,"get_count":6,"put_bytes":2809,"put_count":12}} -{"i":7,"series":2,"stats":{"get_bytes":3953,"get_count":7,"put_bytes":3212,"put_count":12}} -{"i":8,"series":2,"stats":{"get_bytes":4212,"get_count":6,"put_bytes":3505,"put_count":11}} -{"i":9,"series":2,"stats":{"get_bytes":4649,"get_count":6,"put_bytes":3974,"put_count":12}} -{"i":10,"series":2,"stats":{"get_bytes":3062,"get_count":6,"put_bytes":2424,"put_count":12}} -{"i":11,"series":2,"stats":{"get_bytes":3568,"get_count":7,"put_bytes":2826,"put_count":12}} -{"i":12,"series":2,"stats":{"get_bytes":3826,"get_count":6,"put_bytes":3121,"put_count":11}} -{"i":13,"series":2,"stats":{"get_bytes":4265,"get_count":6,"put_bytes":3522,"put_count":11}} -{"i":14,"series":2,"stats":{"get_bytes":4666,"get_count":6,"put_bytes":4095,"put_count":13}} -{"i":15,"series":2,"stats":{"get_bytes":3183,"get_count":7,"put_bytes":2441,"put_count":12}} -{"i":16,"series":2,"stats":{"get_bytes":3441,"get_count":6,"put_bytes":2735,"put_count":11}} -{"i":17,"series":2,"stats":{"get_bytes":3879,"get_count":6,"put_bytes":3137,"put_count":11}} -{"i":18,"series":2,"stats":{"get_bytes":4281,"get_count":6,"put_bytes":3643,"put_count":12}} -{"i":19,"series":2,"stats":{"get_bytes":4787,"get_count":7,"put_bytes":4112,"put_count":13}} -{"i":20,"series":2,"stats":{"get_bytes":3056,"get_count":6,"put_bytes":2350,"put_count":11}} -{"i":21,"series":2,"stats":{"get_bytes":3494,"get_count":6,"put_bytes":2752,"put_count":11}} -{"i":22,"series":2,"stats":{"get_bytes":3896,"get_count":6,"put_bytes":3257,"put_count":12}} -{"i":23,"series":2,"stats":{"get_bytes":4401,"get_count":7,"put_bytes":3660,"put_count":12}} -{"i":24,"series":2,"stats":{"get_bytes":4660,"get_count":6,"put_bytes":4022,"put_count":12}} -{"i":25,"series":2,"stats":{"get_bytes":3110,"get_count":6,"put_bytes":2368,"put_count":11}} -{"i":26,"series":2,"stats":{"get_bytes":3512,"get_count":6,"put_bytes":2873,"put_count":12}} -{"i":27,"series":2,"stats":{"get_bytes":4017,"get_count":7,"put_bytes":3275,"put_count":12}} -{"i":28,"series":2,"stats":{"get_bytes":4275,"get_count":6,"put_bytes":3570,"put_count":11}} -{"i":29,"series":2,"stats":{"get_bytes":4714,"get_count":6,"put_bytes":4039,"put_count":12}} -{"i":30,"series":2,"stats":{"get_bytes":3127,"get_count":6,"put_bytes":2489,"put_count":12}} -{"i":31,"series":2,"stats":{"get_bytes":3633,"get_count":7,"put_bytes":2890,"put_count":12}} -{"i":32,"series":2,"stats":{"get_bytes":3890,"get_count":6,"put_bytes":3184,"put_count":11}} -{"i":33,"series":2,"stats":{"get_bytes":4328,"get_count":6,"put_bytes":3587,"put_count":11}} -{"i":34,"series":2,"stats":{"get_bytes":4731,"get_count":6,"put_bytes":4160,"put_count":13}} -{"i":35,"series":2,"stats":{"get_bytes":3248,"get_count":7,"put_bytes":2505,"put_count":12}} -{"i":36,"series":2,"stats":{"get_bytes":3505,"get_count":6,"put_bytes":2799,"put_count":11}} -{"i":37,"series":2,"stats":{"get_bytes":3943,"get_count":6,"put_bytes":3201,"put_count":11}} -{"i":38,"series":2,"stats":{"get_bytes":4345,"get_count":6,"put_bytes":3708,"put_count":12}} -{"i":39,"series":2,"stats":{"get_bytes":4852,"get_count":7,"put_bytes":4110,"put_count":12}} -{"i":40,"series":2,"stats":{"get_bytes":3198,"get_count":6,"put_bytes":2414,"put_count":11}} -{"i":41,"series":2,"stats":{"get_bytes":3558,"get_count":6,"put_bytes":2816,"put_count":11}} -{"i":42,"series":2,"stats":{"get_bytes":3960,"get_count":6,"put_bytes":3322,"put_count":12}} -{"i":43,"series":2,"stats":{"get_bytes":4466,"get_count":7,"put_bytes":3725,"put_count":12}} -{"i":44,"series":2,"stats":{"get_bytes":4725,"get_count":6,"put_bytes":4018,"put_count":11}} -{"i":45,"series":2,"stats":{"get_bytes":3106,"get_count":5,"put_bytes":2431,"put_count":11}} -{"i":46,"series":2,"stats":{"get_bytes":3575,"get_count":6,"put_bytes":2937,"put_count":12}} -{"i":47,"series":2,"stats":{"get_bytes":4081,"get_count":7,"put_bytes":3339,"put_count":12}} -{"i":48,"series":2,"stats":{"get_bytes":4339,"get_count":6,"put_bytes":3634,"put_count":11}} -{"i":49,"series":2,"stats":{"get_bytes":4778,"get_count":6,"put_bytes":4035,"put_count":11}} -{"i":50,"series":2,"stats":{"get_bytes":5179,"get_count":6,"put_bytes":4608,"put_count":13}} -{"i":51,"series":2,"stats":{"get_bytes":3696,"get_count":7,"put_bytes":2954,"put_count":12}} -{"i":52,"series":2,"stats":{"get_bytes":3954,"get_count":6,"put_bytes":3248,"put_count":11}} -{"i":53,"series":2,"stats":{"get_bytes":4392,"get_count":6,"put_bytes":3651,"put_count":11}} -{"i":54,"series":2,"stats":{"get_bytes":4795,"get_count":6,"put_bytes":4156,"put_count":12}} -{"i":55,"series":2,"stats":{"get_bytes":5300,"get_count":7,"put_bytes":4625,"put_count":13}} -{"i":56,"series":2,"stats":{"get_bytes":3569,"get_count":6,"put_bytes":2863,"put_count":11}} -{"i":57,"series":2,"stats":{"get_bytes":4007,"get_count":6,"put_bytes":3265,"put_count":11}} -{"i":58,"series":2,"stats":{"get_bytes":4409,"get_count":6,"put_bytes":3771,"put_count":12}} -{"i":59,"series":2,"stats":{"get_bytes":4915,"get_count":7,"put_bytes":4173,"put_count":12}} -{"i":60,"series":2,"stats":{"get_bytes":5173,"get_count":6,"put_bytes":4534,"put_count":12}} -{"i":61,"series":2,"stats":{"get_bytes":3622,"get_count":6,"put_bytes":2880,"put_count":11}} -{"i":62,"series":2,"stats":{"get_bytes":4024,"get_count":6,"put_bytes":3386,"put_count":12}} -{"i":63,"series":2,"stats":{"get_bytes":4530,"get_count":7,"put_bytes":3787,"put_count":12}} -{"i":64,"series":2,"stats":{"get_bytes":4787,"get_count":6,"put_bytes":4082,"put_count":11}} -{"i":65,"series":2,"stats":{"get_bytes":5226,"get_count":6,"put_bytes":4552,"put_count":12}} -{"i":66,"series":2,"stats":{"get_bytes":3640,"get_count":6,"put_bytes":3002,"put_count":12}} -{"i":67,"series":2,"stats":{"get_bytes":4146,"get_count":7,"put_bytes":3403,"put_count":12}} -{"i":68,"series":2,"stats":{"get_bytes":4403,"get_count":6,"put_bytes":3697,"put_count":11}} -{"i":69,"series":2,"stats":{"get_bytes":4841,"get_count":6,"put_bytes":4100,"put_count":11}} -{"i":70,"series":2,"stats":{"get_bytes":5244,"get_count":6,"put_bytes":4673,"put_count":13}} -{"i":71,"series":2,"stats":{"get_bytes":3761,"get_count":7,"put_bytes":3019,"put_count":12}} -{"i":72,"series":2,"stats":{"get_bytes":4019,"get_count":6,"put_bytes":3312,"put_count":11}} -{"i":73,"series":2,"stats":{"get_bytes":4456,"get_count":6,"put_bytes":3714,"put_count":11}} -{"i":74,"series":2,"stats":{"get_bytes":4858,"get_count":6,"put_bytes":4221,"put_count":12}} -{"i":75,"series":2,"stats":{"get_bytes":5365,"get_count":7,"put_bytes":4690,"put_count":13}} -{"i":76,"series":2,"stats":{"get_bytes":3634,"get_count":6,"put_bytes":2928,"put_count":11}} -{"i":77,"series":2,"stats":{"get_bytes":4072,"get_count":6,"put_bytes":3329,"put_count":11}} -{"i":78,"series":2,"stats":{"get_bytes":4473,"get_count":6,"put_bytes":3835,"put_count":12}} -{"i":79,"series":2,"stats":{"get_bytes":4979,"get_count":7,"put_bytes":4238,"put_count":12}} -{"i":80,"series":2,"stats":{"get_bytes":5238,"get_count":6,"put_bytes":4599,"put_count":12}} -{"i":81,"series":2,"stats":{"get_bytes":3687,"get_count":6,"put_bytes":2944,"put_count":11}} -{"i":82,"series":2,"stats":{"get_bytes":4088,"get_count":6,"put_bytes":3450,"put_count":12}} -{"i":83,"series":2,"stats":{"get_bytes":4594,"get_count":7,"put_bytes":3852,"put_count":12}} -{"i":84,"series":2,"stats":{"get_bytes":4852,"get_count":6,"put_bytes":4147,"put_count":11}} -{"i":85,"series":2,"stats":{"get_bytes":5291,"get_count":6,"put_bytes":4548,"put_count":11}} -{"i":86,"series":2,"stats":{"get_bytes":3636,"get_count":5,"put_bytes":3065,"put_count":12}} -{"i":87,"series":2,"stats":{"get_bytes":4209,"get_count":7,"put_bytes":3467,"put_count":12}} -{"i":88,"series":2,"stats":{"get_bytes":4467,"get_count":6,"put_bytes":3761,"put_count":11}} -{"i":89,"series":2,"stats":{"get_bytes":4905,"get_count":6,"put_bytes":4164,"put_count":11}} -{"i":90,"series":2,"stats":{"get_bytes":5308,"get_count":6,"put_bytes":4669,"put_count":12}} -{"i":91,"series":2,"stats":{"get_bytes":5813,"get_count":7,"put_bytes":5138,"put_count":13}} -{"i":92,"series":2,"stats":{"get_bytes":4082,"get_count":6,"put_bytes":3376,"put_count":11}} -{"i":93,"series":2,"stats":{"get_bytes":4520,"get_count":6,"put_bytes":3778,"put_count":11}} -{"i":94,"series":2,"stats":{"get_bytes":4922,"get_count":6,"put_bytes":4285,"put_count":12}} -{"i":95,"series":2,"stats":{"get_bytes":5429,"get_count":7,"put_bytes":4686,"put_count":12}} -{"i":96,"series":2,"stats":{"get_bytes":5686,"get_count":6,"put_bytes":5047,"put_count":12}} -{"i":97,"series":2,"stats":{"get_bytes":4135,"get_count":6,"put_bytes":3393,"put_count":11}} -{"i":98,"series":2,"stats":{"get_bytes":4537,"get_count":6,"put_bytes":3899,"put_count":12}} -{"i":99,"series":2,"stats":{"get_bytes":5043,"get_count":7,"put_bytes":4301,"put_count":12}} +{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":4007,"put_count":14}} +{"i":1,"series":1,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":1865,"put_count":10}} +{"i":2,"series":1,"stats":{"get_bytes":3009,"get_count":5,"put_bytes":2372,"put_count":11}} +{"i":3,"series":1,"stats":{"get_bytes":3516,"get_count":6,"put_bytes":2774,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":3774,"get_count":5,"put_bytes":3069,"put_count":10}} +{"i":5,"series":1,"stats":{"get_bytes":4213,"get_count":5,"put_bytes":3470,"put_count":10}} +{"i":6,"series":1,"stats":{"get_bytes":2558,"get_count":4,"put_bytes":1987,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":3131,"get_count":6,"put_bytes":2389,"put_count":11}} +{"i":8,"series":1,"stats":{"get_bytes":3389,"get_count":5,"put_bytes":2683,"put_count":10}} +{"i":9,"series":1,"stats":{"get_bytes":3827,"get_count":5,"put_bytes":3086,"put_count":10}} +{"i":10,"series":1,"stats":{"get_bytes":4230,"get_count":5,"put_bytes":3591,"put_count":11}} +{"i":11,"series":1,"stats":{"get_bytes":4735,"get_count":6,"put_bytes":4060,"put_count":12}} +{"i":12,"series":1,"stats":{"get_bytes":3004,"get_count":5,"put_bytes":2298,"put_count":10}} +{"i":13,"series":1,"stats":{"get_bytes":3442,"get_count":5,"put_bytes":2700,"put_count":10}} +{"i":14,"series":1,"stats":{"get_bytes":3844,"get_count":5,"put_bytes":3207,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":4351,"get_count":6,"put_bytes":3608,"put_count":11}} +{"i":16,"series":1,"stats":{"get_bytes":4608,"get_count":5,"put_bytes":3969,"put_count":11}} +{"i":17,"series":1,"stats":{"get_bytes":3057,"get_count":5,"put_bytes":2315,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":3459,"get_count":5,"put_bytes":2821,"put_count":11}} +{"i":19,"series":1,"stats":{"get_bytes":3965,"get_count":6,"put_bytes":3223,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":4223,"get_count":5,"put_bytes":3517,"put_count":10}} +{"i":21,"series":1,"stats":{"get_bytes":4661,"get_count":5,"put_bytes":3987,"put_count":11}} +{"i":22,"series":1,"stats":{"get_bytes":3075,"get_count":5,"put_bytes":2437,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":3581,"get_count":6,"put_bytes":2839,"put_count":11}} +{"i":24,"series":1,"stats":{"get_bytes":3839,"get_count":5,"put_bytes":3133,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":4277,"get_count":5,"put_bytes":3535,"put_count":10}} +{"i":26,"series":1,"stats":{"get_bytes":4679,"get_count":5,"put_bytes":4108,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":3196,"get_count":6,"put_bytes":2454,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":3454,"get_count":5,"put_bytes":2748,"put_count":10}} +{"i":29,"series":1,"stats":{"get_bytes":3892,"get_count":5,"put_bytes":3149,"put_count":10}} +{"i":30,"series":1,"stats":{"get_bytes":4293,"get_count":5,"put_bytes":3656,"put_count":11}} +{"i":31,"series":1,"stats":{"get_bytes":4800,"get_count":6,"put_bytes":4125,"put_count":12}} +{"i":32,"series":1,"stats":{"get_bytes":3069,"get_count":5,"put_bytes":2363,"put_count":10}} +{"i":33,"series":1,"stats":{"get_bytes":3507,"get_count":5,"put_bytes":2764,"put_count":10}} +{"i":34,"series":1,"stats":{"get_bytes":3908,"get_count":5,"put_bytes":3270,"put_count":11}} +{"i":35,"series":1,"stats":{"get_bytes":4414,"get_count":6,"put_bytes":3673,"put_count":11}} +{"i":36,"series":1,"stats":{"get_bytes":4673,"get_count":5,"put_bytes":4034,"put_count":11}} +{"i":37,"series":1,"stats":{"get_bytes":3122,"get_count":5,"put_bytes":2380,"put_count":10}} +{"i":38,"series":1,"stats":{"get_bytes":3524,"get_count":5,"put_bytes":2885,"put_count":11}} +{"i":39,"series":1,"stats":{"get_bytes":4029,"get_count":6,"put_bytes":3287,"put_count":11}} +{"i":40,"series":1,"stats":{"get_bytes":4287,"get_count":5,"put_bytes":3582,"put_count":10}} +{"i":41,"series":1,"stats":{"get_bytes":4726,"get_count":5,"put_bytes":4051,"put_count":11}} +{"i":42,"series":1,"stats":{"get_bytes":3139,"get_count":5,"put_bytes":2500,"put_count":11}} +{"i":43,"series":1,"stats":{"get_bytes":3644,"get_count":6,"put_bytes":2902,"put_count":11}} +{"i":44,"series":1,"stats":{"get_bytes":3902,"get_count":5,"put_bytes":3196,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":4340,"get_count":5,"put_bytes":3599,"put_count":10}} +{"i":46,"series":1,"stats":{"get_bytes":4743,"get_count":5,"put_bytes":4105,"put_count":11}} +{"i":47,"series":1,"stats":{"get_bytes":3193,"get_count":5,"put_bytes":2517,"put_count":11}} +{"i":48,"series":1,"stats":{"get_bytes":3517,"get_count":5,"put_bytes":2811,"put_count":10}} +{"i":49,"series":1,"stats":{"get_bytes":3955,"get_count":5,"put_bytes":3213,"put_count":10}} +{"i":50,"series":1,"stats":{"get_bytes":4357,"get_count":5,"put_bytes":3720,"put_count":11}} +{"i":51,"series":1,"stats":{"get_bytes":4864,"get_count":6,"put_bytes":4121,"put_count":11}} +{"i":52,"series":1,"stats":{"get_bytes":3065,"get_count":4,"put_bytes":2426,"put_count":10}} +{"i":53,"series":1,"stats":{"get_bytes":3570,"get_count":5,"put_bytes":2828,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":3972,"get_count":5,"put_bytes":3334,"put_count":11}} +{"i":55,"series":1,"stats":{"get_bytes":4478,"get_count":6,"put_bytes":3737,"put_count":11}} +{"i":56,"series":1,"stats":{"get_bytes":4737,"get_count":5,"put_bytes":4030,"put_count":10}} +{"i":57,"series":1,"stats":{"get_bytes":5174,"get_count":5,"put_bytes":4499,"put_count":11}} +{"i":58,"series":1,"stats":{"get_bytes":3587,"get_count":5,"put_bytes":2949,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":4093,"get_count":6,"put_bytes":3351,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":4351,"get_count":5,"put_bytes":3646,"put_count":10}} +{"i":61,"series":1,"stats":{"get_bytes":4790,"get_count":5,"put_bytes":4047,"put_count":10}} +{"i":62,"series":1,"stats":{"get_bytes":5191,"get_count":5,"put_bytes":4689,"put_count":13}} +{"i":63,"series":1,"stats":{"get_bytes":2938,"get_count":6,"put_bytes":2196,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":3196,"get_count":5,"put_bytes":2490,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":3634,"get_count":5,"put_bytes":2892,"put_count":10}} +{"i":66,"series":1,"stats":{"get_bytes":4036,"get_count":5,"put_bytes":3398,"put_count":11}} +{"i":67,"series":1,"stats":{"get_bytes":4542,"get_count":6,"put_bytes":3934,"put_count":13}} +{"i":68,"series":1,"stats":{"get_bytes":2878,"get_count":6,"put_bytes":2172,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":3316,"get_count":6,"put_bytes":2574,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":3718,"get_count":6,"put_bytes":3079,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":4223,"get_count":7,"put_bytes":3482,"put_count":12}} +{"i":72,"series":1,"stats":{"get_bytes":4482,"get_count":6,"put_bytes":3843,"put_count":12}} +{"i":73,"series":1,"stats":{"get_bytes":2931,"get_count":6,"put_bytes":2189,"put_count":11}} +{"i":74,"series":1,"stats":{"get_bytes":3333,"get_count":6,"put_bytes":2694,"put_count":12}} +{"i":75,"series":1,"stats":{"get_bytes":3838,"get_count":7,"put_bytes":3096,"put_count":12}} +{"i":76,"series":1,"stats":{"get_bytes":4096,"get_count":6,"put_bytes":3391,"put_count":11}} +{"i":77,"series":1,"stats":{"get_bytes":4535,"get_count":6,"put_bytes":3860,"put_count":12}} +{"i":78,"series":1,"stats":{"get_bytes":2948,"get_count":6,"put_bytes":2310,"put_count":12}} +{"i":79,"series":1,"stats":{"get_bytes":3454,"get_count":7,"put_bytes":2711,"put_count":12}} +{"i":80,"series":1,"stats":{"get_bytes":3711,"get_count":6,"put_bytes":3005,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":4149,"get_count":6,"put_bytes":3408,"put_count":11}} +{"i":82,"series":1,"stats":{"get_bytes":4552,"get_count":6,"put_bytes":3981,"put_count":13}} +{"i":83,"series":1,"stats":{"get_bytes":3069,"get_count":7,"put_bytes":2326,"put_count":12}} +{"i":84,"series":1,"stats":{"get_bytes":3326,"get_count":6,"put_bytes":2620,"put_count":11}} +{"i":85,"series":1,"stats":{"get_bytes":3764,"get_count":6,"put_bytes":3022,"put_count":11}} +{"i":86,"series":1,"stats":{"get_bytes":4166,"get_count":6,"put_bytes":3529,"put_count":12}} +{"i":87,"series":1,"stats":{"get_bytes":4673,"get_count":7,"put_bytes":3931,"put_count":12}} +{"i":88,"series":1,"stats":{"get_bytes":3019,"get_count":6,"put_bytes":2235,"put_count":11}} +{"i":89,"series":1,"stats":{"get_bytes":3379,"get_count":6,"put_bytes":2637,"put_count":11}} +{"i":90,"series":1,"stats":{"get_bytes":3781,"get_count":6,"put_bytes":3143,"put_count":12}} +{"i":91,"series":1,"stats":{"get_bytes":4287,"get_count":7,"put_bytes":3546,"put_count":12}} +{"i":92,"series":1,"stats":{"get_bytes":4546,"get_count":6,"put_bytes":3839,"put_count":11}} +{"i":93,"series":1,"stats":{"get_bytes":2927,"get_count":5,"put_bytes":2252,"put_count":11}} +{"i":94,"series":1,"stats":{"get_bytes":3396,"get_count":6,"put_bytes":2758,"put_count":12}} +{"i":95,"series":1,"stats":{"get_bytes":3902,"get_count":7,"put_bytes":3160,"put_count":12}} +{"i":96,"series":1,"stats":{"get_bytes":4160,"get_count":6,"put_bytes":3455,"put_count":11}} +{"i":97,"series":1,"stats":{"get_bytes":4599,"get_count":6,"put_bytes":3856,"put_count":11}} +{"i":98,"series":1,"stats":{"get_bytes":5000,"get_count":6,"put_bytes":4429,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":3517,"get_count":7,"put_bytes":2775,"put_count":12}} +{"i":0,"series":2,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":1887,"put_count":10}} +{"i":1,"series":2,"stats":{"get_bytes":3031,"get_count":5,"put_bytes":2289,"put_count":10}} +{"i":2,"series":2,"stats":{"get_bytes":3433,"get_count":5,"put_bytes":2796,"put_count":11}} +{"i":3,"series":2,"stats":{"get_bytes":3940,"get_count":6,"put_bytes":3198,"put_count":11}} +{"i":4,"series":2,"stats":{"get_bytes":4198,"get_count":5,"put_bytes":2018,"put_count":11}} +{"i":5,"series":2,"stats":{"get_bytes":3162,"get_count":6,"put_bytes":2420,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":3564,"get_count":6,"put_bytes":2926,"put_count":12}} +{"i":7,"series":2,"stats":{"get_bytes":4070,"get_count":7,"put_bytes":3329,"put_count":12}} +{"i":8,"series":2,"stats":{"get_bytes":4329,"get_count":6,"put_bytes":3622,"put_count":11}} +{"i":9,"series":2,"stats":{"get_bytes":4766,"get_count":6,"put_bytes":4091,"put_count":12}} +{"i":10,"series":2,"stats":{"get_bytes":3179,"get_count":6,"put_bytes":2541,"put_count":12}} +{"i":11,"series":2,"stats":{"get_bytes":3685,"get_count":7,"put_bytes":2943,"put_count":12}} +{"i":12,"series":2,"stats":{"get_bytes":3943,"get_count":6,"put_bytes":3238,"put_count":11}} +{"i":13,"series":2,"stats":{"get_bytes":4382,"get_count":6,"put_bytes":3639,"put_count":11}} +{"i":14,"series":2,"stats":{"get_bytes":4783,"get_count":6,"put_bytes":4212,"put_count":13}} +{"i":15,"series":2,"stats":{"get_bytes":3300,"get_count":7,"put_bytes":2558,"put_count":12}} +{"i":16,"series":2,"stats":{"get_bytes":3558,"get_count":6,"put_bytes":2852,"put_count":11}} +{"i":17,"series":2,"stats":{"get_bytes":3996,"get_count":6,"put_bytes":3254,"put_count":11}} +{"i":18,"series":2,"stats":{"get_bytes":4398,"get_count":6,"put_bytes":3760,"put_count":12}} +{"i":19,"series":2,"stats":{"get_bytes":4904,"get_count":7,"put_bytes":4229,"put_count":13}} +{"i":20,"series":2,"stats":{"get_bytes":3173,"get_count":6,"put_bytes":2467,"put_count":11}} +{"i":21,"series":2,"stats":{"get_bytes":3611,"get_count":6,"put_bytes":2869,"put_count":11}} +{"i":22,"series":2,"stats":{"get_bytes":4013,"get_count":6,"put_bytes":3374,"put_count":12}} +{"i":23,"series":2,"stats":{"get_bytes":4518,"get_count":7,"put_bytes":3777,"put_count":12}} +{"i":24,"series":2,"stats":{"get_bytes":4777,"get_count":6,"put_bytes":4139,"put_count":12}} +{"i":25,"series":2,"stats":{"get_bytes":3227,"get_count":6,"put_bytes":2485,"put_count":11}} +{"i":26,"series":2,"stats":{"get_bytes":3629,"get_count":6,"put_bytes":2990,"put_count":12}} +{"i":27,"series":2,"stats":{"get_bytes":4134,"get_count":7,"put_bytes":3392,"put_count":12}} +{"i":28,"series":2,"stats":{"get_bytes":4392,"get_count":6,"put_bytes":3687,"put_count":11}} +{"i":29,"series":2,"stats":{"get_bytes":4831,"get_count":6,"put_bytes":4156,"put_count":12}} +{"i":30,"series":2,"stats":{"get_bytes":3244,"get_count":6,"put_bytes":2606,"put_count":12}} +{"i":31,"series":2,"stats":{"get_bytes":3750,"get_count":7,"put_bytes":3007,"put_count":12}} +{"i":32,"series":2,"stats":{"get_bytes":4007,"get_count":6,"put_bytes":3301,"put_count":11}} +{"i":33,"series":2,"stats":{"get_bytes":4445,"get_count":6,"put_bytes":3704,"put_count":11}} +{"i":34,"series":2,"stats":{"get_bytes":4848,"get_count":6,"put_bytes":4277,"put_count":13}} +{"i":35,"series":2,"stats":{"get_bytes":3365,"get_count":7,"put_bytes":2622,"put_count":12}} +{"i":36,"series":2,"stats":{"get_bytes":3622,"get_count":6,"put_bytes":2916,"put_count":11}} +{"i":37,"series":2,"stats":{"get_bytes":4060,"get_count":6,"put_bytes":3318,"put_count":11}} +{"i":38,"series":2,"stats":{"get_bytes":4462,"get_count":6,"put_bytes":3825,"put_count":12}} +{"i":39,"series":2,"stats":{"get_bytes":4969,"get_count":7,"put_bytes":4227,"put_count":12}} +{"i":40,"series":2,"stats":{"get_bytes":3315,"get_count":6,"put_bytes":2531,"put_count":11}} +{"i":41,"series":2,"stats":{"get_bytes":3675,"get_count":6,"put_bytes":2933,"put_count":11}} +{"i":42,"series":2,"stats":{"get_bytes":4077,"get_count":6,"put_bytes":3439,"put_count":12}} +{"i":43,"series":2,"stats":{"get_bytes":4583,"get_count":7,"put_bytes":3842,"put_count":12}} +{"i":44,"series":2,"stats":{"get_bytes":4842,"get_count":6,"put_bytes":4135,"put_count":11}} +{"i":45,"series":2,"stats":{"get_bytes":3223,"get_count":5,"put_bytes":2548,"put_count":11}} +{"i":46,"series":2,"stats":{"get_bytes":3692,"get_count":6,"put_bytes":3054,"put_count":12}} +{"i":47,"series":2,"stats":{"get_bytes":4198,"get_count":7,"put_bytes":3456,"put_count":12}} +{"i":48,"series":2,"stats":{"get_bytes":4456,"get_count":6,"put_bytes":3751,"put_count":11}} +{"i":49,"series":2,"stats":{"get_bytes":4895,"get_count":6,"put_bytes":4152,"put_count":11}} +{"i":50,"series":2,"stats":{"get_bytes":5296,"get_count":6,"put_bytes":4725,"put_count":13}} +{"i":51,"series":2,"stats":{"get_bytes":3813,"get_count":7,"put_bytes":3071,"put_count":12}} +{"i":52,"series":2,"stats":{"get_bytes":4071,"get_count":6,"put_bytes":3365,"put_count":11}} +{"i":53,"series":2,"stats":{"get_bytes":4509,"get_count":6,"put_bytes":3768,"put_count":11}} +{"i":54,"series":2,"stats":{"get_bytes":4912,"get_count":6,"put_bytes":4273,"put_count":12}} +{"i":55,"series":2,"stats":{"get_bytes":5417,"get_count":7,"put_bytes":4742,"put_count":13}} +{"i":56,"series":2,"stats":{"get_bytes":3686,"get_count":6,"put_bytes":2980,"put_count":11}} +{"i":57,"series":2,"stats":{"get_bytes":4124,"get_count":6,"put_bytes":3382,"put_count":11}} +{"i":58,"series":2,"stats":{"get_bytes":4526,"get_count":6,"put_bytes":3888,"put_count":12}} +{"i":59,"series":2,"stats":{"get_bytes":5032,"get_count":7,"put_bytes":4290,"put_count":12}} +{"i":60,"series":2,"stats":{"get_bytes":5290,"get_count":6,"put_bytes":4651,"put_count":12}} +{"i":61,"series":2,"stats":{"get_bytes":3739,"get_count":6,"put_bytes":2997,"put_count":11}} +{"i":62,"series":2,"stats":{"get_bytes":4141,"get_count":6,"put_bytes":3503,"put_count":12}} +{"i":63,"series":2,"stats":{"get_bytes":4647,"get_count":7,"put_bytes":3904,"put_count":12}} +{"i":64,"series":2,"stats":{"get_bytes":4904,"get_count":6,"put_bytes":4199,"put_count":11}} +{"i":65,"series":2,"stats":{"get_bytes":5343,"get_count":6,"put_bytes":4669,"put_count":12}} +{"i":66,"series":2,"stats":{"get_bytes":3757,"get_count":6,"put_bytes":3119,"put_count":12}} +{"i":67,"series":2,"stats":{"get_bytes":4263,"get_count":7,"put_bytes":3520,"put_count":12}} +{"i":68,"series":2,"stats":{"get_bytes":4520,"get_count":6,"put_bytes":3814,"put_count":11}} +{"i":69,"series":2,"stats":{"get_bytes":4958,"get_count":6,"put_bytes":4217,"put_count":11}} +{"i":70,"series":2,"stats":{"get_bytes":5361,"get_count":6,"put_bytes":4790,"put_count":13}} +{"i":71,"series":2,"stats":{"get_bytes":3878,"get_count":7,"put_bytes":3136,"put_count":12}} +{"i":72,"series":2,"stats":{"get_bytes":4136,"get_count":6,"put_bytes":3429,"put_count":11}} +{"i":73,"series":2,"stats":{"get_bytes":4573,"get_count":6,"put_bytes":3831,"put_count":11}} +{"i":74,"series":2,"stats":{"get_bytes":4975,"get_count":6,"put_bytes":4338,"put_count":12}} +{"i":75,"series":2,"stats":{"get_bytes":5482,"get_count":7,"put_bytes":4807,"put_count":13}} +{"i":76,"series":2,"stats":{"get_bytes":3751,"get_count":6,"put_bytes":3045,"put_count":11}} +{"i":77,"series":2,"stats":{"get_bytes":4189,"get_count":6,"put_bytes":3446,"put_count":11}} +{"i":78,"series":2,"stats":{"get_bytes":4590,"get_count":6,"put_bytes":3952,"put_count":12}} +{"i":79,"series":2,"stats":{"get_bytes":5096,"get_count":7,"put_bytes":4355,"put_count":12}} +{"i":80,"series":2,"stats":{"get_bytes":5355,"get_count":6,"put_bytes":4716,"put_count":12}} +{"i":81,"series":2,"stats":{"get_bytes":3804,"get_count":6,"put_bytes":3061,"put_count":11}} +{"i":82,"series":2,"stats":{"get_bytes":4205,"get_count":6,"put_bytes":3567,"put_count":12}} +{"i":83,"series":2,"stats":{"get_bytes":4711,"get_count":7,"put_bytes":3969,"put_count":12}} +{"i":84,"series":2,"stats":{"get_bytes":4969,"get_count":6,"put_bytes":4264,"put_count":11}} +{"i":85,"series":2,"stats":{"get_bytes":5408,"get_count":6,"put_bytes":4665,"put_count":11}} +{"i":86,"series":2,"stats":{"get_bytes":3753,"get_count":5,"put_bytes":3182,"put_count":12}} +{"i":87,"series":2,"stats":{"get_bytes":4326,"get_count":7,"put_bytes":3584,"put_count":12}} +{"i":88,"series":2,"stats":{"get_bytes":4584,"get_count":6,"put_bytes":3878,"put_count":11}} +{"i":89,"series":2,"stats":{"get_bytes":5022,"get_count":6,"put_bytes":4281,"put_count":11}} +{"i":90,"series":2,"stats":{"get_bytes":5425,"get_count":6,"put_bytes":4786,"put_count":12}} +{"i":91,"series":2,"stats":{"get_bytes":5930,"get_count":7,"put_bytes":5255,"put_count":13}} +{"i":92,"series":2,"stats":{"get_bytes":4199,"get_count":6,"put_bytes":3493,"put_count":11}} +{"i":93,"series":2,"stats":{"get_bytes":4637,"get_count":6,"put_bytes":3895,"put_count":11}} +{"i":94,"series":2,"stats":{"get_bytes":5039,"get_count":6,"put_bytes":4402,"put_count":12}} +{"i":95,"series":2,"stats":{"get_bytes":5546,"get_count":7,"put_bytes":4803,"put_count":12}} +{"i":96,"series":2,"stats":{"get_bytes":5803,"get_count":6,"put_bytes":5164,"put_count":12}} +{"i":97,"series":2,"stats":{"get_bytes":4252,"get_count":6,"put_bytes":3510,"put_count":11}} +{"i":98,"series":2,"stats":{"get_bytes":4654,"get_count":6,"put_bytes":4016,"put_count":12}} +{"i":99,"series":2,"stats":{"get_bytes":5160,"get_count":7,"put_bytes":4418,"put_count":12}} diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index cdbc266d3..8427f0837 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":3679,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":5408,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index bb0d53250..526e9193d 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6599,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6272,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6279,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":8342,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6286,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":8858,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":8019,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5963,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6736,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":4680,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6716,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6389,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6396,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":8975,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6853,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4797,"get_count":13,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index 3b75a8bc2..905039ee1 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2240,"get_count":3,"put_bytes":342,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":2,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":3,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":4,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":5,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":6,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":8,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":338,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":30,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":31,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":32,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":45,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":57,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":58,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":61,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":62,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":63,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":74,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":2347,"get_count":4,"put_bytes":339,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":2477,"get_count":3,"put_bytes":477,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2825,"get_count":3,"put_bytes":817,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1119,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3404,"get_count":3,"put_bytes":1396,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":3530,"get_count":3,"put_bytes":1522,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":3717,"get_count":3,"put_bytes":1709,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":3865,"get_count":4,"put_bytes":1857,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":3975,"get_count":4,"put_bytes":1967,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":4067,"get_count":4,"put_bytes":2059,"put_count":3}} -{"i":9,"series":2,"stats":{"get_bytes":4113,"get_count":4,"put_bytes":2105,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":4159,"get_count":4,"put_bytes":2151,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":4199,"get_count":4,"put_bytes":2191,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2214,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":4222,"get_count":4,"put_bytes":2214,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":28,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":38,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":43,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":52,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":55,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":56,"series":2,"stats":{"get_bytes":4494,"get_count":4,"put_bytes":2486,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} -{"i":58,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} -{"i":60,"series":2,"stats":{"get_bytes":4535,"get_count":4,"put_bytes":2527,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":63,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":64,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":65,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":67,"series":2,"stats":{"get_bytes":4576,"get_count":4,"put_bytes":2568,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4617,"get_count":4,"put_bytes":2609,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4681,"get_count":4,"put_bytes":2673,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4704,"get_count":4,"put_bytes":2696,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4745,"get_count":4,"put_bytes":2737,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4768,"get_count":4,"put_bytes":2760,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4768,"get_count":4,"put_bytes":2760,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4876,"get_count":5,"put_bytes":2868,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":4876,"get_count":5,"put_bytes":2868,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":4917,"get_count":5,"put_bytes":2909,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} -{"i":89,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} -{"i":90,"series":2,"stats":{"get_bytes":4958,"get_count":5,"put_bytes":2950,"put_count":4}} -{"i":91,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} -{"i":92,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} -{"i":93,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} -{"i":94,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} -{"i":95,"series":2,"stats":{"get_bytes":4999,"get_count":5,"put_bytes":2991,"put_count":4}} -{"i":96,"series":2,"stats":{"get_bytes":5039,"get_count":5,"put_bytes":3031,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":5039,"get_count":5,"put_bytes":3031,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":5102,"get_count":5,"put_bytes":3094,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":5125,"get_count":5,"put_bytes":3117,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2357,"get_count":3,"put_bytes":459,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":2,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":3,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":4,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":5,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":6,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":8,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2463,"get_count":4,"put_bytes":455,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":30,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":31,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":32,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":45,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":57,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":58,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":61,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":62,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":63,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":74,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":2594,"get_count":3,"put_bytes":594,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2942,"get_count":3,"put_bytes":934,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3244,"get_count":3,"put_bytes":1236,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3521,"get_count":3,"put_bytes":1513,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3647,"get_count":3,"put_bytes":1639,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3834,"get_count":3,"put_bytes":1826,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3982,"get_count":4,"put_bytes":1974,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":4092,"get_count":4,"put_bytes":2084,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4184,"get_count":4,"put_bytes":2176,"put_count":3}} +{"i":9,"series":2,"stats":{"get_bytes":4230,"get_count":4,"put_bytes":2222,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2268,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4316,"get_count":4,"put_bytes":2308,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4316,"get_count":4,"put_bytes":2308,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4316,"get_count":4,"put_bytes":2308,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4339,"get_count":4,"put_bytes":2331,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4339,"get_count":4,"put_bytes":2331,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":28,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":38,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":43,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":52,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":55,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":56,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2644,"put_count":3}} +{"i":58,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2644,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2644,"put_count":3}} +{"i":60,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2644,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":63,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":64,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":65,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":67,"series":2,"stats":{"get_bytes":4693,"get_count":4,"put_bytes":2685,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4734,"get_count":4,"put_bytes":2726,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4798,"get_count":4,"put_bytes":2790,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4798,"get_count":4,"put_bytes":2790,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4798,"get_count":4,"put_bytes":2790,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4798,"get_count":4,"put_bytes":2790,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4798,"get_count":4,"put_bytes":2790,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4821,"get_count":4,"put_bytes":2813,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4862,"get_count":4,"put_bytes":2854,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4862,"get_count":4,"put_bytes":2854,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4862,"get_count":4,"put_bytes":2854,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4885,"get_count":4,"put_bytes":2877,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4885,"get_count":4,"put_bytes":2877,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":5075,"get_count":5,"put_bytes":3067,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":5075,"get_count":5,"put_bytes":3067,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":5075,"get_count":5,"put_bytes":3067,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":5075,"get_count":5,"put_bytes":3067,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":5116,"get_count":5,"put_bytes":3108,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":5116,"get_count":5,"put_bytes":3108,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":5116,"get_count":5,"put_bytes":3108,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":5116,"get_count":5,"put_bytes":3108,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":5116,"get_count":5,"put_bytes":3108,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":5156,"get_count":5,"put_bytes":3148,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":5156,"get_count":5,"put_bytes":3148,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":5219,"get_count":5,"put_bytes":3211,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":5242,"get_count":5,"put_bytes":3234,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index 4c72076a1..5372cfeee 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2199,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2108,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2120,"get_count":3,"put_bytes":112,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2433,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2225,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2237,"get_count":3,"put_bytes":229,"put_count":2}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 5c27c992e..53bf29af4 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":135,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2143,"get_count":3,"put_bytes":177,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2185,"get_count":3,"put_bytes":218,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2226,"get_count":3,"put_bytes":259,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2267,"get_count":3,"put_bytes":300,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2308,"get_count":3,"put_bytes":412,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2331,"get_count":3,"put_bytes":364,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2372,"get_count":3,"put_bytes":475,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2395,"get_count":3,"put_bytes":428,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2436,"get_count":3,"put_bytes":469,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2477,"get_count":3,"put_bytes":510,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2518,"get_count":3,"put_bytes":551,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2559,"get_count":3,"put_bytes":592,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2600,"get_count":3,"put_bytes":704,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2623,"get_count":3,"put_bytes":727,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2646,"get_count":3,"put_bytes":750,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2669,"get_count":3,"put_bytes":702,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2710,"get_count":3,"put_bytes":743,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2751,"get_count":3,"put_bytes":854,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2776,"get_count":3,"put_bytes":809,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2817,"get_count":3,"put_bytes":850,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2858,"get_count":3,"put_bytes":891,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2899,"get_count":3,"put_bytes":1003,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2922,"get_count":3,"put_bytes":1024,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":2945,"get_count":3,"put_bytes":1048,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":2968,"get_count":3,"put_bytes":1070,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":2991,"get_count":3,"put_bytes":1094,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":3014,"get_count":3,"put_bytes":1047,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":3055,"get_count":3,"put_bytes":1088,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":3096,"get_count":3,"put_bytes":1129,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":3137,"get_count":3,"put_bytes":1170,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":3178,"get_count":3,"put_bytes":1211,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":3219,"get_count":3,"put_bytes":1252,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":3260,"get_count":3,"put_bytes":1364,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":3283,"get_count":3,"put_bytes":1387,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":3306,"get_count":3,"put_bytes":1339,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":3347,"get_count":3,"put_bytes":1451,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":3457,"get_count":4,"put_bytes":1492,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":3370,"get_count":3,"put_bytes":1476,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":3484,"get_count":4,"put_bytes":1517,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":3482,"get_count":4,"put_bytes":1517,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":3395,"get_count":3,"put_bytes":1498,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":3507,"get_count":4,"put_bytes":1540,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":3418,"get_count":3,"put_bytes":1452,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":3590,"get_count":4,"put_bytes":1623,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":3631,"get_count":4,"put_bytes":1664,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":3546,"get_count":4,"put_bytes":1560,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":3458,"get_count":3,"put_bytes":1491,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3499,"get_count":3,"put_bytes":1602,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":3522,"get_count":3,"put_bytes":1555,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3652,"get_count":4,"put_bytes":1685,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":3563,"get_count":3,"put_bytes":1596,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3734,"get_count":4,"put_bytes":1767,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":3604,"get_count":3,"put_bytes":1707,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":3715,"get_count":4,"put_bytes":1748,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":3627,"get_count":3,"put_bytes":1660,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3668,"get_count":3,"put_bytes":1701,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1831,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":3798,"get_count":4,"put_bytes":1831,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":3709,"get_count":3,"put_bytes":1742,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":3962,"get_count":4,"put_bytes":2066,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":3750,"get_count":3,"put_bytes":1783,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3880,"get_count":4,"put_bytes":1893,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1934,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1911,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":3877,"get_count":4,"put_bytes":1910,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":3878,"get_count":4,"put_bytes":1911,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":3899,"get_count":4,"put_bytes":1932,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":3789,"get_count":3,"put_bytes":1892,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":3812,"get_count":3,"put_bytes":1916,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":3923,"get_count":4,"put_bytes":1956,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":3835,"get_count":3,"put_bytes":1868,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2039,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":3876,"get_count":3,"put_bytes":1980,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":3987,"get_count":4,"put_bytes":2020,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":4029,"get_count":4,"put_bytes":2062,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":3899,"get_count":3,"put_bytes":2003,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":4011,"get_count":4,"put_bytes":2044,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":3922,"get_count":3,"put_bytes":2024,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":3945,"get_count":3,"put_bytes":2049,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":4097,"get_count":4,"put_bytes":2130,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":4055,"get_count":4,"put_bytes":2088,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":4098,"get_count":4,"put_bytes":2199,"put_count":4}} -{"i":87,"series":1,"stats":{"get_bytes":4203,"get_count":4,"put_bytes":2307,"put_count":4}} -{"i":88,"series":1,"stats":{"get_bytes":3968,"get_count":3,"put_bytes":2070,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":4119,"get_count":4,"put_bytes":2154,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":4121,"get_count":4,"put_bytes":2154,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":4120,"get_count":4,"put_bytes":2153,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":4162,"get_count":4,"put_bytes":2195,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":4080,"get_count":4,"put_bytes":2113,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":4203,"get_count":4,"put_bytes":2236,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":3991,"get_count":3,"put_bytes":2095,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":4102,"get_count":4,"put_bytes":2135,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":4014,"get_count":3,"put_bytes":2115,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":4125,"get_count":4,"put_bytes":2158,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":4166,"get_count":4,"put_bytes":2199,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":4166,"get_count":4,"put_bytes":2200,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":4125,"get_count":4,"put_bytes":2158,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":4037,"get_count":3,"put_bytes":2140,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":2223,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":4272,"get_count":4,"put_bytes":2373,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":4190,"get_count":4,"put_bytes":2223,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":4149,"get_count":4,"put_bytes":2182,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":4272,"get_count":4,"put_bytes":2305,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":4149,"get_count":4,"put_bytes":2182,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2182,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":4231,"get_count":4,"put_bytes":2264,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":4189,"get_count":4,"put_bytes":2222,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":4060,"get_count":3,"put_bytes":2164,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":4254,"get_count":4,"put_bytes":2287,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":4212,"get_count":4,"put_bytes":2245,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2287,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2287,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2286,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":4213,"get_count":4,"put_bytes":2246,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":4295,"get_count":4,"put_bytes":2328,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":4212,"get_count":4,"put_bytes":2314,"put_count":4}} -{"i":28,"series":2,"stats":{"get_bytes":4336,"get_count":4,"put_bytes":2369,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":4083,"get_count":3,"put_bytes":2187,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2309,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2350,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2381,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2309,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":4258,"get_count":4,"put_bytes":2291,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2379,"put_count":4}} -{"i":43,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":4193,"get_count":4,"put_bytes":2228,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2379,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":4194,"get_count":4,"put_bytes":2228,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2228,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2339,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2421,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4195,"get_count":4,"put_bytes":2296,"put_count":4}} -{"i":58,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} -{"i":60,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} -{"i":63,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2503,"put_count":4}} -{"i":64,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2422,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4299,"get_count":4,"put_bytes":2402,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":4341,"get_count":4,"put_bytes":2374,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2456,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4359,"get_count":4,"put_bytes":2392,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2310,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4318,"get_count":4,"put_bytes":2351,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2433,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2269,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4106,"get_count":3,"put_bytes":2210,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2456,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4363,"get_count":4,"put_bytes":2396,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4446,"get_count":4,"put_bytes":2479,"put_count":3}} -{"i":81,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":83,"series":2,"stats":{"get_bytes":4423,"get_count":4,"put_bytes":2526,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} -{"i":85,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":86,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} -{"i":87,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} -{"i":88,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} -{"i":89,"series":2,"stats":{"get_bytes":4218,"get_count":4,"put_bytes":2251,"put_count":3}} -{"i":90,"series":2,"stats":{"get_bytes":4345,"get_count":4,"put_bytes":2379,"put_count":3}} -{"i":91,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":92,"series":2,"stats":{"get_bytes":4405,"get_count":4,"put_bytes":2438,"put_count":3}} -{"i":93,"series":2,"stats":{"get_bytes":4259,"get_count":4,"put_bytes":2292,"put_count":3}} -{"i":94,"series":2,"stats":{"get_bytes":4382,"get_count":4,"put_bytes":2415,"put_count":3}} -{"i":95,"series":2,"stats":{"get_bytes":4300,"get_count":4,"put_bytes":2333,"put_count":3}} -{"i":96,"series":2,"stats":{"get_bytes":4129,"get_count":3,"put_bytes":2231,"put_count":3}} -{"i":97,"series":2,"stats":{"get_bytes":4364,"get_count":4,"put_bytes":2397,"put_count":3}} -{"i":98,"series":2,"stats":{"get_bytes":4428,"get_count":4,"put_bytes":2461,"put_count":3}} -{"i":99,"series":2,"stats":{"get_bytes":4282,"get_count":4,"put_bytes":2315,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2216,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":252,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2260,"get_count":3,"put_bytes":294,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2302,"get_count":3,"put_bytes":335,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2343,"get_count":3,"put_bytes":376,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2384,"get_count":3,"put_bytes":417,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2425,"get_count":3,"put_bytes":529,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2448,"get_count":3,"put_bytes":481,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2489,"get_count":3,"put_bytes":592,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":545,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2553,"get_count":3,"put_bytes":586,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2594,"get_count":3,"put_bytes":627,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2635,"get_count":3,"put_bytes":668,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2676,"get_count":3,"put_bytes":709,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2717,"get_count":3,"put_bytes":821,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2740,"get_count":3,"put_bytes":844,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2763,"get_count":3,"put_bytes":867,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2786,"get_count":3,"put_bytes":819,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2827,"get_count":3,"put_bytes":860,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2868,"get_count":3,"put_bytes":971,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2893,"get_count":3,"put_bytes":926,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2934,"get_count":3,"put_bytes":967,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2975,"get_count":3,"put_bytes":1008,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":3016,"get_count":3,"put_bytes":1120,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1141,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":3062,"get_count":3,"put_bytes":1165,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":3085,"get_count":3,"put_bytes":1187,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":3108,"get_count":3,"put_bytes":1211,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":3131,"get_count":3,"put_bytes":1164,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":3172,"get_count":3,"put_bytes":1205,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":3213,"get_count":3,"put_bytes":1246,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3254,"get_count":3,"put_bytes":1287,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3295,"get_count":3,"put_bytes":1328,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3336,"get_count":3,"put_bytes":1369,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3377,"get_count":3,"put_bytes":1481,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":3400,"get_count":3,"put_bytes":1504,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":3423,"get_count":3,"put_bytes":1456,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3464,"get_count":3,"put_bytes":1568,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":3574,"get_count":4,"put_bytes":1609,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":3487,"get_count":3,"put_bytes":1593,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":3601,"get_count":4,"put_bytes":1634,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":3599,"get_count":4,"put_bytes":1634,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":3512,"get_count":3,"put_bytes":1615,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":3624,"get_count":4,"put_bytes":1657,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":3535,"get_count":3,"put_bytes":1569,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3707,"get_count":4,"put_bytes":1740,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":3748,"get_count":4,"put_bytes":1781,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":3663,"get_count":4,"put_bytes":1677,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1608,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3616,"get_count":3,"put_bytes":1719,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":3639,"get_count":3,"put_bytes":1672,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3769,"get_count":4,"put_bytes":1802,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":3680,"get_count":3,"put_bytes":1713,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3851,"get_count":4,"put_bytes":1884,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":3721,"get_count":3,"put_bytes":1824,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":3832,"get_count":4,"put_bytes":1865,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":3744,"get_count":3,"put_bytes":1777,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3785,"get_count":3,"put_bytes":1818,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3915,"get_count":4,"put_bytes":1948,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":3915,"get_count":4,"put_bytes":1948,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":3826,"get_count":3,"put_bytes":1859,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":4079,"get_count":4,"put_bytes":2183,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":3867,"get_count":3,"put_bytes":1900,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3997,"get_count":4,"put_bytes":2010,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":4018,"get_count":4,"put_bytes":2051,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":3994,"get_count":4,"put_bytes":2027,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":4077,"get_count":4,"put_bytes":2110,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":4016,"get_count":4,"put_bytes":2049,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":3906,"get_count":3,"put_bytes":2009,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":3929,"get_count":3,"put_bytes":2033,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":4040,"get_count":4,"put_bytes":2073,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":3952,"get_count":3,"put_bytes":1985,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":4123,"get_count":4,"put_bytes":2156,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":3993,"get_count":3,"put_bytes":2097,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":4104,"get_count":4,"put_bytes":2137,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":4016,"get_count":3,"put_bytes":2120,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":4128,"get_count":4,"put_bytes":2161,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":4039,"get_count":3,"put_bytes":2141,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":4062,"get_count":3,"put_bytes":2166,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":4214,"get_count":4,"put_bytes":2247,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":4172,"get_count":4,"put_bytes":2205,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":4215,"get_count":4,"put_bytes":2316,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":4320,"get_count":4,"put_bytes":2424,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":4085,"get_count":3,"put_bytes":2187,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2271,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":4238,"get_count":4,"put_bytes":2271,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":4237,"get_count":4,"put_bytes":2270,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":4197,"get_count":4,"put_bytes":2230,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":4320,"get_count":4,"put_bytes":2353,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":4108,"get_count":3,"put_bytes":2212,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2252,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":4131,"get_count":3,"put_bytes":2232,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":4242,"get_count":4,"put_bytes":2275,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":4283,"get_count":4,"put_bytes":2316,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":4283,"get_count":4,"put_bytes":2317,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":4242,"get_count":4,"put_bytes":2275,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":4154,"get_count":3,"put_bytes":2257,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4389,"get_count":4,"put_bytes":2490,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2299,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4389,"get_count":4,"put_bytes":2422,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2299,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4263,"get_count":4,"put_bytes":2299,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4348,"get_count":4,"put_bytes":2381,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4306,"get_count":4,"put_bytes":2339,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4177,"get_count":3,"put_bytes":2281,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4329,"get_count":4,"put_bytes":2362,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2403,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2445,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4329,"get_count":4,"put_bytes":2431,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2486,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4200,"get_count":3,"put_bytes":2304,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4393,"get_count":4,"put_bytes":2426,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2467,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2498,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4393,"get_count":4,"put_bytes":2426,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4375,"get_count":4,"put_bytes":2408,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2496,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4310,"get_count":4,"put_bytes":2345,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4374,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2496,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":4311,"get_count":4,"put_bytes":2345,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2456,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2538,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2413,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2539,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4416,"get_count":4,"put_bytes":2519,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2573,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2550,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4223,"get_count":3,"put_bytes":2327,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2573,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4480,"get_count":4,"put_bytes":2513,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4563,"get_count":4,"put_bytes":2596,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2643,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":4462,"get_count":4,"put_bytes":2496,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":4499,"get_count":4,"put_bytes":2532,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":4246,"get_count":3,"put_bytes":2348,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":4545,"get_count":4,"put_bytes":2578,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2432,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index 3bb020620..67296f9aa 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":2099,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2099,"get_count":3,"put_bytes":6519,"put_count":35}} -{"i":2,"series":1,"stats":{"get_bytes":7742,"get_count":28,"put_bytes":10971,"put_count":43}} -{"i":3,"series":1,"stats":{"get_bytes":11277,"get_count":32,"put_bytes":14428,"put_count":46}} -{"i":4,"series":1,"stats":{"get_bytes":15861,"get_count":37,"put_bytes":20198,"put_count":68}} -{"i":5,"series":1,"stats":{"get_bytes":19308,"get_count":43,"put_bytes":22950,"put_count":64}} -{"i":6,"series":1,"stats":{"get_bytes":20933,"get_count":38,"put_bytes":25558,"put_count":73}} -{"i":7,"series":1,"stats":{"get_bytes":25662,"get_count":47,"put_bytes":30218,"put_count":81}} -{"i":8,"series":1,"stats":{"get_bytes":30144,"get_count":48,"put_bytes":34690,"put_count":82}} -{"i":9,"series":1,"stats":{"get_bytes":32104,"get_count":55,"put_bytes":36897,"put_count":93}} -{"i":10,"series":1,"stats":{"get_bytes":35617,"get_count":49,"put_bytes":40031,"put_count":81}} -{"i":11,"series":1,"stats":{"get_bytes":38827,"get_count":54,"put_bytes":43796,"put_count":94}} -{"i":12,"series":1,"stats":{"get_bytes":41747,"get_count":70,"put_bytes":46546,"put_count":108}} -{"i":13,"series":1,"stats":{"get_bytes":41298,"get_count":68,"put_bytes":45844,"put_count":102}} -{"i":14,"series":1,"stats":{"get_bytes":45814,"get_count":67,"put_bytes":50908,"put_count":109}} -{"i":15,"series":1,"stats":{"get_bytes":47243,"get_count":71,"put_bytes":51942,"put_count":107}} -{"i":16,"series":1,"stats":{"get_bytes":49844,"get_count":72,"put_bytes":54874,"put_count":113}} -{"i":17,"series":1,"stats":{"get_bytes":50905,"get_count":74,"put_bytes":55887,"put_count":114}} -{"i":18,"series":1,"stats":{"get_bytes":49539,"get_count":75,"put_bytes":54434,"put_count":114}} -{"i":19,"series":1,"stats":{"get_bytes":59204,"get_count":92,"put_bytes":63340,"put_count":120}} -{"i":20,"series":1,"stats":{"get_bytes":53996,"get_count":81,"put_bytes":58422,"put_count":113}} -{"i":21,"series":1,"stats":{"get_bytes":56878,"get_count":80,"put_bytes":61938,"put_count":121}} -{"i":22,"series":1,"stats":{"get_bytes":57211,"get_count":83,"put_bytes":61775,"put_count":117}} -{"i":23,"series":1,"stats":{"get_bytes":65829,"get_count":108,"put_bytes":68885,"put_count":121}} -{"i":24,"series":1,"stats":{"get_bytes":64920,"get_count":104,"put_bytes":68621,"put_count":126}} -{"i":25,"series":1,"stats":{"get_bytes":65356,"get_count":102,"put_bytes":69556,"put_count":131}} -{"i":26,"series":1,"stats":{"get_bytes":64373,"get_count":94,"put_bytes":69010,"put_count":129}} -{"i":27,"series":1,"stats":{"get_bytes":67478,"get_count":107,"put_bytes":70921,"put_count":125}} -{"i":28,"series":1,"stats":{"get_bytes":67869,"get_count":106,"put_bytes":72012,"put_count":134}} -{"i":29,"series":1,"stats":{"get_bytes":68545,"get_count":106,"put_bytes":71920,"put_count":123}} -{"i":30,"series":1,"stats":{"get_bytes":73611,"get_count":113,"put_bytes":77671,"put_count":140}} -{"i":31,"series":1,"stats":{"get_bytes":67769,"get_count":107,"put_bytes":71605,"put_count":131}} -{"i":32,"series":1,"stats":{"get_bytes":74875,"get_count":111,"put_bytes":78712,"put_count":135}} -{"i":33,"series":1,"stats":{"get_bytes":72397,"get_count":114,"put_bytes":76441,"put_count":141}} -{"i":34,"series":1,"stats":{"get_bytes":74728,"get_count":113,"put_bytes":78044,"put_count":130}} -{"i":35,"series":1,"stats":{"get_bytes":74250,"get_count":116,"put_bytes":77438,"put_count":131}} -{"i":36,"series":1,"stats":{"get_bytes":75270,"get_count":113,"put_bytes":78545,"put_count":129}} -{"i":37,"series":1,"stats":{"get_bytes":76288,"get_count":117,"put_bytes":79945,"put_count":138}} -{"i":38,"series":1,"stats":{"get_bytes":78472,"get_count":120,"put_bytes":82316,"put_count":144}} -{"i":39,"series":1,"stats":{"get_bytes":72991,"get_count":115,"put_bytes":76226,"put_count":130}} -{"i":40,"series":1,"stats":{"get_bytes":78483,"get_count":121,"put_bytes":81563,"put_count":134}} -{"i":41,"series":1,"stats":{"get_bytes":78043,"get_count":118,"put_bytes":81349,"put_count":134}} -{"i":42,"series":1,"stats":{"get_bytes":80519,"get_count":118,"put_bytes":84308,"put_count":141}} -{"i":43,"series":1,"stats":{"get_bytes":79002,"get_count":121,"put_bytes":82284,"put_count":137}} -{"i":44,"series":1,"stats":{"get_bytes":84630,"get_count":127,"put_bytes":87585,"put_count":138}} -{"i":45,"series":1,"stats":{"get_bytes":81893,"get_count":123,"put_bytes":85512,"put_count":144}} -{"i":46,"series":1,"stats":{"get_bytes":83817,"get_count":128,"put_bytes":87318,"put_count":147}} -{"i":47,"series":1,"stats":{"get_bytes":83553,"get_count":128,"put_bytes":86920,"put_count":145}} -{"i":48,"series":1,"stats":{"get_bytes":87152,"get_count":125,"put_bytes":90316,"put_count":139}} -{"i":49,"series":1,"stats":{"get_bytes":85056,"get_count":127,"put_bytes":88492,"put_count":145}} -{"i":50,"series":1,"stats":{"get_bytes":81605,"get_count":117,"put_bytes":84758,"put_count":131}} -{"i":51,"series":1,"stats":{"get_bytes":85290,"get_count":119,"put_bytes":88376,"put_count":132}} -{"i":52,"series":1,"stats":{"get_bytes":86074,"get_count":128,"put_bytes":89503,"put_count":146}} -{"i":53,"series":1,"stats":{"get_bytes":82817,"get_count":122,"put_bytes":85687,"put_count":132}} -{"i":54,"series":1,"stats":{"get_bytes":87781,"get_count":130,"put_bytes":91081,"put_count":146}} -{"i":55,"series":1,"stats":{"get_bytes":85214,"get_count":120,"put_bytes":88273,"put_count":133}} -{"i":56,"series":1,"stats":{"get_bytes":85673,"get_count":127,"put_bytes":88899,"put_count":142}} -{"i":57,"series":1,"stats":{"get_bytes":89977,"get_count":132,"put_bytes":92910,"put_count":143}} -{"i":58,"series":1,"stats":{"get_bytes":87311,"get_count":129,"put_bytes":90520,"put_count":144}} -{"i":59,"series":1,"stats":{"get_bytes":89642,"get_count":135,"put_bytes":92369,"put_count":143}} -{"i":60,"series":1,"stats":{"get_bytes":88152,"get_count":129,"put_bytes":91446,"put_count":145}} -{"i":61,"series":1,"stats":{"get_bytes":92710,"get_count":131,"put_bytes":95863,"put_count":145}} -{"i":62,"series":1,"stats":{"get_bytes":85431,"get_count":127,"put_bytes":88385,"put_count":138}} -{"i":63,"series":1,"stats":{"get_bytes":93713,"get_count":134,"put_bytes":96949,"put_count":150}} -{"i":64,"series":1,"stats":{"get_bytes":93582,"get_count":131,"put_bytes":96812,"put_count":146}} -{"i":65,"series":1,"stats":{"get_bytes":86287,"get_count":126,"put_bytes":89721,"put_count":144}} -{"i":66,"series":1,"stats":{"get_bytes":92118,"get_count":128,"put_bytes":95619,"put_count":147}} -{"i":67,"series":1,"stats":{"get_bytes":92875,"get_count":134,"put_bytes":95819,"put_count":145}} -{"i":68,"series":1,"stats":{"get_bytes":94456,"get_count":130,"put_bytes":97610,"put_count":144}} -{"i":69,"series":1,"stats":{"get_bytes":94240,"get_count":131,"put_bytes":97527,"put_count":147}} -{"i":70,"series":1,"stats":{"get_bytes":95340,"get_count":131,"put_bytes":98762,"put_count":149}} -{"i":71,"series":1,"stats":{"get_bytes":91853,"get_count":126,"put_bytes":95355,"put_count":145}} -{"i":72,"series":1,"stats":{"get_bytes":91845,"get_count":131,"put_bytes":95135,"put_count":147}} -{"i":73,"series":1,"stats":{"get_bytes":93444,"get_count":132,"put_bytes":96309,"put_count":142}} -{"i":74,"series":1,"stats":{"get_bytes":90604,"get_count":127,"put_bytes":93967,"put_count":144}} -{"i":75,"series":1,"stats":{"get_bytes":97866,"get_count":134,"put_bytes":101300,"put_count":152}} -{"i":76,"series":1,"stats":{"get_bytes":96072,"get_count":131,"put_bytes":99365,"put_count":147}} -{"i":77,"series":1,"stats":{"get_bytes":93849,"get_count":132,"put_bytes":96580,"put_count":140}} -{"i":78,"series":1,"stats":{"get_bytes":95377,"get_count":130,"put_bytes":98456,"put_count":143}} -{"i":79,"series":1,"stats":{"get_bytes":95274,"get_count":132,"put_bytes":99056,"put_count":155}} -{"i":80,"series":1,"stats":{"get_bytes":96128,"get_count":133,"put_bytes":99555,"put_count":151}} -{"i":81,"series":1,"stats":{"get_bytes":99709,"get_count":138,"put_bytes":103137,"put_count":156}} -{"i":82,"series":1,"stats":{"get_bytes":95831,"get_count":127,"put_bytes":99684,"put_count":151}} -{"i":83,"series":1,"stats":{"get_bytes":92715,"get_count":123,"put_bytes":95865,"put_count":137}} -{"i":84,"series":1,"stats":{"get_bytes":102124,"get_count":134,"put_bytes":105614,"put_count":153}} -{"i":85,"series":1,"stats":{"get_bytes":97091,"get_count":129,"put_bytes":100169,"put_count":142}} -{"i":86,"series":1,"stats":{"get_bytes":99448,"get_count":131,"put_bytes":102601,"put_count":145}} -{"i":87,"series":1,"stats":{"get_bytes":97359,"get_count":129,"put_bytes":100790,"put_count":147}} -{"i":88,"series":1,"stats":{"get_bytes":98257,"get_count":131,"put_bytes":102101,"put_count":155}} -{"i":89,"series":1,"stats":{"get_bytes":101820,"get_count":133,"put_bytes":105733,"put_count":158}} -{"i":90,"series":1,"stats":{"get_bytes":105076,"get_count":135,"put_bytes":108787,"put_count":157}} -{"i":91,"series":1,"stats":{"get_bytes":98922,"get_count":135,"put_bytes":102422,"put_count":154}} -{"i":92,"series":1,"stats":{"get_bytes":104982,"get_count":132,"put_bytes":108833,"put_count":156}} -{"i":93,"series":1,"stats":{"get_bytes":99176,"get_count":130,"put_bytes":103586,"put_count":162}} -{"i":94,"series":1,"stats":{"get_bytes":102213,"get_count":130,"put_bytes":105774,"put_count":150}} -{"i":95,"series":1,"stats":{"get_bytes":102390,"get_count":132,"put_bytes":106245,"put_count":156}} -{"i":96,"series":1,"stats":{"get_bytes":99872,"get_count":134,"put_bytes":102950,"put_count":147}} -{"i":97,"series":1,"stats":{"get_bytes":100940,"get_count":135,"put_bytes":104658,"put_count":157}} -{"i":98,"series":1,"stats":{"get_bytes":99828,"get_count":133,"put_bytes":103321,"put_count":152}} -{"i":99,"series":1,"stats":{"get_bytes":103264,"get_count":135,"put_bytes":106626,"put_count":152}} -{"i":0,"series":2,"stats":{"get_bytes":106237,"get_count":140,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":104104,"get_count":131,"put_bytes":107741,"put_count":152}} -{"i":2,"series":2,"stats":{"get_bytes":103479,"get_count":132,"put_bytes":107466,"put_count":158}} -{"i":3,"series":2,"stats":{"get_bytes":105396,"get_count":133,"put_bytes":109461,"put_count":160}} -{"i":4,"series":2,"stats":{"get_bytes":99286,"get_count":130,"put_bytes":103202,"put_count":155}} -{"i":5,"series":2,"stats":{"get_bytes":104792,"get_count":131,"put_bytes":108853,"put_count":158}} -{"i":6,"series":2,"stats":{"get_bytes":108316,"get_count":133,"put_bytes":112242,"put_count":158}} -{"i":7,"series":2,"stats":{"get_bytes":108222,"get_count":134,"put_bytes":112290,"put_count":161}} -{"i":8,"series":2,"stats":{"get_bytes":110738,"get_count":138,"put_bytes":114657,"put_count":163}} -{"i":9,"series":2,"stats":{"get_bytes":109367,"get_count":134,"put_bytes":113148,"put_count":157}} -{"i":10,"series":2,"stats":{"get_bytes":105071,"get_count":131,"put_bytes":108851,"put_count":154}} -{"i":11,"series":2,"stats":{"get_bytes":108392,"get_count":137,"put_bytes":111961,"put_count":157}} -{"i":12,"series":2,"stats":{"get_bytes":106081,"get_count":130,"put_bytes":109916,"put_count":154}} -{"i":13,"series":2,"stats":{"get_bytes":105853,"get_count":132,"put_bytes":109707,"put_count":156}} -{"i":14,"series":2,"stats":{"get_bytes":109958,"get_count":135,"put_bytes":114002,"put_count":162}} -{"i":15,"series":2,"stats":{"get_bytes":97848,"get_count":127,"put_bytes":101204,"put_count":144}} -{"i":16,"series":2,"stats":{"get_bytes":109556,"get_count":134,"put_bytes":113274,"put_count":156}} -{"i":17,"series":2,"stats":{"get_bytes":110933,"get_count":136,"put_bytes":114499,"put_count":156}} -{"i":18,"series":2,"stats":{"get_bytes":111529,"get_count":135,"put_bytes":115663,"put_count":163}} -{"i":19,"series":2,"stats":{"get_bytes":108148,"get_count":129,"put_bytes":111441,"put_count":145}} -{"i":20,"series":2,"stats":{"get_bytes":108837,"get_count":132,"put_bytes":112684,"put_count":156}} -{"i":21,"series":2,"stats":{"get_bytes":115246,"get_count":139,"put_bytes":119240,"put_count":165}} -{"i":22,"series":2,"stats":{"get_bytes":111964,"get_count":135,"put_bytes":115884,"put_count":160}} -{"i":23,"series":2,"stats":{"get_bytes":109389,"get_count":131,"put_bytes":113101,"put_count":153}} -{"i":24,"series":2,"stats":{"get_bytes":111808,"get_count":131,"put_bytes":115790,"put_count":157}} -{"i":25,"series":2,"stats":{"get_bytes":112928,"get_count":129,"put_bytes":116758,"put_count":153}} -{"i":26,"series":2,"stats":{"get_bytes":114977,"get_count":139,"put_bytes":118483,"put_count":158}} -{"i":27,"series":2,"stats":{"get_bytes":116191,"get_count":138,"put_bytes":119948,"put_count":161}} -{"i":28,"series":2,"stats":{"get_bytes":114316,"get_count":135,"put_bytes":118576,"put_count":165}} -{"i":29,"series":2,"stats":{"get_bytes":107953,"get_count":133,"put_bytes":111461,"put_count":152}} -{"i":30,"series":2,"stats":{"get_bytes":112735,"get_count":136,"put_bytes":116867,"put_count":164}} -{"i":31,"series":2,"stats":{"get_bytes":115493,"get_count":137,"put_bytes":119691,"put_count":166}} -{"i":32,"series":2,"stats":{"get_bytes":116304,"get_count":139,"put_bytes":120783,"put_count":172}} -{"i":33,"series":2,"stats":{"get_bytes":109730,"get_count":131,"put_bytes":113988,"put_count":161}} -{"i":34,"series":2,"stats":{"get_bytes":115868,"get_count":136,"put_bytes":119852,"put_count":162}} -{"i":35,"series":2,"stats":{"get_bytes":117164,"get_count":133,"put_bytes":121206,"put_count":160}} -{"i":36,"series":2,"stats":{"get_bytes":116400,"get_count":134,"put_bytes":121094,"put_count":170}} -{"i":37,"series":2,"stats":{"get_bytes":114775,"get_count":132,"put_bytes":118699,"put_count":157}} -{"i":38,"series":2,"stats":{"get_bytes":112757,"get_count":131,"put_bytes":117388,"put_count":166}} -{"i":39,"series":2,"stats":{"get_bytes":110309,"get_count":131,"put_bytes":113871,"put_count":151}} -{"i":40,"series":2,"stats":{"get_bytes":111342,"get_count":131,"put_bytes":115398,"put_count":158}} -{"i":41,"series":2,"stats":{"get_bytes":115709,"get_count":143,"put_bytes":119558,"put_count":167}} -{"i":42,"series":2,"stats":{"get_bytes":110642,"get_count":127,"put_bytes":114915,"put_count":157}} -{"i":43,"series":2,"stats":{"get_bytes":117673,"get_count":131,"put_bytes":121235,"put_count":151}} -{"i":44,"series":2,"stats":{"get_bytes":117699,"get_count":137,"put_bytes":121481,"put_count":160}} -{"i":45,"series":2,"stats":{"get_bytes":121353,"get_count":142,"put_bytes":125495,"put_count":170}} -{"i":46,"series":2,"stats":{"get_bytes":120186,"get_count":140,"put_bytes":123821,"put_count":161}} -{"i":47,"series":2,"stats":{"get_bytes":118448,"get_count":135,"put_bytes":122925,"put_count":168}} -{"i":48,"series":2,"stats":{"get_bytes":118469,"get_count":137,"put_bytes":122951,"put_count":170}} -{"i":49,"series":2,"stats":{"get_bytes":119761,"get_count":136,"put_bytes":123817,"put_count":163}} -{"i":50,"series":2,"stats":{"get_bytes":117416,"get_count":136,"put_bytes":121201,"put_count":159}} -{"i":51,"series":2,"stats":{"get_bytes":120080,"get_count":139,"put_bytes":123434,"put_count":156}} -{"i":52,"series":2,"stats":{"get_bytes":122196,"get_count":144,"put_bytes":126672,"put_count":177}} -{"i":53,"series":2,"stats":{"get_bytes":115800,"get_count":129,"put_bytes":119785,"put_count":155}} -{"i":54,"series":2,"stats":{"get_bytes":118420,"get_count":142,"put_bytes":122557,"put_count":170}} -{"i":55,"series":2,"stats":{"get_bytes":118031,"get_count":130,"put_bytes":122019,"put_count":156}} -{"i":56,"series":2,"stats":{"get_bytes":120365,"get_count":140,"put_bytes":124774,"put_count":172}} -{"i":57,"series":2,"stats":{"get_bytes":120326,"get_count":139,"put_bytes":124575,"put_count":169}} -{"i":58,"series":2,"stats":{"get_bytes":126829,"get_count":139,"put_bytes":131386,"put_count":173}} -{"i":59,"series":2,"stats":{"get_bytes":125729,"get_count":133,"put_bytes":130832,"put_count":175}} -{"i":60,"series":2,"stats":{"get_bytes":127163,"get_count":136,"put_bytes":131778,"put_count":171}} -{"i":61,"series":2,"stats":{"get_bytes":126622,"get_count":134,"put_bytes":131463,"put_count":172}} -{"i":62,"series":2,"stats":{"get_bytes":120376,"get_count":131,"put_bytes":124722,"put_count":162}} -{"i":63,"series":2,"stats":{"get_bytes":117992,"get_count":138,"put_bytes":122529,"put_count":172}} -{"i":64,"series":2,"stats":{"get_bytes":124680,"get_count":139,"put_bytes":129090,"put_count":171}} -{"i":65,"series":2,"stats":{"get_bytes":125481,"get_count":137,"put_bytes":129961,"put_count":170}} -{"i":66,"series":2,"stats":{"get_bytes":125328,"get_count":138,"put_bytes":129535,"put_count":167}} -{"i":67,"series":2,"stats":{"get_bytes":127745,"get_count":144,"put_bytes":131872,"put_count":172}} -{"i":68,"series":2,"stats":{"get_bytes":128214,"get_count":140,"put_bytes":132474,"put_count":170}} -{"i":69,"series":2,"stats":{"get_bytes":124509,"get_count":140,"put_bytes":129050,"put_count":174}} -{"i":70,"series":2,"stats":{"get_bytes":128341,"get_count":142,"put_bytes":132119,"put_count":165}} -{"i":71,"series":2,"stats":{"get_bytes":125323,"get_count":134,"put_bytes":129588,"put_count":164}} -{"i":72,"series":2,"stats":{"get_bytes":123821,"get_count":135,"put_bytes":128092,"put_count":165}} -{"i":73,"series":2,"stats":{"get_bytes":125335,"get_count":140,"put_bytes":130239,"put_count":179}} -{"i":74,"series":2,"stats":{"get_bytes":122783,"get_count":134,"put_bytes":127269,"put_count":167}} -{"i":75,"series":2,"stats":{"get_bytes":124414,"get_count":141,"put_bytes":128473,"put_count":168}} -{"i":76,"series":2,"stats":{"get_bytes":126788,"get_count":142,"put_bytes":131414,"put_count":177}} -{"i":77,"series":2,"stats":{"get_bytes":131185,"get_count":135,"put_bytes":135654,"put_count":168}} -{"i":78,"series":2,"stats":{"get_bytes":129171,"get_count":140,"put_bytes":133522,"put_count":171}} -{"i":79,"series":2,"stats":{"get_bytes":128734,"get_count":134,"put_bytes":133367,"put_count":169}} -{"i":80,"series":2,"stats":{"get_bytes":131195,"get_count":143,"put_bytes":135879,"put_count":179}} -{"i":81,"series":2,"stats":{"get_bytes":125430,"get_count":137,"put_bytes":129637,"put_count":166}} -{"i":82,"series":2,"stats":{"get_bytes":132465,"get_count":145,"put_bytes":136807,"put_count":176}} -{"i":83,"series":2,"stats":{"get_bytes":129025,"get_count":136,"put_bytes":133934,"put_count":175}} -{"i":84,"series":2,"stats":{"get_bytes":128066,"get_count":136,"put_bytes":132249,"put_count":165}} -{"i":85,"series":2,"stats":{"get_bytes":128611,"get_count":137,"put_bytes":133094,"put_count":170}} -{"i":86,"series":2,"stats":{"get_bytes":125953,"get_count":142,"put_bytes":130583,"put_count":177}} -{"i":87,"series":2,"stats":{"get_bytes":134276,"get_count":140,"put_bytes":138836,"put_count":174}} -{"i":88,"series":2,"stats":{"get_bytes":128599,"get_count":138,"put_bytes":133003,"put_count":170}} -{"i":89,"series":2,"stats":{"get_bytes":126078,"get_count":140,"put_bytes":130147,"put_count":167}} -{"i":90,"series":2,"stats":{"get_bytes":127884,"get_count":141,"put_bytes":131944,"put_count":168}} -{"i":91,"series":2,"stats":{"get_bytes":134233,"get_count":143,"put_bytes":138244,"put_count":170}} -{"i":92,"series":2,"stats":{"get_bytes":128689,"get_count":140,"put_bytes":133010,"put_count":171}} -{"i":93,"series":2,"stats":{"get_bytes":129812,"get_count":140,"put_bytes":134679,"put_count":179}} -{"i":94,"series":2,"stats":{"get_bytes":132369,"get_count":143,"put_bytes":136568,"put_count":172}} -{"i":95,"series":2,"stats":{"get_bytes":129756,"get_count":142,"put_bytes":133954,"put_count":171}} -{"i":96,"series":2,"stats":{"get_bytes":133823,"get_count":142,"put_bytes":138356,"put_count":176}} -{"i":97,"series":2,"stats":{"get_bytes":133390,"get_count":141,"put_bytes":137948,"put_count":175}} -{"i":98,"series":2,"stats":{"get_bytes":136765,"get_count":146,"put_bytes":140620,"put_count":170}} -{"i":99,"series":2,"stats":{"get_bytes":126420,"get_count":139,"put_bytes":130901,"put_count":172}} +{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2216,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":6636,"put_count":35}} +{"i":2,"series":1,"stats":{"get_bytes":7859,"get_count":28,"put_bytes":11088,"put_count":43}} +{"i":3,"series":1,"stats":{"get_bytes":11394,"get_count":32,"put_bytes":14545,"put_count":46}} +{"i":4,"series":1,"stats":{"get_bytes":15978,"get_count":37,"put_bytes":20315,"put_count":68}} +{"i":5,"series":1,"stats":{"get_bytes":19425,"get_count":43,"put_bytes":23067,"put_count":64}} +{"i":6,"series":1,"stats":{"get_bytes":21050,"get_count":38,"put_bytes":25675,"put_count":73}} +{"i":7,"series":1,"stats":{"get_bytes":25779,"get_count":47,"put_bytes":30335,"put_count":81}} +{"i":8,"series":1,"stats":{"get_bytes":30261,"get_count":48,"put_bytes":34807,"put_count":82}} +{"i":9,"series":1,"stats":{"get_bytes":32221,"get_count":55,"put_bytes":37014,"put_count":93}} +{"i":10,"series":1,"stats":{"get_bytes":35734,"get_count":49,"put_bytes":40148,"put_count":81}} +{"i":11,"series":1,"stats":{"get_bytes":38944,"get_count":54,"put_bytes":43913,"put_count":94}} +{"i":12,"series":1,"stats":{"get_bytes":41864,"get_count":70,"put_bytes":46663,"put_count":108}} +{"i":13,"series":1,"stats":{"get_bytes":41415,"get_count":68,"put_bytes":45961,"put_count":102}} +{"i":14,"series":1,"stats":{"get_bytes":45931,"get_count":67,"put_bytes":51025,"put_count":109}} +{"i":15,"series":1,"stats":{"get_bytes":47360,"get_count":71,"put_bytes":52059,"put_count":107}} +{"i":16,"series":1,"stats":{"get_bytes":49961,"get_count":72,"put_bytes":54991,"put_count":113}} +{"i":17,"series":1,"stats":{"get_bytes":51022,"get_count":74,"put_bytes":56004,"put_count":114}} +{"i":18,"series":1,"stats":{"get_bytes":49656,"get_count":75,"put_bytes":54551,"put_count":114}} +{"i":19,"series":1,"stats":{"get_bytes":59321,"get_count":92,"put_bytes":63457,"put_count":120}} +{"i":20,"series":1,"stats":{"get_bytes":54113,"get_count":81,"put_bytes":58539,"put_count":113}} +{"i":21,"series":1,"stats":{"get_bytes":56995,"get_count":80,"put_bytes":62055,"put_count":121}} +{"i":22,"series":1,"stats":{"get_bytes":57328,"get_count":83,"put_bytes":61892,"put_count":117}} +{"i":23,"series":1,"stats":{"get_bytes":65946,"get_count":108,"put_bytes":69002,"put_count":121}} +{"i":24,"series":1,"stats":{"get_bytes":65037,"get_count":104,"put_bytes":68738,"put_count":126}} +{"i":25,"series":1,"stats":{"get_bytes":65473,"get_count":102,"put_bytes":69673,"put_count":131}} +{"i":26,"series":1,"stats":{"get_bytes":64490,"get_count":94,"put_bytes":69127,"put_count":129}} +{"i":27,"series":1,"stats":{"get_bytes":67595,"get_count":107,"put_bytes":71038,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":67986,"get_count":106,"put_bytes":72129,"put_count":134}} +{"i":29,"series":1,"stats":{"get_bytes":68662,"get_count":106,"put_bytes":72037,"put_count":123}} +{"i":30,"series":1,"stats":{"get_bytes":73728,"get_count":113,"put_bytes":77788,"put_count":140}} +{"i":31,"series":1,"stats":{"get_bytes":67886,"get_count":107,"put_bytes":71722,"put_count":131}} +{"i":32,"series":1,"stats":{"get_bytes":74992,"get_count":111,"put_bytes":78829,"put_count":135}} +{"i":33,"series":1,"stats":{"get_bytes":72514,"get_count":114,"put_bytes":76558,"put_count":141}} +{"i":34,"series":1,"stats":{"get_bytes":74845,"get_count":113,"put_bytes":78161,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":74367,"get_count":116,"put_bytes":77555,"put_count":131}} +{"i":36,"series":1,"stats":{"get_bytes":75387,"get_count":113,"put_bytes":78662,"put_count":129}} +{"i":37,"series":1,"stats":{"get_bytes":76405,"get_count":117,"put_bytes":80062,"put_count":138}} +{"i":38,"series":1,"stats":{"get_bytes":78589,"get_count":120,"put_bytes":82433,"put_count":144}} +{"i":39,"series":1,"stats":{"get_bytes":73108,"get_count":115,"put_bytes":76343,"put_count":130}} +{"i":40,"series":1,"stats":{"get_bytes":78600,"get_count":121,"put_bytes":81680,"put_count":134}} +{"i":41,"series":1,"stats":{"get_bytes":78160,"get_count":118,"put_bytes":81466,"put_count":134}} +{"i":42,"series":1,"stats":{"get_bytes":80636,"get_count":118,"put_bytes":84425,"put_count":141}} +{"i":43,"series":1,"stats":{"get_bytes":79119,"get_count":121,"put_bytes":82401,"put_count":137}} +{"i":44,"series":1,"stats":{"get_bytes":84747,"get_count":127,"put_bytes":87702,"put_count":138}} +{"i":45,"series":1,"stats":{"get_bytes":82010,"get_count":123,"put_bytes":85629,"put_count":144}} +{"i":46,"series":1,"stats":{"get_bytes":83934,"get_count":128,"put_bytes":87435,"put_count":147}} +{"i":47,"series":1,"stats":{"get_bytes":83670,"get_count":128,"put_bytes":87037,"put_count":145}} +{"i":48,"series":1,"stats":{"get_bytes":87269,"get_count":125,"put_bytes":90433,"put_count":139}} +{"i":49,"series":1,"stats":{"get_bytes":85173,"get_count":127,"put_bytes":88609,"put_count":145}} +{"i":50,"series":1,"stats":{"get_bytes":81722,"get_count":117,"put_bytes":84875,"put_count":131}} +{"i":51,"series":1,"stats":{"get_bytes":85407,"get_count":119,"put_bytes":88493,"put_count":132}} +{"i":52,"series":1,"stats":{"get_bytes":86191,"get_count":128,"put_bytes":89620,"put_count":146}} +{"i":53,"series":1,"stats":{"get_bytes":82934,"get_count":122,"put_bytes":85804,"put_count":132}} +{"i":54,"series":1,"stats":{"get_bytes":87898,"get_count":130,"put_bytes":91198,"put_count":146}} +{"i":55,"series":1,"stats":{"get_bytes":85331,"get_count":120,"put_bytes":88390,"put_count":133}} +{"i":56,"series":1,"stats":{"get_bytes":85790,"get_count":127,"put_bytes":89016,"put_count":142}} +{"i":57,"series":1,"stats":{"get_bytes":90094,"get_count":132,"put_bytes":93027,"put_count":143}} +{"i":58,"series":1,"stats":{"get_bytes":87428,"get_count":129,"put_bytes":90637,"put_count":144}} +{"i":59,"series":1,"stats":{"get_bytes":89759,"get_count":135,"put_bytes":92486,"put_count":143}} +{"i":60,"series":1,"stats":{"get_bytes":88269,"get_count":129,"put_bytes":91563,"put_count":145}} +{"i":61,"series":1,"stats":{"get_bytes":92827,"get_count":131,"put_bytes":95980,"put_count":145}} +{"i":62,"series":1,"stats":{"get_bytes":85548,"get_count":127,"put_bytes":88502,"put_count":138}} +{"i":63,"series":1,"stats":{"get_bytes":93830,"get_count":134,"put_bytes":97066,"put_count":150}} +{"i":64,"series":1,"stats":{"get_bytes":93699,"get_count":131,"put_bytes":96929,"put_count":146}} +{"i":65,"series":1,"stats":{"get_bytes":86404,"get_count":126,"put_bytes":89838,"put_count":144}} +{"i":66,"series":1,"stats":{"get_bytes":92235,"get_count":128,"put_bytes":95736,"put_count":147}} +{"i":67,"series":1,"stats":{"get_bytes":92992,"get_count":134,"put_bytes":95936,"put_count":145}} +{"i":68,"series":1,"stats":{"get_bytes":94573,"get_count":130,"put_bytes":97727,"put_count":144}} +{"i":69,"series":1,"stats":{"get_bytes":94357,"get_count":131,"put_bytes":97644,"put_count":147}} +{"i":70,"series":1,"stats":{"get_bytes":95457,"get_count":131,"put_bytes":98879,"put_count":149}} +{"i":71,"series":1,"stats":{"get_bytes":91970,"get_count":126,"put_bytes":95472,"put_count":145}} +{"i":72,"series":1,"stats":{"get_bytes":91962,"get_count":131,"put_bytes":95252,"put_count":147}} +{"i":73,"series":1,"stats":{"get_bytes":93561,"get_count":132,"put_bytes":96426,"put_count":142}} +{"i":74,"series":1,"stats":{"get_bytes":90721,"get_count":127,"put_bytes":94084,"put_count":144}} +{"i":75,"series":1,"stats":{"get_bytes":97983,"get_count":134,"put_bytes":101417,"put_count":152}} +{"i":76,"series":1,"stats":{"get_bytes":96189,"get_count":131,"put_bytes":99482,"put_count":147}} +{"i":77,"series":1,"stats":{"get_bytes":93966,"get_count":132,"put_bytes":96697,"put_count":140}} +{"i":78,"series":1,"stats":{"get_bytes":95494,"get_count":130,"put_bytes":98573,"put_count":143}} +{"i":79,"series":1,"stats":{"get_bytes":95391,"get_count":132,"put_bytes":99173,"put_count":155}} +{"i":80,"series":1,"stats":{"get_bytes":96245,"get_count":133,"put_bytes":99672,"put_count":151}} +{"i":81,"series":1,"stats":{"get_bytes":99826,"get_count":138,"put_bytes":103254,"put_count":156}} +{"i":82,"series":1,"stats":{"get_bytes":95948,"get_count":127,"put_bytes":99801,"put_count":151}} +{"i":83,"series":1,"stats":{"get_bytes":92832,"get_count":123,"put_bytes":95982,"put_count":137}} +{"i":84,"series":1,"stats":{"get_bytes":102241,"get_count":134,"put_bytes":105731,"put_count":153}} +{"i":85,"series":1,"stats":{"get_bytes":97208,"get_count":129,"put_bytes":100286,"put_count":142}} +{"i":86,"series":1,"stats":{"get_bytes":99565,"get_count":131,"put_bytes":102718,"put_count":145}} +{"i":87,"series":1,"stats":{"get_bytes":97476,"get_count":129,"put_bytes":100907,"put_count":147}} +{"i":88,"series":1,"stats":{"get_bytes":98374,"get_count":131,"put_bytes":102218,"put_count":155}} +{"i":89,"series":1,"stats":{"get_bytes":101937,"get_count":133,"put_bytes":105850,"put_count":158}} +{"i":90,"series":1,"stats":{"get_bytes":105193,"get_count":135,"put_bytes":108904,"put_count":157}} +{"i":91,"series":1,"stats":{"get_bytes":99039,"get_count":135,"put_bytes":102539,"put_count":154}} +{"i":92,"series":1,"stats":{"get_bytes":105099,"get_count":132,"put_bytes":108950,"put_count":156}} +{"i":93,"series":1,"stats":{"get_bytes":99293,"get_count":130,"put_bytes":103703,"put_count":162}} +{"i":94,"series":1,"stats":{"get_bytes":102330,"get_count":130,"put_bytes":105891,"put_count":150}} +{"i":95,"series":1,"stats":{"get_bytes":102507,"get_count":132,"put_bytes":106362,"put_count":156}} +{"i":96,"series":1,"stats":{"get_bytes":99989,"get_count":134,"put_bytes":103067,"put_count":147}} +{"i":97,"series":1,"stats":{"get_bytes":101057,"get_count":135,"put_bytes":104775,"put_count":157}} +{"i":98,"series":1,"stats":{"get_bytes":99945,"get_count":133,"put_bytes":103438,"put_count":152}} +{"i":99,"series":1,"stats":{"get_bytes":103381,"get_count":135,"put_bytes":106743,"put_count":152}} +{"i":0,"series":2,"stats":{"get_bytes":106354,"get_count":140,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":104221,"get_count":131,"put_bytes":107858,"put_count":152}} +{"i":2,"series":2,"stats":{"get_bytes":103596,"get_count":132,"put_bytes":107583,"put_count":158}} +{"i":3,"series":2,"stats":{"get_bytes":105513,"get_count":133,"put_bytes":109578,"put_count":160}} +{"i":4,"series":2,"stats":{"get_bytes":99403,"get_count":130,"put_bytes":103319,"put_count":155}} +{"i":5,"series":2,"stats":{"get_bytes":104909,"get_count":131,"put_bytes":108970,"put_count":158}} +{"i":6,"series":2,"stats":{"get_bytes":108433,"get_count":133,"put_bytes":112359,"put_count":158}} +{"i":7,"series":2,"stats":{"get_bytes":108339,"get_count":134,"put_bytes":112407,"put_count":161}} +{"i":8,"series":2,"stats":{"get_bytes":110855,"get_count":138,"put_bytes":114774,"put_count":163}} +{"i":9,"series":2,"stats":{"get_bytes":109484,"get_count":134,"put_bytes":113265,"put_count":157}} +{"i":10,"series":2,"stats":{"get_bytes":105188,"get_count":131,"put_bytes":108968,"put_count":154}} +{"i":11,"series":2,"stats":{"get_bytes":108509,"get_count":137,"put_bytes":112078,"put_count":157}} +{"i":12,"series":2,"stats":{"get_bytes":106198,"get_count":130,"put_bytes":110033,"put_count":154}} +{"i":13,"series":2,"stats":{"get_bytes":105970,"get_count":132,"put_bytes":109824,"put_count":156}} +{"i":14,"series":2,"stats":{"get_bytes":110075,"get_count":135,"put_bytes":114119,"put_count":162}} +{"i":15,"series":2,"stats":{"get_bytes":97965,"get_count":127,"put_bytes":101321,"put_count":144}} +{"i":16,"series":2,"stats":{"get_bytes":109673,"get_count":134,"put_bytes":113391,"put_count":156}} +{"i":17,"series":2,"stats":{"get_bytes":111050,"get_count":136,"put_bytes":114616,"put_count":156}} +{"i":18,"series":2,"stats":{"get_bytes":111646,"get_count":135,"put_bytes":115780,"put_count":163}} +{"i":19,"series":2,"stats":{"get_bytes":108265,"get_count":129,"put_bytes":111558,"put_count":145}} +{"i":20,"series":2,"stats":{"get_bytes":108954,"get_count":132,"put_bytes":112801,"put_count":156}} +{"i":21,"series":2,"stats":{"get_bytes":115363,"get_count":139,"put_bytes":119357,"put_count":165}} +{"i":22,"series":2,"stats":{"get_bytes":112081,"get_count":135,"put_bytes":116001,"put_count":160}} +{"i":23,"series":2,"stats":{"get_bytes":109506,"get_count":131,"put_bytes":113218,"put_count":153}} +{"i":24,"series":2,"stats":{"get_bytes":111925,"get_count":131,"put_bytes":115907,"put_count":157}} +{"i":25,"series":2,"stats":{"get_bytes":113045,"get_count":129,"put_bytes":116875,"put_count":153}} +{"i":26,"series":2,"stats":{"get_bytes":115094,"get_count":139,"put_bytes":118600,"put_count":158}} +{"i":27,"series":2,"stats":{"get_bytes":116308,"get_count":138,"put_bytes":120065,"put_count":161}} +{"i":28,"series":2,"stats":{"get_bytes":114433,"get_count":135,"put_bytes":118693,"put_count":165}} +{"i":29,"series":2,"stats":{"get_bytes":108070,"get_count":133,"put_bytes":111578,"put_count":152}} +{"i":30,"series":2,"stats":{"get_bytes":112852,"get_count":136,"put_bytes":116984,"put_count":164}} +{"i":31,"series":2,"stats":{"get_bytes":115610,"get_count":137,"put_bytes":119808,"put_count":166}} +{"i":32,"series":2,"stats":{"get_bytes":116421,"get_count":139,"put_bytes":120900,"put_count":172}} +{"i":33,"series":2,"stats":{"get_bytes":109847,"get_count":131,"put_bytes":114105,"put_count":161}} +{"i":34,"series":2,"stats":{"get_bytes":115985,"get_count":136,"put_bytes":119969,"put_count":162}} +{"i":35,"series":2,"stats":{"get_bytes":117281,"get_count":133,"put_bytes":121323,"put_count":160}} +{"i":36,"series":2,"stats":{"get_bytes":116517,"get_count":134,"put_bytes":121211,"put_count":170}} +{"i":37,"series":2,"stats":{"get_bytes":114892,"get_count":132,"put_bytes":118816,"put_count":157}} +{"i":38,"series":2,"stats":{"get_bytes":112874,"get_count":131,"put_bytes":117505,"put_count":166}} +{"i":39,"series":2,"stats":{"get_bytes":110426,"get_count":131,"put_bytes":113988,"put_count":151}} +{"i":40,"series":2,"stats":{"get_bytes":111459,"get_count":131,"put_bytes":115515,"put_count":158}} +{"i":41,"series":2,"stats":{"get_bytes":115826,"get_count":143,"put_bytes":119675,"put_count":167}} +{"i":42,"series":2,"stats":{"get_bytes":110759,"get_count":127,"put_bytes":115032,"put_count":157}} +{"i":43,"series":2,"stats":{"get_bytes":117790,"get_count":131,"put_bytes":121352,"put_count":151}} +{"i":44,"series":2,"stats":{"get_bytes":117816,"get_count":137,"put_bytes":121598,"put_count":160}} +{"i":45,"series":2,"stats":{"get_bytes":121470,"get_count":142,"put_bytes":125612,"put_count":170}} +{"i":46,"series":2,"stats":{"get_bytes":120303,"get_count":140,"put_bytes":123938,"put_count":161}} +{"i":47,"series":2,"stats":{"get_bytes":118565,"get_count":135,"put_bytes":123042,"put_count":168}} +{"i":48,"series":2,"stats":{"get_bytes":118586,"get_count":137,"put_bytes":123068,"put_count":170}} +{"i":49,"series":2,"stats":{"get_bytes":119878,"get_count":136,"put_bytes":123934,"put_count":163}} +{"i":50,"series":2,"stats":{"get_bytes":117533,"get_count":136,"put_bytes":121318,"put_count":159}} +{"i":51,"series":2,"stats":{"get_bytes":120197,"get_count":139,"put_bytes":123551,"put_count":156}} +{"i":52,"series":2,"stats":{"get_bytes":122313,"get_count":144,"put_bytes":126789,"put_count":177}} +{"i":53,"series":2,"stats":{"get_bytes":115917,"get_count":129,"put_bytes":119902,"put_count":155}} +{"i":54,"series":2,"stats":{"get_bytes":118537,"get_count":142,"put_bytes":122674,"put_count":170}} +{"i":55,"series":2,"stats":{"get_bytes":118148,"get_count":130,"put_bytes":122136,"put_count":156}} +{"i":56,"series":2,"stats":{"get_bytes":120482,"get_count":140,"put_bytes":124891,"put_count":172}} +{"i":57,"series":2,"stats":{"get_bytes":120443,"get_count":139,"put_bytes":124692,"put_count":169}} +{"i":58,"series":2,"stats":{"get_bytes":126946,"get_count":139,"put_bytes":131503,"put_count":173}} +{"i":59,"series":2,"stats":{"get_bytes":125846,"get_count":133,"put_bytes":130949,"put_count":175}} +{"i":60,"series":2,"stats":{"get_bytes":127280,"get_count":136,"put_bytes":131895,"put_count":171}} +{"i":61,"series":2,"stats":{"get_bytes":126739,"get_count":134,"put_bytes":131580,"put_count":172}} +{"i":62,"series":2,"stats":{"get_bytes":120493,"get_count":131,"put_bytes":124839,"put_count":162}} +{"i":63,"series":2,"stats":{"get_bytes":118109,"get_count":138,"put_bytes":122646,"put_count":172}} +{"i":64,"series":2,"stats":{"get_bytes":124797,"get_count":139,"put_bytes":129207,"put_count":171}} +{"i":65,"series":2,"stats":{"get_bytes":125598,"get_count":137,"put_bytes":130078,"put_count":170}} +{"i":66,"series":2,"stats":{"get_bytes":125445,"get_count":138,"put_bytes":129652,"put_count":167}} +{"i":67,"series":2,"stats":{"get_bytes":127862,"get_count":144,"put_bytes":131989,"put_count":172}} +{"i":68,"series":2,"stats":{"get_bytes":128331,"get_count":140,"put_bytes":132591,"put_count":170}} +{"i":69,"series":2,"stats":{"get_bytes":124626,"get_count":140,"put_bytes":129167,"put_count":174}} +{"i":70,"series":2,"stats":{"get_bytes":128458,"get_count":142,"put_bytes":132236,"put_count":165}} +{"i":71,"series":2,"stats":{"get_bytes":125440,"get_count":134,"put_bytes":129705,"put_count":164}} +{"i":72,"series":2,"stats":{"get_bytes":123938,"get_count":135,"put_bytes":128209,"put_count":165}} +{"i":73,"series":2,"stats":{"get_bytes":125452,"get_count":140,"put_bytes":130356,"put_count":179}} +{"i":74,"series":2,"stats":{"get_bytes":122900,"get_count":134,"put_bytes":127386,"put_count":167}} +{"i":75,"series":2,"stats":{"get_bytes":124531,"get_count":141,"put_bytes":128590,"put_count":168}} +{"i":76,"series":2,"stats":{"get_bytes":126905,"get_count":142,"put_bytes":131531,"put_count":177}} +{"i":77,"series":2,"stats":{"get_bytes":131302,"get_count":135,"put_bytes":135771,"put_count":168}} +{"i":78,"series":2,"stats":{"get_bytes":129288,"get_count":140,"put_bytes":133639,"put_count":171}} +{"i":79,"series":2,"stats":{"get_bytes":128851,"get_count":134,"put_bytes":133484,"put_count":169}} +{"i":80,"series":2,"stats":{"get_bytes":131312,"get_count":143,"put_bytes":135996,"put_count":179}} +{"i":81,"series":2,"stats":{"get_bytes":125547,"get_count":137,"put_bytes":129754,"put_count":166}} +{"i":82,"series":2,"stats":{"get_bytes":132582,"get_count":145,"put_bytes":136924,"put_count":176}} +{"i":83,"series":2,"stats":{"get_bytes":129142,"get_count":136,"put_bytes":134051,"put_count":175}} +{"i":84,"series":2,"stats":{"get_bytes":128183,"get_count":136,"put_bytes":132366,"put_count":165}} +{"i":85,"series":2,"stats":{"get_bytes":128728,"get_count":137,"put_bytes":133211,"put_count":170}} +{"i":86,"series":2,"stats":{"get_bytes":126070,"get_count":142,"put_bytes":130700,"put_count":177}} +{"i":87,"series":2,"stats":{"get_bytes":134393,"get_count":140,"put_bytes":138953,"put_count":174}} +{"i":88,"series":2,"stats":{"get_bytes":128716,"get_count":138,"put_bytes":133120,"put_count":170}} +{"i":89,"series":2,"stats":{"get_bytes":126195,"get_count":140,"put_bytes":130264,"put_count":167}} +{"i":90,"series":2,"stats":{"get_bytes":128001,"get_count":141,"put_bytes":132061,"put_count":168}} +{"i":91,"series":2,"stats":{"get_bytes":134350,"get_count":143,"put_bytes":138361,"put_count":170}} +{"i":92,"series":2,"stats":{"get_bytes":128806,"get_count":140,"put_bytes":133127,"put_count":171}} +{"i":93,"series":2,"stats":{"get_bytes":129929,"get_count":140,"put_bytes":134796,"put_count":179}} +{"i":94,"series":2,"stats":{"get_bytes":132486,"get_count":143,"put_bytes":136685,"put_count":172}} +{"i":95,"series":2,"stats":{"get_bytes":129873,"get_count":142,"put_bytes":134071,"put_count":171}} +{"i":96,"series":2,"stats":{"get_bytes":133940,"get_count":142,"put_bytes":138473,"put_count":176}} +{"i":97,"series":2,"stats":{"get_bytes":133507,"get_count":141,"put_bytes":138065,"put_count":175}} +{"i":98,"series":2,"stats":{"get_bytes":136882,"get_count":146,"put_bytes":140737,"put_count":170}} +{"i":99,"series":2,"stats":{"get_bytes":126537,"get_count":139,"put_bytes":131018,"put_count":172}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index 364ec85e6..b7a027021 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":13248,"get_count":20,"put_bytes":11240,"put_count":19}} -{"i":1,"series":1,"stats":{"get_bytes":14166,"get_count":15,"put_bytes":12158,"put_count":14}} -{"i":2,"series":1,"stats":{"get_bytes":12471,"get_count":14,"put_bytes":10463,"put_count":13}} -{"i":3,"series":1,"stats":{"get_bytes":14055,"get_count":20,"put_bytes":12047,"put_count":19}} -{"i":4,"series":1,"stats":{"get_bytes":13391,"get_count":18,"put_bytes":11383,"put_count":17}} -{"i":5,"series":1,"stats":{"get_bytes":14219,"get_count":16,"put_bytes":12211,"put_count":15}} -{"i":6,"series":1,"stats":{"get_bytes":14427,"get_count":20,"put_bytes":12419,"put_count":19}} -{"i":7,"series":1,"stats":{"get_bytes":14085,"get_count":17,"put_bytes":12077,"put_count":16}} -{"i":8,"series":1,"stats":{"get_bytes":14374,"get_count":21,"put_bytes":12366,"put_count":20}} -{"i":9,"series":1,"stats":{"get_bytes":13769,"get_count":16,"put_bytes":11761,"put_count":15}} -{"i":10,"series":1,"stats":{"get_bytes":15264,"get_count":20,"put_bytes":13256,"put_count":19}} -{"i":11,"series":1,"stats":{"get_bytes":12715,"get_count":14,"put_bytes":10707,"put_count":13}} -{"i":12,"series":1,"stats":{"get_bytes":13207,"get_count":18,"put_bytes":11199,"put_count":17}} -{"i":13,"series":1,"stats":{"get_bytes":14756,"get_count":18,"put_bytes":12748,"put_count":17}} -{"i":14,"series":1,"stats":{"get_bytes":14043,"get_count":18,"put_bytes":12035,"put_count":17}} -{"i":15,"series":1,"stats":{"get_bytes":13313,"get_count":17,"put_bytes":11305,"put_count":16}} -{"i":16,"series":1,"stats":{"get_bytes":11402,"get_count":13,"put_bytes":9394,"put_count":12}} -{"i":17,"series":1,"stats":{"get_bytes":12267,"get_count":17,"put_bytes":10259,"put_count":16}} -{"i":18,"series":1,"stats":{"get_bytes":15021,"get_count":18,"put_bytes":13013,"put_count":17}} -{"i":19,"series":1,"stats":{"get_bytes":13961,"get_count":19,"put_bytes":11953,"put_count":18}} -{"i":20,"series":1,"stats":{"get_bytes":14580,"get_count":20,"put_bytes":12572,"put_count":19}} -{"i":21,"series":1,"stats":{"get_bytes":13048,"get_count":17,"put_bytes":11040,"put_count":16}} -{"i":22,"series":1,"stats":{"get_bytes":15172,"get_count":19,"put_bytes":13164,"put_count":18}} -{"i":23,"series":1,"stats":{"get_bytes":12384,"get_count":19,"put_bytes":10376,"put_count":18}} -{"i":24,"series":1,"stats":{"get_bytes":14164,"get_count":18,"put_bytes":12156,"put_count":17}} -{"i":25,"series":1,"stats":{"get_bytes":14427,"get_count":18,"put_bytes":12419,"put_count":17}} -{"i":26,"series":1,"stats":{"get_bytes":13538,"get_count":18,"put_bytes":11530,"put_count":17}} -{"i":27,"series":1,"stats":{"get_bytes":12967,"get_count":14,"put_bytes":10959,"put_count":13}} -{"i":28,"series":1,"stats":{"get_bytes":14742,"get_count":19,"put_bytes":12734,"put_count":18}} -{"i":29,"series":1,"stats":{"get_bytes":14405,"get_count":18,"put_bytes":12397,"put_count":17}} -{"i":30,"series":1,"stats":{"get_bytes":15307,"get_count":21,"put_bytes":13299,"put_count":20}} -{"i":31,"series":1,"stats":{"get_bytes":13285,"get_count":16,"put_bytes":11277,"put_count":15}} -{"i":32,"series":1,"stats":{"get_bytes":14604,"get_count":19,"put_bytes":12596,"put_count":18}} -{"i":33,"series":1,"stats":{"get_bytes":13969,"get_count":19,"put_bytes":11961,"put_count":18}} -{"i":34,"series":1,"stats":{"get_bytes":13449,"get_count":15,"put_bytes":11441,"put_count":14}} -{"i":35,"series":1,"stats":{"get_bytes":14075,"get_count":19,"put_bytes":12067,"put_count":18}} -{"i":36,"series":1,"stats":{"get_bytes":15110,"get_count":19,"put_bytes":13102,"put_count":18}} -{"i":37,"series":1,"stats":{"get_bytes":12949,"get_count":16,"put_bytes":10941,"put_count":15}} -{"i":38,"series":1,"stats":{"get_bytes":12287,"get_count":16,"put_bytes":10279,"put_count":15}} -{"i":39,"series":1,"stats":{"get_bytes":14145,"get_count":18,"put_bytes":12137,"put_count":17}} -{"i":40,"series":1,"stats":{"get_bytes":13898,"get_count":22,"put_bytes":11890,"put_count":21}} -{"i":41,"series":1,"stats":{"get_bytes":15709,"get_count":20,"put_bytes":13701,"put_count":19}} -{"i":42,"series":1,"stats":{"get_bytes":14389,"get_count":20,"put_bytes":12381,"put_count":19}} -{"i":43,"series":1,"stats":{"get_bytes":14626,"get_count":17,"put_bytes":12618,"put_count":16}} -{"i":44,"series":1,"stats":{"get_bytes":15453,"get_count":21,"put_bytes":13445,"put_count":20}} -{"i":45,"series":1,"stats":{"get_bytes":12916,"get_count":17,"put_bytes":10908,"put_count":16}} -{"i":46,"series":1,"stats":{"get_bytes":14236,"get_count":18,"put_bytes":12228,"put_count":17}} -{"i":47,"series":1,"stats":{"get_bytes":15170,"get_count":19,"put_bytes":13162,"put_count":18}} -{"i":48,"series":1,"stats":{"get_bytes":14435,"get_count":20,"put_bytes":12427,"put_count":19}} -{"i":49,"series":1,"stats":{"get_bytes":13919,"get_count":20,"put_bytes":11911,"put_count":19}} -{"i":50,"series":1,"stats":{"get_bytes":14948,"get_count":19,"put_bytes":12940,"put_count":18}} -{"i":51,"series":1,"stats":{"get_bytes":12795,"get_count":15,"put_bytes":10787,"put_count":14}} -{"i":52,"series":1,"stats":{"get_bytes":13070,"get_count":17,"put_bytes":11062,"put_count":16}} -{"i":53,"series":1,"stats":{"get_bytes":14447,"get_count":18,"put_bytes":12439,"put_count":17}} -{"i":54,"series":1,"stats":{"get_bytes":14702,"get_count":22,"put_bytes":12694,"put_count":21}} -{"i":55,"series":1,"stats":{"get_bytes":12293,"get_count":17,"put_bytes":10285,"put_count":16}} -{"i":56,"series":1,"stats":{"get_bytes":13338,"get_count":17,"put_bytes":11330,"put_count":16}} -{"i":57,"series":1,"stats":{"get_bytes":15032,"get_count":17,"put_bytes":13024,"put_count":16}} -{"i":58,"series":1,"stats":{"get_bytes":15334,"get_count":21,"put_bytes":13326,"put_count":20}} -{"i":59,"series":1,"stats":{"get_bytes":14464,"get_count":16,"put_bytes":12456,"put_count":15}} -{"i":60,"series":1,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":12432,"put_count":17}} -{"i":61,"series":1,"stats":{"get_bytes":15888,"get_count":23,"put_bytes":13880,"put_count":22}} -{"i":62,"series":1,"stats":{"get_bytes":14141,"get_count":18,"put_bytes":12133,"put_count":17}} -{"i":63,"series":1,"stats":{"get_bytes":15211,"get_count":19,"put_bytes":13203,"put_count":18}} -{"i":64,"series":1,"stats":{"get_bytes":13529,"get_count":19,"put_bytes":11521,"put_count":18}} -{"i":65,"series":1,"stats":{"get_bytes":13852,"get_count":19,"put_bytes":11844,"put_count":18}} -{"i":66,"series":1,"stats":{"get_bytes":12444,"get_count":16,"put_bytes":10436,"put_count":15}} -{"i":67,"series":1,"stats":{"get_bytes":14376,"get_count":20,"put_bytes":12368,"put_count":19}} -{"i":68,"series":1,"stats":{"get_bytes":13957,"get_count":17,"put_bytes":11949,"put_count":16}} -{"i":69,"series":1,"stats":{"get_bytes":12351,"get_count":17,"put_bytes":10343,"put_count":16}} -{"i":70,"series":1,"stats":{"get_bytes":15353,"get_count":18,"put_bytes":13345,"put_count":17}} -{"i":71,"series":1,"stats":{"get_bytes":11626,"get_count":16,"put_bytes":9618,"put_count":15}} -{"i":72,"series":1,"stats":{"get_bytes":13773,"get_count":15,"put_bytes":11765,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":14303,"get_count":19,"put_bytes":12295,"put_count":18}} -{"i":74,"series":1,"stats":{"get_bytes":14567,"get_count":20,"put_bytes":12559,"put_count":19}} -{"i":75,"series":1,"stats":{"get_bytes":12905,"get_count":18,"put_bytes":10897,"put_count":17}} -{"i":76,"series":1,"stats":{"get_bytes":14310,"get_count":19,"put_bytes":12302,"put_count":18}} -{"i":77,"series":1,"stats":{"get_bytes":15034,"get_count":18,"put_bytes":13026,"put_count":17}} -{"i":78,"series":1,"stats":{"get_bytes":14170,"get_count":18,"put_bytes":12162,"put_count":17}} -{"i":79,"series":1,"stats":{"get_bytes":14447,"get_count":21,"put_bytes":12439,"put_count":20}} -{"i":80,"series":1,"stats":{"get_bytes":13682,"get_count":18,"put_bytes":11674,"put_count":17}} -{"i":81,"series":1,"stats":{"get_bytes":13100,"get_count":18,"put_bytes":11092,"put_count":17}} -{"i":82,"series":1,"stats":{"get_bytes":13025,"get_count":19,"put_bytes":11017,"put_count":18}} -{"i":83,"series":1,"stats":{"get_bytes":15499,"get_count":17,"put_bytes":13491,"put_count":16}} -{"i":84,"series":1,"stats":{"get_bytes":14050,"get_count":18,"put_bytes":12042,"put_count":17}} -{"i":85,"series":1,"stats":{"get_bytes":13603,"get_count":17,"put_bytes":11595,"put_count":16}} -{"i":86,"series":1,"stats":{"get_bytes":13960,"get_count":18,"put_bytes":11952,"put_count":17}} -{"i":87,"series":1,"stats":{"get_bytes":13671,"get_count":16,"put_bytes":11663,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":15119,"get_count":20,"put_bytes":13111,"put_count":19}} -{"i":89,"series":1,"stats":{"get_bytes":15317,"get_count":18,"put_bytes":13309,"put_count":17}} -{"i":90,"series":1,"stats":{"get_bytes":14640,"get_count":19,"put_bytes":12632,"put_count":18}} -{"i":91,"series":1,"stats":{"get_bytes":14622,"get_count":18,"put_bytes":12614,"put_count":17}} -{"i":92,"series":1,"stats":{"get_bytes":12906,"get_count":16,"put_bytes":10898,"put_count":15}} -{"i":93,"series":1,"stats":{"get_bytes":14418,"get_count":18,"put_bytes":12410,"put_count":17}} -{"i":94,"series":1,"stats":{"get_bytes":13103,"get_count":18,"put_bytes":11095,"put_count":17}} -{"i":95,"series":1,"stats":{"get_bytes":12847,"get_count":17,"put_bytes":10839,"put_count":16}} -{"i":96,"series":1,"stats":{"get_bytes":12886,"get_count":19,"put_bytes":10878,"put_count":18}} -{"i":97,"series":1,"stats":{"get_bytes":13142,"get_count":16,"put_bytes":11134,"put_count":15}} -{"i":98,"series":1,"stats":{"get_bytes":14204,"get_count":20,"put_bytes":12196,"put_count":19}} -{"i":99,"series":1,"stats":{"get_bytes":14098,"get_count":21,"put_bytes":12090,"put_count":20}} -{"i":0,"series":2,"stats":{"get_bytes":13248,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":14166,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":2,"series":2,"stats":{"get_bytes":12471,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":3,"series":2,"stats":{"get_bytes":14055,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":4,"series":2,"stats":{"get_bytes":13391,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":5,"series":2,"stats":{"get_bytes":14219,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":6,"series":2,"stats":{"get_bytes":14427,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":7,"series":2,"stats":{"get_bytes":14085,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":8,"series":2,"stats":{"get_bytes":14374,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":9,"series":2,"stats":{"get_bytes":13769,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":10,"series":2,"stats":{"get_bytes":15264,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":11,"series":2,"stats":{"get_bytes":12715,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":2,"stats":{"get_bytes":13207,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":13,"series":2,"stats":{"get_bytes":14756,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":14,"series":2,"stats":{"get_bytes":14043,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":15,"series":2,"stats":{"get_bytes":13313,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":16,"series":2,"stats":{"get_bytes":11402,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":17,"series":2,"stats":{"get_bytes":12267,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":18,"series":2,"stats":{"get_bytes":15021,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":19,"series":2,"stats":{"get_bytes":13961,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":20,"series":2,"stats":{"get_bytes":14580,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":21,"series":2,"stats":{"get_bytes":13048,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":22,"series":2,"stats":{"get_bytes":15172,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":23,"series":2,"stats":{"get_bytes":12384,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":24,"series":2,"stats":{"get_bytes":14164,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":25,"series":2,"stats":{"get_bytes":14427,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":26,"series":2,"stats":{"get_bytes":13538,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":27,"series":2,"stats":{"get_bytes":12967,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":28,"series":2,"stats":{"get_bytes":14742,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":29,"series":2,"stats":{"get_bytes":14405,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":30,"series":2,"stats":{"get_bytes":15307,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":31,"series":2,"stats":{"get_bytes":13285,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":32,"series":2,"stats":{"get_bytes":14604,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":33,"series":2,"stats":{"get_bytes":13969,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":34,"series":2,"stats":{"get_bytes":13449,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":35,"series":2,"stats":{"get_bytes":14075,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":36,"series":2,"stats":{"get_bytes":15110,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":37,"series":2,"stats":{"get_bytes":12949,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":38,"series":2,"stats":{"get_bytes":12287,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":39,"series":2,"stats":{"get_bytes":14145,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":40,"series":2,"stats":{"get_bytes":13898,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":41,"series":2,"stats":{"get_bytes":15709,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":42,"series":2,"stats":{"get_bytes":14389,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":43,"series":2,"stats":{"get_bytes":14626,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":44,"series":2,"stats":{"get_bytes":15453,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":45,"series":2,"stats":{"get_bytes":12916,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":46,"series":2,"stats":{"get_bytes":14236,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":47,"series":2,"stats":{"get_bytes":15170,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":48,"series":2,"stats":{"get_bytes":14435,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":49,"series":2,"stats":{"get_bytes":13919,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":50,"series":2,"stats":{"get_bytes":14948,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":51,"series":2,"stats":{"get_bytes":12795,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":52,"series":2,"stats":{"get_bytes":13070,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":53,"series":2,"stats":{"get_bytes":14447,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":54,"series":2,"stats":{"get_bytes":14702,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":55,"series":2,"stats":{"get_bytes":12293,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":56,"series":2,"stats":{"get_bytes":13338,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":57,"series":2,"stats":{"get_bytes":15032,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":58,"series":2,"stats":{"get_bytes":15334,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":59,"series":2,"stats":{"get_bytes":14464,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":60,"series":2,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":61,"series":2,"stats":{"get_bytes":15888,"get_count":23,"put_bytes":0,"put_count":0}} -{"i":62,"series":2,"stats":{"get_bytes":14141,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":63,"series":2,"stats":{"get_bytes":15211,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":64,"series":2,"stats":{"get_bytes":13529,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":65,"series":2,"stats":{"get_bytes":13852,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":66,"series":2,"stats":{"get_bytes":12444,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":67,"series":2,"stats":{"get_bytes":14376,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":68,"series":2,"stats":{"get_bytes":13957,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":69,"series":2,"stats":{"get_bytes":12351,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":70,"series":2,"stats":{"get_bytes":15353,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":71,"series":2,"stats":{"get_bytes":11626,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":72,"series":2,"stats":{"get_bytes":13773,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":73,"series":2,"stats":{"get_bytes":14303,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":74,"series":2,"stats":{"get_bytes":14567,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":75,"series":2,"stats":{"get_bytes":12905,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":76,"series":2,"stats":{"get_bytes":14310,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":77,"series":2,"stats":{"get_bytes":15034,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":78,"series":2,"stats":{"get_bytes":14170,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":79,"series":2,"stats":{"get_bytes":14447,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":80,"series":2,"stats":{"get_bytes":13682,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":81,"series":2,"stats":{"get_bytes":13100,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":82,"series":2,"stats":{"get_bytes":13025,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":83,"series":2,"stats":{"get_bytes":15499,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":84,"series":2,"stats":{"get_bytes":14050,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":85,"series":2,"stats":{"get_bytes":13603,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":86,"series":2,"stats":{"get_bytes":13960,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":87,"series":2,"stats":{"get_bytes":13671,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":88,"series":2,"stats":{"get_bytes":15119,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":89,"series":2,"stats":{"get_bytes":15317,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":90,"series":2,"stats":{"get_bytes":14640,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":91,"series":2,"stats":{"get_bytes":14622,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":92,"series":2,"stats":{"get_bytes":12906,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":93,"series":2,"stats":{"get_bytes":14418,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":94,"series":2,"stats":{"get_bytes":13103,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":95,"series":2,"stats":{"get_bytes":12847,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":96,"series":2,"stats":{"get_bytes":12886,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":97,"series":2,"stats":{"get_bytes":13142,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":98,"series":2,"stats":{"get_bytes":14204,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":99,"series":2,"stats":{"get_bytes":14098,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":13365,"get_count":20,"put_bytes":11357,"put_count":19}} +{"i":1,"series":1,"stats":{"get_bytes":14283,"get_count":15,"put_bytes":12275,"put_count":14}} +{"i":2,"series":1,"stats":{"get_bytes":12588,"get_count":14,"put_bytes":10580,"put_count":13}} +{"i":3,"series":1,"stats":{"get_bytes":14172,"get_count":20,"put_bytes":12164,"put_count":19}} +{"i":4,"series":1,"stats":{"get_bytes":13508,"get_count":18,"put_bytes":11500,"put_count":17}} +{"i":5,"series":1,"stats":{"get_bytes":14336,"get_count":16,"put_bytes":12328,"put_count":15}} +{"i":6,"series":1,"stats":{"get_bytes":14544,"get_count":20,"put_bytes":12536,"put_count":19}} +{"i":7,"series":1,"stats":{"get_bytes":14202,"get_count":17,"put_bytes":12194,"put_count":16}} +{"i":8,"series":1,"stats":{"get_bytes":14491,"get_count":21,"put_bytes":12483,"put_count":20}} +{"i":9,"series":1,"stats":{"get_bytes":13886,"get_count":16,"put_bytes":11878,"put_count":15}} +{"i":10,"series":1,"stats":{"get_bytes":15381,"get_count":20,"put_bytes":13373,"put_count":19}} +{"i":11,"series":1,"stats":{"get_bytes":12832,"get_count":14,"put_bytes":10824,"put_count":13}} +{"i":12,"series":1,"stats":{"get_bytes":13324,"get_count":18,"put_bytes":11316,"put_count":17}} +{"i":13,"series":1,"stats":{"get_bytes":14873,"get_count":18,"put_bytes":12865,"put_count":17}} +{"i":14,"series":1,"stats":{"get_bytes":14160,"get_count":18,"put_bytes":12152,"put_count":17}} +{"i":15,"series":1,"stats":{"get_bytes":13430,"get_count":17,"put_bytes":11422,"put_count":16}} +{"i":16,"series":1,"stats":{"get_bytes":11519,"get_count":13,"put_bytes":9511,"put_count":12}} +{"i":17,"series":1,"stats":{"get_bytes":12384,"get_count":17,"put_bytes":10376,"put_count":16}} +{"i":18,"series":1,"stats":{"get_bytes":15138,"get_count":18,"put_bytes":13130,"put_count":17}} +{"i":19,"series":1,"stats":{"get_bytes":14078,"get_count":19,"put_bytes":12070,"put_count":18}} +{"i":20,"series":1,"stats":{"get_bytes":14697,"get_count":20,"put_bytes":12689,"put_count":19}} +{"i":21,"series":1,"stats":{"get_bytes":13165,"get_count":17,"put_bytes":11157,"put_count":16}} +{"i":22,"series":1,"stats":{"get_bytes":15289,"get_count":19,"put_bytes":13281,"put_count":18}} +{"i":23,"series":1,"stats":{"get_bytes":12501,"get_count":19,"put_bytes":10493,"put_count":18}} +{"i":24,"series":1,"stats":{"get_bytes":14281,"get_count":18,"put_bytes":12273,"put_count":17}} +{"i":25,"series":1,"stats":{"get_bytes":14544,"get_count":18,"put_bytes":12536,"put_count":17}} +{"i":26,"series":1,"stats":{"get_bytes":13655,"get_count":18,"put_bytes":11647,"put_count":17}} +{"i":27,"series":1,"stats":{"get_bytes":13084,"get_count":14,"put_bytes":11076,"put_count":13}} +{"i":28,"series":1,"stats":{"get_bytes":14859,"get_count":19,"put_bytes":12851,"put_count":18}} +{"i":29,"series":1,"stats":{"get_bytes":14522,"get_count":18,"put_bytes":12514,"put_count":17}} +{"i":30,"series":1,"stats":{"get_bytes":15424,"get_count":21,"put_bytes":13416,"put_count":20}} +{"i":31,"series":1,"stats":{"get_bytes":13402,"get_count":16,"put_bytes":11394,"put_count":15}} +{"i":32,"series":1,"stats":{"get_bytes":14721,"get_count":19,"put_bytes":12713,"put_count":18}} +{"i":33,"series":1,"stats":{"get_bytes":14086,"get_count":19,"put_bytes":12078,"put_count":18}} +{"i":34,"series":1,"stats":{"get_bytes":13566,"get_count":15,"put_bytes":11558,"put_count":14}} +{"i":35,"series":1,"stats":{"get_bytes":14192,"get_count":19,"put_bytes":12184,"put_count":18}} +{"i":36,"series":1,"stats":{"get_bytes":15227,"get_count":19,"put_bytes":13219,"put_count":18}} +{"i":37,"series":1,"stats":{"get_bytes":13066,"get_count":16,"put_bytes":11058,"put_count":15}} +{"i":38,"series":1,"stats":{"get_bytes":12404,"get_count":16,"put_bytes":10396,"put_count":15}} +{"i":39,"series":1,"stats":{"get_bytes":14262,"get_count":18,"put_bytes":12254,"put_count":17}} +{"i":40,"series":1,"stats":{"get_bytes":14015,"get_count":22,"put_bytes":12007,"put_count":21}} +{"i":41,"series":1,"stats":{"get_bytes":15826,"get_count":20,"put_bytes":13818,"put_count":19}} +{"i":42,"series":1,"stats":{"get_bytes":14506,"get_count":20,"put_bytes":12498,"put_count":19}} +{"i":43,"series":1,"stats":{"get_bytes":14743,"get_count":17,"put_bytes":12735,"put_count":16}} +{"i":44,"series":1,"stats":{"get_bytes":15570,"get_count":21,"put_bytes":13562,"put_count":20}} +{"i":45,"series":1,"stats":{"get_bytes":13033,"get_count":17,"put_bytes":11025,"put_count":16}} +{"i":46,"series":1,"stats":{"get_bytes":14353,"get_count":18,"put_bytes":12345,"put_count":17}} +{"i":47,"series":1,"stats":{"get_bytes":15287,"get_count":19,"put_bytes":13279,"put_count":18}} +{"i":48,"series":1,"stats":{"get_bytes":14552,"get_count":20,"put_bytes":12544,"put_count":19}} +{"i":49,"series":1,"stats":{"get_bytes":14036,"get_count":20,"put_bytes":12028,"put_count":19}} +{"i":50,"series":1,"stats":{"get_bytes":15065,"get_count":19,"put_bytes":13057,"put_count":18}} +{"i":51,"series":1,"stats":{"get_bytes":12912,"get_count":15,"put_bytes":10904,"put_count":14}} +{"i":52,"series":1,"stats":{"get_bytes":13187,"get_count":17,"put_bytes":11179,"put_count":16}} +{"i":53,"series":1,"stats":{"get_bytes":14564,"get_count":18,"put_bytes":12556,"put_count":17}} +{"i":54,"series":1,"stats":{"get_bytes":14819,"get_count":22,"put_bytes":12811,"put_count":21}} +{"i":55,"series":1,"stats":{"get_bytes":12410,"get_count":17,"put_bytes":10402,"put_count":16}} +{"i":56,"series":1,"stats":{"get_bytes":13455,"get_count":17,"put_bytes":11447,"put_count":16}} +{"i":57,"series":1,"stats":{"get_bytes":15149,"get_count":17,"put_bytes":13141,"put_count":16}} +{"i":58,"series":1,"stats":{"get_bytes":15451,"get_count":21,"put_bytes":13443,"put_count":20}} +{"i":59,"series":1,"stats":{"get_bytes":14581,"get_count":16,"put_bytes":12573,"put_count":15}} +{"i":60,"series":1,"stats":{"get_bytes":14557,"get_count":18,"put_bytes":12549,"put_count":17}} +{"i":61,"series":1,"stats":{"get_bytes":16005,"get_count":23,"put_bytes":13997,"put_count":22}} +{"i":62,"series":1,"stats":{"get_bytes":14258,"get_count":18,"put_bytes":12250,"put_count":17}} +{"i":63,"series":1,"stats":{"get_bytes":15328,"get_count":19,"put_bytes":13320,"put_count":18}} +{"i":64,"series":1,"stats":{"get_bytes":13646,"get_count":19,"put_bytes":11638,"put_count":18}} +{"i":65,"series":1,"stats":{"get_bytes":13969,"get_count":19,"put_bytes":11961,"put_count":18}} +{"i":66,"series":1,"stats":{"get_bytes":12561,"get_count":16,"put_bytes":10553,"put_count":15}} +{"i":67,"series":1,"stats":{"get_bytes":14493,"get_count":20,"put_bytes":12485,"put_count":19}} +{"i":68,"series":1,"stats":{"get_bytes":14074,"get_count":17,"put_bytes":12066,"put_count":16}} +{"i":69,"series":1,"stats":{"get_bytes":12468,"get_count":17,"put_bytes":10460,"put_count":16}} +{"i":70,"series":1,"stats":{"get_bytes":15470,"get_count":18,"put_bytes":13462,"put_count":17}} +{"i":71,"series":1,"stats":{"get_bytes":11743,"get_count":16,"put_bytes":9735,"put_count":15}} +{"i":72,"series":1,"stats":{"get_bytes":13890,"get_count":15,"put_bytes":11882,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":14420,"get_count":19,"put_bytes":12412,"put_count":18}} +{"i":74,"series":1,"stats":{"get_bytes":14684,"get_count":20,"put_bytes":12676,"put_count":19}} +{"i":75,"series":1,"stats":{"get_bytes":13022,"get_count":18,"put_bytes":11014,"put_count":17}} +{"i":76,"series":1,"stats":{"get_bytes":14427,"get_count":19,"put_bytes":12419,"put_count":18}} +{"i":77,"series":1,"stats":{"get_bytes":15151,"get_count":18,"put_bytes":13143,"put_count":17}} +{"i":78,"series":1,"stats":{"get_bytes":14287,"get_count":18,"put_bytes":12279,"put_count":17}} +{"i":79,"series":1,"stats":{"get_bytes":14564,"get_count":21,"put_bytes":12556,"put_count":20}} +{"i":80,"series":1,"stats":{"get_bytes":13799,"get_count":18,"put_bytes":11791,"put_count":17}} +{"i":81,"series":1,"stats":{"get_bytes":13217,"get_count":18,"put_bytes":11209,"put_count":17}} +{"i":82,"series":1,"stats":{"get_bytes":13142,"get_count":19,"put_bytes":11134,"put_count":18}} +{"i":83,"series":1,"stats":{"get_bytes":15616,"get_count":17,"put_bytes":13608,"put_count":16}} +{"i":84,"series":1,"stats":{"get_bytes":14167,"get_count":18,"put_bytes":12159,"put_count":17}} +{"i":85,"series":1,"stats":{"get_bytes":13720,"get_count":17,"put_bytes":11712,"put_count":16}} +{"i":86,"series":1,"stats":{"get_bytes":14077,"get_count":18,"put_bytes":12069,"put_count":17}} +{"i":87,"series":1,"stats":{"get_bytes":13788,"get_count":16,"put_bytes":11780,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":15236,"get_count":20,"put_bytes":13228,"put_count":19}} +{"i":89,"series":1,"stats":{"get_bytes":15434,"get_count":18,"put_bytes":13426,"put_count":17}} +{"i":90,"series":1,"stats":{"get_bytes":14757,"get_count":19,"put_bytes":12749,"put_count":18}} +{"i":91,"series":1,"stats":{"get_bytes":14739,"get_count":18,"put_bytes":12731,"put_count":17}} +{"i":92,"series":1,"stats":{"get_bytes":13023,"get_count":16,"put_bytes":11015,"put_count":15}} +{"i":93,"series":1,"stats":{"get_bytes":14535,"get_count":18,"put_bytes":12527,"put_count":17}} +{"i":94,"series":1,"stats":{"get_bytes":13220,"get_count":18,"put_bytes":11212,"put_count":17}} +{"i":95,"series":1,"stats":{"get_bytes":12964,"get_count":17,"put_bytes":10956,"put_count":16}} +{"i":96,"series":1,"stats":{"get_bytes":13003,"get_count":19,"put_bytes":10995,"put_count":18}} +{"i":97,"series":1,"stats":{"get_bytes":13259,"get_count":16,"put_bytes":11251,"put_count":15}} +{"i":98,"series":1,"stats":{"get_bytes":14321,"get_count":20,"put_bytes":12313,"put_count":19}} +{"i":99,"series":1,"stats":{"get_bytes":14215,"get_count":21,"put_bytes":12207,"put_count":20}} +{"i":0,"series":2,"stats":{"get_bytes":13365,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":14283,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":2,"series":2,"stats":{"get_bytes":12588,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":3,"series":2,"stats":{"get_bytes":14172,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":4,"series":2,"stats":{"get_bytes":13508,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":5,"series":2,"stats":{"get_bytes":14336,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":6,"series":2,"stats":{"get_bytes":14544,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":7,"series":2,"stats":{"get_bytes":14202,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":8,"series":2,"stats":{"get_bytes":14491,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":9,"series":2,"stats":{"get_bytes":13886,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":10,"series":2,"stats":{"get_bytes":15381,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":11,"series":2,"stats":{"get_bytes":12832,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":2,"stats":{"get_bytes":13324,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":13,"series":2,"stats":{"get_bytes":14873,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":14,"series":2,"stats":{"get_bytes":14160,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":15,"series":2,"stats":{"get_bytes":13430,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":16,"series":2,"stats":{"get_bytes":11519,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":17,"series":2,"stats":{"get_bytes":12384,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":18,"series":2,"stats":{"get_bytes":15138,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":19,"series":2,"stats":{"get_bytes":14078,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":20,"series":2,"stats":{"get_bytes":14697,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":21,"series":2,"stats":{"get_bytes":13165,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":2,"stats":{"get_bytes":15289,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":23,"series":2,"stats":{"get_bytes":12501,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":24,"series":2,"stats":{"get_bytes":14281,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":25,"series":2,"stats":{"get_bytes":14544,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":26,"series":2,"stats":{"get_bytes":13655,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":27,"series":2,"stats":{"get_bytes":13084,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":28,"series":2,"stats":{"get_bytes":14859,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":29,"series":2,"stats":{"get_bytes":14522,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":30,"series":2,"stats":{"get_bytes":15424,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":31,"series":2,"stats":{"get_bytes":13402,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":32,"series":2,"stats":{"get_bytes":14721,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":33,"series":2,"stats":{"get_bytes":14086,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":34,"series":2,"stats":{"get_bytes":13566,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":35,"series":2,"stats":{"get_bytes":14192,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":36,"series":2,"stats":{"get_bytes":15227,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":37,"series":2,"stats":{"get_bytes":13066,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":38,"series":2,"stats":{"get_bytes":12404,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":2,"stats":{"get_bytes":14262,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":40,"series":2,"stats":{"get_bytes":14015,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":41,"series":2,"stats":{"get_bytes":15826,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":42,"series":2,"stats":{"get_bytes":14506,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":43,"series":2,"stats":{"get_bytes":14743,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":44,"series":2,"stats":{"get_bytes":15570,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":45,"series":2,"stats":{"get_bytes":13033,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":46,"series":2,"stats":{"get_bytes":14353,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":47,"series":2,"stats":{"get_bytes":15287,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":48,"series":2,"stats":{"get_bytes":14552,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":49,"series":2,"stats":{"get_bytes":14036,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":50,"series":2,"stats":{"get_bytes":15065,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":51,"series":2,"stats":{"get_bytes":12912,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":2,"stats":{"get_bytes":13187,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":53,"series":2,"stats":{"get_bytes":14564,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":54,"series":2,"stats":{"get_bytes":14819,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":55,"series":2,"stats":{"get_bytes":12410,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":56,"series":2,"stats":{"get_bytes":13455,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":57,"series":2,"stats":{"get_bytes":15149,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":58,"series":2,"stats":{"get_bytes":15451,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":59,"series":2,"stats":{"get_bytes":14581,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":60,"series":2,"stats":{"get_bytes":14557,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":61,"series":2,"stats":{"get_bytes":16005,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":62,"series":2,"stats":{"get_bytes":14258,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":63,"series":2,"stats":{"get_bytes":15328,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":64,"series":2,"stats":{"get_bytes":13646,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":65,"series":2,"stats":{"get_bytes":13969,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":66,"series":2,"stats":{"get_bytes":12561,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":67,"series":2,"stats":{"get_bytes":14493,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":68,"series":2,"stats":{"get_bytes":14074,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":69,"series":2,"stats":{"get_bytes":12468,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":70,"series":2,"stats":{"get_bytes":15470,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":71,"series":2,"stats":{"get_bytes":11743,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":72,"series":2,"stats":{"get_bytes":13890,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":73,"series":2,"stats":{"get_bytes":14420,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":74,"series":2,"stats":{"get_bytes":14684,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":75,"series":2,"stats":{"get_bytes":13022,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":76,"series":2,"stats":{"get_bytes":14427,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":77,"series":2,"stats":{"get_bytes":15151,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":78,"series":2,"stats":{"get_bytes":14287,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":79,"series":2,"stats":{"get_bytes":14564,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":80,"series":2,"stats":{"get_bytes":13799,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":81,"series":2,"stats":{"get_bytes":13217,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":82,"series":2,"stats":{"get_bytes":13142,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":83,"series":2,"stats":{"get_bytes":15616,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":84,"series":2,"stats":{"get_bytes":14167,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":85,"series":2,"stats":{"get_bytes":13720,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":86,"series":2,"stats":{"get_bytes":14077,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":87,"series":2,"stats":{"get_bytes":13788,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":2,"stats":{"get_bytes":15236,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":89,"series":2,"stats":{"get_bytes":15434,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":90,"series":2,"stats":{"get_bytes":14757,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":91,"series":2,"stats":{"get_bytes":14739,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":92,"series":2,"stats":{"get_bytes":13023,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":93,"series":2,"stats":{"get_bytes":14535,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":94,"series":2,"stats":{"get_bytes":13220,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":95,"series":2,"stats":{"get_bytes":12964,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":96,"series":2,"stats":{"get_bytes":13003,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":97,"series":2,"stats":{"get_bytes":13259,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":98,"series":2,"stats":{"get_bytes":14321,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":99,"series":2,"stats":{"get_bytes":14215,"get_count":21,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index ac62b105d..128049dd7 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6778,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":6543,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6799,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6846,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6737,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6840,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6543,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":6696,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6798,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6658,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6490,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6461,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":6696,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":6917,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6548,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":6928,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":6584,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6484,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6543,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6696,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6694,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":6718,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6701,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6484,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":6653,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6776,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6782,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6607,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6607,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6647,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6735,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":6689,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6689,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6794,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6548,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6881,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":6707,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6712,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6630,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6635,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6612,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6584,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6543,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6759,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6841,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6689,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":6572,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6484,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":6420,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":6589,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":6507,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":6637,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":6671,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":6566,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":6789,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":6507,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":6707,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":6502,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":6612,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":6904,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":6783,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":6566,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":6461,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":6719,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":6712,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":6676,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":6525,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":6484,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":6783,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":6548,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":6794,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":6566,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":6543,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":6612,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":6689,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":6648,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":6699,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":6584,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":6501,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":6607,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":6443,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6653,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":6614,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6895,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6916,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6963,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6854,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6957,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":6813,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6915,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6775,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6607,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":6813,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":7034,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":7045,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6813,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6811,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":6835,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6818,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":6770,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6893,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6899,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6764,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6852,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6911,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6998,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":6824,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6752,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6876,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6958,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":6689,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":6537,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":6706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":6624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":6754,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":6788,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":6906,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":6624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":6824,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":7021,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":6836,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":6793,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":6911,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":6816,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":6618,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6770,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":6731,"get_count":6,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 6015af932..2b0c8ce6e 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":111112,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":111088,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":105833,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":104453,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":110429,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":110830,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":102818,"get_count":144,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":108134,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":113816,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":109265,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":111875,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":113752,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":113889,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":107225,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":109253,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":109358,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":109578,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":108268,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":103039,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":111309,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":103603,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":106207,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":103430,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":112854,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":111783,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":108958,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":107513,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":107095,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":108351,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":106766,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":112132,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":103947,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":112404,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":107622,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":109282,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":106319,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":107416,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":108665,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":109254,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":103540,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":108010,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":107392,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":109271,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":107402,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":111354,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":110474,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":111138,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":109887,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":112598,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":110130,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":104977,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":106912,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":110300,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":105700,"get_count":143,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":110423,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":105348,"get_count":139,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":106529,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":109941,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":108707,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":111049,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":108234,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":112314,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":104046,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":112079,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":111466,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":104213,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":110061,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":109923,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":111213,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":109935,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":112583,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":106604,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":107910,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":107235,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":105796,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":112441,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":109330,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":107187,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":107939,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":108410,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":108829,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":112145,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":108559,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":103906,"get_count":142,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":113044,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":107851,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":109644,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":107437,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":108030,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":111770,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":114295,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":107352,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":114053,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":107937,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":109306,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":109947,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":106144,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":107992,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":106359,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":108904,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":111229,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":111205,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":105950,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":104570,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":110546,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":110947,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":102935,"get_count":144,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":108251,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":113933,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":109382,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":111992,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":113869,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":114006,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":107342,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":109370,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":109475,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":109695,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":108385,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":103156,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":111426,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":103720,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":106324,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":103547,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":112971,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":111900,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":109075,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":107630,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":107212,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":108468,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":106883,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":112249,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":104064,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":112521,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":107739,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":109399,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":106436,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":107533,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":108782,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":109371,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":103657,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":108127,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":107509,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":109388,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":107519,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":111471,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":110591,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":111255,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":110004,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":112715,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":110247,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":105094,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":107029,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":110417,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":105817,"get_count":143,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":110540,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":105465,"get_count":139,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":106646,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":110058,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":108824,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":111166,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":108351,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":112431,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":104163,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":112196,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":111583,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":104330,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":110178,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":110040,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":111330,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":110052,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":112700,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":106721,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":108027,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":107352,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":105913,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":112558,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":109447,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":107304,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":108056,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":108527,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":108946,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":112262,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":108676,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":104023,"get_count":142,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":113161,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":107968,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":109761,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":107554,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":108147,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":111887,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":114412,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":107469,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":114170,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":108054,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":109423,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":110064,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":106261,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":108109,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":106476,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":109021,"get_count":153,"put_bytes":0,"put_count":0}} From f40fa42a9892d614b30cd032a5534fe5530001cd Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 12 Dec 2022 18:26:14 -0800 Subject: [PATCH 203/339] Add test case for extcodehash (#941) * add some tests and be exaustive with match arms * fix test comment --- .../evm/src/interpreter/instructions/ext.rs | 6 +-- actors/evm/src/lib.rs | 3 +- actors/evm/tests/ext_opcodes.rs | 50 +++++++++++++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 12f46b9b4..2ebf78fa4 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -48,7 +48,7 @@ pub fn extcodehash( ) -> Result { let addr = match get_contract_type(system.rt, addr) { ContractType::EVM(a) => a, - // _Technically_ since we have native "bytecode" set as 0xfe this is valid, though we cant differentiate between + // _Technically_ since we have native "bytecode" set as 0xfe this is valid, though we cant differentiate between different native actors. ContractType::Native(_) => return Ok(NATIVE_BYTECODE_HASH.into()), // Precompiles "exist" and therefore aren't empty (although spec-wise they can be either 0 or keccak("") ). ContractType::Precompile => return Ok(EMPTY_EVM_HASH.into()), @@ -57,8 +57,8 @@ pub fn extcodehash( // 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(EMPTY_EVM_HASH.into()), - // Everything else is flattened to 0 - _ => return Ok(U256::zero()), + // Not found + ContractType::NotFound => return Ok(U256::zero()), }; // multihash { keccak256(bytecode) } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index e41793001..158a323aa 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -217,6 +217,7 @@ impl EvmContractActor { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; + // return value must be either keccak("") or keccak(bytecode) Ok(state.bytecode_hash) } @@ -288,7 +289,7 @@ impl ActorCode for EvmContractActor { Ok(RawBytes::serialize(cid)?) } Some(Method::GetBytecodeHash) => { - let cid = Self::bytecode(rt)?; + let cid = Self::bytecode_hash(rt)?; Ok(RawBytes::serialize(cid)?) } Some(Method::GetStorageAt) => { diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 516c16168..159dac0b5 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -3,7 +3,7 @@ mod asm; use cid::Cid; use evm::interpreter::U256; use fil_actor_evm as evm; -use fil_actors_runtime::runtime::Primitives; +use fil_actors_runtime::runtime::{Primitives, Runtime}; use fil_actors_runtime::test_utils::*; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::RawBytes; @@ -144,6 +144,7 @@ fn test_extcodehash() { %dispatch(0x00, evm_contract) %dispatch(0x01, native_actor) %dispatch(0x02, non_exist) +%dispatch(0x03, account) %dispatch_end() evm_contract: @@ -167,6 +168,13 @@ non_exist: 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() @@ -178,10 +186,16 @@ non_exist: let evm_target = FILAddress::new_id(0x88); rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); - // 0x88 is an EVM actor + // 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, *EMBRYO_ACTOR_CODE_ID); + + let empty_hash = empty_bytecode_hash(&mut rt); + // a random hash value let bytecode = b"foo bar boxy"; let bytecode_hash = Multihash::wrap( @@ -214,11 +228,41 @@ non_exist: ); rt.reset(); - // Non-existing accounts are 0 + // 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: Multihash = rt + .call::(evm::Method::GetBytecodeHash as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + assert_eq!(res.digest(), 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] From 4c1c2518995bc65a82daf6c902827715f948f81b Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Tue, 13 Dec 2022 17:12:51 -0500 Subject: [PATCH 204/339] MarketNotifyDeal (#944) Co-authored-by: zenground0 --- actors/market/src/lib.rs | 33 ++++++-- actors/market/src/types.rs | 11 +++ actors/market/tests/harness.rs | 27 ++++++- actors/market/tests/market_actor_test.rs | 75 ++++++++++++++++++- .../tests/publish_storage_deals_failures.rs | 23 +++++- runtime/src/test_utils.rs | 6 +- test_vm/src/util.rs | 70 +++++++++-------- 7 files changed, 199 insertions(+), 46 deletions(-) diff --git a/actors/market/src/lib.rs b/actors/market/src/lib.rs index f3afaa882..c2e5b1c8b 100644 --- a/actors/market/src/lib.rs +++ b/actors/market/src/lib.rs @@ -27,7 +27,7 @@ use num_derive::FromPrimitive; use num_traits::{FromPrimitive, Zero}; use crate::balance_table::BalanceTable; -use fil_actors_runtime::cbor::{deserialize, serialize, serialize_vec}; +use fil_actors_runtime::cbor::{deserialize, serialize}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime}; use fil_actors_runtime::{ @@ -231,6 +231,7 @@ impl Actor { struct ValidDeal { proposal: DealProposal, + serialized_proposal: RawBytes, cid: Cid, allocation: AllocationID, } @@ -248,7 +249,6 @@ impl Actor { let state: State = rt.state()?; for (di, mut deal) in params.deals.into_iter().enumerate() { - // drop malformed deals if let Err(e) = validate_deal(rt, &deal, &network_raw_power, &baseline_power) { info!("invalid deal {}: {}", di, e); continue; @@ -305,7 +305,9 @@ impl Actor { // Must happen after signature verification and before taking cid. deal.proposal.provider = Address::new_id(provider_id); deal.proposal.client = Address::new_id(client_id); - let pcid = rt_deal_cid(rt, &deal.proposal).map_err( + let serialized_proposal = serialize(&deal.proposal, "normalized deal proposal") + .context_code(ExitCode::USR_SERIALIZATION, "failed to serialize")?; + let pcid = rt_serialized_deal_cid(rt, &serialized_proposal).map_err( |e| actor_error!(illegal_argument; "failed to take cid of proposal {}: {}", di, e), )?; @@ -370,6 +372,7 @@ impl Actor { proposal_cid_lookup.insert(pcid); valid_deals.push(ValidDeal { proposal: deal.proposal, + serialized_proposal, cid: pcid, allocation: allocation_id, }); @@ -435,6 +438,19 @@ impl Actor { Ok(()) })?; + // notify clients ignoring any errors + for (i, valid_deal) in valid_deals.iter().enumerate() { + _ = rt.send( + &valid_deal.proposal.client, + MARKET_NOTIFY_DEAL_METHOD, + RawBytes::serialize(MarketNotifyDealParams { + proposal: valid_deal.serialized_proposal.to_vec(), + deal_id: new_deal_ids[i], + })?, + TokenAmount::zero(), + ); + } + Ok(PublishStorageDealsReturn { ids: new_deal_ids, valid_deals: valid_input_bf }) } @@ -1231,14 +1247,14 @@ fn deal_proposal_is_internally_valid( ) -> Result<(), ActorError> { let signature_bytes = proposal.client_signature.bytes.clone(); // Generate unsigned bytes - let proposal_bytes = serialize_vec(&proposal.proposal, "deal proposal")?; + let proposal_bytes = serialize(&proposal.proposal, "deal proposal")?; rt.send( &proposal.proposal.client, ext::account::AUTHENTICATE_MESSAGE_METHOD, RawBytes::serialize(ext::account::AuthenticateMessageParams { signature: signature_bytes, - message: proposal_bytes, + message: proposal_bytes.to_vec(), })?, TokenAmount::zero(), ) @@ -1250,8 +1266,13 @@ pub const DAG_CBOR: u64 = 0x71; // TODO is there a better place to get this? /// Compute a deal CID using the runtime. pub(crate) fn rt_deal_cid(rt: &impl Runtime, proposal: &DealProposal) -> Result { - const DIGEST_SIZE: u32 = 32; let data = &proposal.marshal_cbor()?; + rt_serialized_deal_cid(rt, data) +} + +/// Compute a deal CID from serialized proposal using the runtime +pub(crate) fn rt_serialized_deal_cid(rt: &impl Runtime, data: &[u8]) -> Result { + const DIGEST_SIZE: u32 = 32; let hash = MultihashGeneric::wrap(Code::Blake2b256.into(), &rt.hash_blake2b(data)) .map_err(|e| actor_error!(illegal_argument; "failed to take cid of proposal {}", e))?; debug_assert_eq!(u32::from(hash.size()), DIGEST_SIZE, "expected 32byte digest"); diff --git a/actors/market/src/types.rs b/actors/market/src/types.rs index eaa98b970..7d751c83a 100644 --- a/actors/market/src/types.rs +++ b/actors/market/src/types.rs @@ -5,6 +5,7 @@ use super::ext::verifreg::AllocationID; use cid::Cid; use fil_actors_runtime::Array; use fvm_ipld_bitfield::BitField; +use fvm_ipld_encoding::serde_bytes; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::Cbor; use fvm_shared::address::Address; @@ -216,3 +217,13 @@ pub struct GetDealActivationReturn { /// Epoch at which the deal was terminated abnormally, or -1. pub terminated: ChainEpoch, } + +// Interface market clients can implement to receive notifications from builtin market +pub const MARKET_NOTIFY_DEAL_METHOD: u64 = frc42_dispatch::method_hash!("MarketNotifyDeal"); + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct MarketNotifyDealParams { + #[serde(with = "serde_bytes")] + pub proposal: Vec, + pub deal_id: u64, +} diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 36a071c2d..1b8113f7b 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -13,10 +13,10 @@ use fil_actor_market::{ deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, gen_rand_next_epoch, testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, Actor as MarketActor, ClientDealProposal, DealArray, DealMetaArray, DealProposal, DealState, - GetBalanceReturn, Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, - PublishStorageDealsReturn, SectorDeals, State, VerifyDealsForActivationParams, - VerifyDealsForActivationReturn, WithdrawBalanceParams, WithdrawBalanceReturn, NO_ALLOCATION_ID, - PROPOSALS_AMT_BITWIDTH, + GetBalanceReturn, Label, MarketNotifyDealParams, Method, OnMinerSectorsTerminateParams, + PublishStorageDealsParams, PublishStorageDealsReturn, SectorDeals, State, + VerifyDealsForActivationParams, VerifyDealsForActivationReturn, WithdrawBalanceParams, + WithdrawBalanceReturn, MARKET_NOTIFY_DEAL_METHOD, NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, }; use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod}; use fil_actor_reward::Method as RewardMethod; @@ -439,6 +439,8 @@ pub fn publish_deals( publish_deals: &[DealProposal], next_allocation_id: AllocationID, ) -> Vec { + let st: State = rt.get_state(); + let next_deal_id = st.next_id; rt.expect_validate_caller_any(); let return_value = GetControlAddressesReturnParams { owner: addrs.owner, @@ -529,6 +531,23 @@ pub fn publish_deals( } } + let mut deal_id = next_deal_id; + for deal in publish_deals { + let buf = RawBytes::serialize(deal.clone()).expect("failed to marshal deal proposal"); + let params = + RawBytes::serialize(MarketNotifyDealParams { proposal: buf.to_vec(), deal_id }) + .unwrap(); + rt.expect_send( + deal.client, + MARKET_NOTIFY_DEAL_METHOD, + params, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); + deal_id += 1; + } + let ret: PublishStorageDealsReturn = rt .call::( Method::PublishStorageDeals as u64, diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index be3ebd637..39b6ad1bd 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -5,8 +5,9 @@ use fil_actor_market::balance_table::BALANCE_TABLE_BITWIDTH; use fil_actor_market::policy::detail::DEAL_MAX_LABEL_SIZE; use fil_actor_market::{ deal_id_key, ext, ActivateDealsParams, Actor as MarketActor, ClientDealProposal, DealArray, - DealMetaArray, Label, Method, PublishStorageDealsParams, PublishStorageDealsReturn, State, - WithdrawBalanceParams, NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, STATES_AMT_BITWIDTH, + DealMetaArray, Label, MarketNotifyDealParams, Method, PublishStorageDealsParams, + PublishStorageDealsReturn, State, WithdrawBalanceParams, MARKET_NOTIFY_DEAL_METHOD, + NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, STATES_AMT_BITWIDTH, }; use fil_actors_runtime::cbor::{deserialize, serialize}; use fil_actors_runtime::network::EPOCHS_IN_DAY; @@ -832,6 +833,8 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t expect_query_network_info(&mut rt); // create a client proposal with a valid signature + let st: State = rt.get_state(); + let deal_id = st.next_id; let mut params = PublishStorageDealsParams { deals: vec![] }; let buf = RawBytes::serialize(&deal).expect("failed to marshal deal proposal"); let sig = Signature::new_bls(buf.to_vec()); @@ -895,6 +898,24 @@ fn provider_and_client_addresses_are_resolved_before_persisting_state_and_sent_t serialize(&transfer_return, "transfer from return").unwrap(), ExitCode::OK, ); + let mut normalized_deal = deal; + normalized_deal.provider = provider_resolved; + normalized_deal.client = client_resolved; + let normalized_proposal_bytes = + RawBytes::serialize(&normalized_deal).expect("failed to marshal deal proposal"); + let notify_param = RawBytes::serialize(MarketNotifyDealParams { + proposal: normalized_proposal_bytes.to_vec(), + deal_id, + }) + .unwrap(); + rt.expect_send( + client_resolved, + MARKET_NOTIFY_DEAL_METHOD, + notify_param, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); let ret: PublishStorageDealsReturn = rt .call::( @@ -1723,6 +1744,8 @@ fn max_deal_label_size() { /// but can cover the second, then the first deal fails, but the second passes fn insufficient_client_balance_in_a_batch() { let mut rt = setup(); + let st: State = rt.get_state(); + let next_deal_id = st.next_id; let mut deal1 = generate_deal_proposal( CLIENT_ADDR, @@ -1810,6 +1833,21 @@ fn insufficient_client_balance_in_a_batch() { ExitCode::OK, ); + // only valid deals notified + let notify_param2 = RawBytes::serialize(MarketNotifyDealParams { + proposal: buf2.to_vec(), + deal_id: next_deal_id, + }) + .unwrap(); + rt.expect_send( + deal2.client, + MARKET_NOTIFY_DEAL_METHOD, + notify_param2, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); let ret: PublishStorageDealsReturn = rt @@ -1834,6 +1872,8 @@ fn insufficient_client_balance_in_a_batch() { /// but can cover the second, then the first deal fails, but the second passes fn insufficient_provider_balance_in_a_batch() { let mut rt = setup(); + let st: State = rt.get_state(); + let next_deal_id = st.next_id; let mut deal1 = generate_deal_proposal( CLIENT_ADDR, @@ -1927,6 +1967,21 @@ fn insufficient_provider_balance_in_a_batch() { ExitCode::OK, ); + // only valid deal notified + let notify_param2 = RawBytes::serialize(MarketNotifyDealParams { + proposal: buf2.to_vec(), + deal_id: next_deal_id, + }) + .unwrap(); + rt.expect_send( + deal2.client, + MARKET_NOTIFY_DEAL_METHOD, + notify_param2, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); let ret: PublishStorageDealsReturn = rt @@ -1979,6 +2034,8 @@ fn add_balance_restricted_correctly() { #[test] fn psd_restricted_correctly() { let mut rt = setup(); + let st: State = rt.get_state(); + let next_deal_id = st.next_id; let deal = generate_deal_proposal( CLIENT_ADDR, @@ -2051,6 +2108,20 @@ fn psd_restricted_correctly() { ExitCode::OK, ); + let notify_param = RawBytes::serialize(MarketNotifyDealParams { + proposal: buf.to_vec(), + deal_id: next_deal_id, + }) + .unwrap(); + rt.expect_send( + deal.client, + MARKET_NOTIFY_DEAL_METHOD, + notify_param, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); + let ret: PublishStorageDealsReturn = rt .call::( Method::PublishStorageDealsExported as MethodNum, diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index 7cdf2492b..e183d1377 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -3,8 +3,8 @@ use fil_actor_market::policy::deal_provider_collateral_bounds; use fil_actor_market::{ - Actor as MarketActor, ClientDealProposal, DealProposal, Method, PublishStorageDealsParams, - PublishStorageDealsReturn, + Actor as MarketActor, ClientDealProposal, DealProposal, MarketNotifyDealParams, Method, + PublishStorageDealsParams, PublishStorageDealsReturn, State, MARKET_NOTIFY_DEAL_METHOD, }; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; @@ -292,6 +292,9 @@ fn fail_when_deals_have_different_providers() { let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; let mut rt = setup(); + let st: State = rt.get_state(); + let next_deal_id = st.next_id; + let deal1 = generate_deal_and_add_funds( &mut rt, CLIENT_ADDR, @@ -346,6 +349,22 @@ fn fail_when_deals_have_different_providers() { ExitCode::OK, ); + // only valid deals are notified + let notify_param1 = RawBytes::serialize(MarketNotifyDealParams { + proposal: RawBytes::serialize(&deal1).expect("failed to marshal deal proposal").to_vec(), + deal_id: next_deal_id, + }) + .unwrap(); + + rt.expect_send( + deal1.client, + MARKET_NOTIFY_DEAL_METHOD, + notify_param1, + TokenAmount::zero(), + RawBytes::default(), + ExitCode::USR_UNHANDLED_MESSAGE, + ); + let psd_ret: PublishStorageDealsReturn = rt .call::( Method::PublishStorageDeals as u64, diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 3f9565565..445dd06d5 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -932,7 +932,8 @@ impl Runtime for MockRuntime { && expected_msg.value == value, "message sent does not match expectation.\n\ message - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ - expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}", + expected - to: {:?}, method: {:?}, value: {:?}, params: {:?}\n\ + method match {}, params match {}, value match {}", to, method, value, @@ -941,6 +942,9 @@ impl Runtime for MockRuntime { expected_msg.method, expected_msg.value, expected_msg.params, + expected_msg.method == method, + expected_msg.params == params, + expected_msg.value == value, ); { diff --git a/test_vm/src/util.rs b/test_vm/src/util.rs index 5eaa7ce79..407cf50fa 100644 --- a/test_vm/src/util.rs +++ b/test_vm/src/util.rs @@ -23,7 +23,7 @@ use fil_actor_market::ext::verifreg::{ }; use fil_actor_market::{ ClientDealProposal, DealProposal, Label, Method as MarketMethod, PublishStorageDealsParams, - PublishStorageDealsReturn, + PublishStorageDealsReturn, MARKET_NOTIFY_DEAL_METHOD, }; use fil_actor_miner::{ aggregate_pre_commit_network_fee, max_prove_commit_duration, @@ -1133,6 +1133,11 @@ pub fn market_publish_deal( method: AccountMethod::AuthenticateMessageExported as u64, ..Default::default() }, + ExpectInvocation { + to: deal_client, + method: MARKET_NOTIFY_DEAL_METHOD, + ..Default::default() + }, ]; if verified_deal { let deal_term = deal.end_epoch - deal.start_epoch; @@ -1151,42 +1156,45 @@ pub fn market_publish_deal( }], extensions: vec![], }; - expect_publish_invocs.push(ExpectInvocation { - to: DATACAP_TOKEN_ACTOR_ADDR, - method: DataCapMethod::TransferFromExported as u64, - params: Some( - RawBytes::serialize(&TransferFromParams { - from: deal_client, - to: VERIFIED_REGISTRY_ACTOR_ADDR, - amount: token_amount.clone(), - operator_data: RawBytes::serialize(&alloc_reqs).unwrap(), - }) - .unwrap(), - ), - code: Some(ExitCode::OK), - subinvocs: Some(vec![ExpectInvocation { - to: VERIFIED_REGISTRY_ACTOR_ADDR, - method: VerifregMethod::UniversalReceiverHook as u64, + expect_publish_invocs.insert( + expect_publish_invocs.len() - 1, + ExpectInvocation { + to: DATACAP_TOKEN_ACTOR_ADDR, + method: DataCapMethod::TransferFromExported as u64, params: Some( - RawBytes::serialize(&UniversalReceiverParams { - type_: FRC46_TOKEN_TYPE, - payload: RawBytes::serialize(&FRC46TokenReceived { - from: deal_client.id().unwrap(), - to: VERIFIED_REGISTRY_ACTOR_ADDR.id().unwrap(), - operator: STORAGE_MARKET_ACTOR_ADDR.id().unwrap(), - amount: token_amount, - operator_data: RawBytes::serialize(&alloc_reqs).unwrap(), - token_data: Default::default(), - }) - .unwrap(), + RawBytes::serialize(&TransferFromParams { + from: deal_client, + to: VERIFIED_REGISTRY_ACTOR_ADDR, + amount: token_amount.clone(), + operator_data: RawBytes::serialize(&alloc_reqs).unwrap(), }) .unwrap(), ), code: Some(ExitCode::OK), + subinvocs: Some(vec![ExpectInvocation { + to: VERIFIED_REGISTRY_ACTOR_ADDR, + method: VerifregMethod::UniversalReceiverHook as u64, + params: Some( + RawBytes::serialize(&UniversalReceiverParams { + type_: FRC46_TOKEN_TYPE, + payload: RawBytes::serialize(&FRC46TokenReceived { + from: deal_client.id().unwrap(), + to: VERIFIED_REGISTRY_ACTOR_ADDR.id().unwrap(), + operator: STORAGE_MARKET_ACTOR_ADDR.id().unwrap(), + amount: token_amount, + operator_data: RawBytes::serialize(&alloc_reqs).unwrap(), + token_data: Default::default(), + }) + .unwrap(), + }) + .unwrap(), + ), + code: Some(ExitCode::OK), + ..Default::default() + }]), ..Default::default() - }]), - ..Default::default() - }) + }, + ) } ExpectInvocation { to: STORAGE_MARKET_ACTOR_ADDR, From 16a587513d8e4a47be4e3ab4f7d697225dcb2869 Mon Sep 17 00:00:00 2001 From: Aayush Date: Sat, 10 Dec 2022 19:54:09 -0500 Subject: [PATCH 205/339] Create the Externally Owned Address actor --- Cargo.lock | 51 +++++--- Cargo.toml | 1 + actors/ethaccount/Cargo.toml | 29 +++++ actors/ethaccount/src/lib.rs | 123 ++++++++++++++++++ actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- build.rs | 1 + runtime/src/runtime/builtins.rs | 2 + src/lib.rs | 3 + state/src/check.rs | 1 + test_vm/Cargo.toml | 1 + test_vm/src/lib.rs | 14 +- 11 files changed, 203 insertions(+), 25 deletions(-) create mode 100644 actors/ethaccount/Cargo.toml create mode 100644 actors/ethaccount/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1692f59af..2d1129070 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1507,6 +1507,19 @@ dependencies = [ "fvm_shared", ] +[[package]] +name = "fil_actor_ethaccount" +version = "10.0.0-alpha.1" +dependencies = [ + "fil_actors_runtime", + "fvm_ipld_encoding", + "fvm_shared", + "hex-literal", + "num-derive", + "num-traits", + "serde", +] + [[package]] name = "fil_actor_evm" version = "10.0.0-alpha.1" @@ -1775,6 +1788,7 @@ dependencies = [ "fil_actor_cron", "fil_actor_datacap", "fil_actor_embryo", + "fil_actor_ethaccount", "fil_actor_evm", "fil_actor_init", "fil_actor_market", @@ -1982,9 +1996,9 @@ dependencies = [ [[package]] name = "futures-locks" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb42d4fb72227be5778429f9ef5240a38a358925a49f05b5cf702ce7c7e558a" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" dependencies = [ "futures-channel", "futures-task", @@ -2415,9 +2429,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", @@ -2521,9 +2535,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" [[package]] name = "itertools" @@ -2964,9 +2978,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", @@ -3000,9 +3014,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b" [[package]] name = "pbkdf2" @@ -3129,9 +3143,9 @@ dependencies = [ [[package]] name = "polling" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ "autocfg", "cfg-if", @@ -3588,9 +3602,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" dependencies = [ "serde_derive", ] @@ -3625,9 +3639,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" dependencies = [ "proc-macro2", "quote", @@ -3957,6 +3971,7 @@ dependencies = [ "fil_actor_cron", "fil_actor_datacap", "fil_actor_eam", + "fil_actor_ethaccount", "fil_actor_evm", "fil_actor_init", "fil_actor_market", @@ -4382,9 +4397,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] diff --git a/Cargo.toml b/Cargo.toml index 0a9caf00d..1615c2d24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ fil_actor_account = { version = "10.0.0-alpha.1", path = "./actors/account", fea fil_actor_cron = { version = "10.0.0-alpha.1", path = "./actors/cron", features = ["fil-actor"] } fil_actor_datacap = { version = "10.0.0-alpha.1", path = "./actors/datacap", features = ["fil-actor"] } fil_actor_embryo = { version = "10.0.0-alpha.1", path = "./actors/embryo", 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_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"] } diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml new file mode 100644 index 000000000..90aeba0ca --- /dev/null +++ b/actors/ethaccount/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "fil_actor_ethaccount" +description = "Builtin Ethereum Externally Owned Address actor for Filecoin" +version = "10.0.0-alpha.1" +license = "MIT OR Apache-2.0" +authors = ["Protocol Labs", "Filecoin Core Devs"] +edition = "2021" +repository = "https://github.com/filecoin-project/builtin-actors" +keywords = ["filecoin", "web3", "wasm", "evm"] + +[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" } +serde = { version = "1.0.136", features = ["derive"] } +fvm_ipld_encoding = "0.3.0" +fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +num-traits = "0.2.15" +num-derive = "0.3.3" +hex-literal = "0.3.4" + +[dev-dependencies] +fil_actors_runtime = { path = "../../runtime", features = ["test_utils"] } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/ethaccount/src/lib.rs b/actors/ethaccount/src/lib.rs new file mode 100644 index 000000000..97bff427d --- /dev/null +++ b/actors/ethaccount/src/lib.rs @@ -0,0 +1,123 @@ +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Payload; +use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; + +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::{ + actor_error, restrict_internal_api, ActorError, EAM_ACTOR_ID, SYSTEM_ACTOR_ADDR, +}; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(EthAccountActor); + +/// Ethereum Externally Owned Address actor methods. +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, +} + +/// Ethereum Externally Owned Address actor. +pub struct EthAccountActor; + +impl EthAccountActor { + /// Ethereum Externally Owned Address actor constructor. + pub fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + match rt.lookup_address(rt.message().receiver().id().unwrap()).map(|a| *a.payload()) { + Some(Payload::Delegated(da)) if da.namespace() == EAM_ACTOR_ID => {} + Some(_) => { + return Err(ActorError::illegal_argument( + "invalid target for EthAccount creation".to_string(), + )); + } + None => { + return Err(ActorError::illegal_argument( + "receiver must have a predictable address".to_string(), + )); + } + } + + Ok(()) + } +} + +impl ActorCode for EthAccountActor { + fn invoke_method( + rt: &mut RT, + method: MethodNum, + _params: &RawBytes, + ) -> Result + where + RT: Runtime, + { + restrict_internal_api(rt, method)?; + match FromPrimitive::from_u64(method) { + Some(Method::Constructor) => { + Self::constructor(rt)?; + Ok(RawBytes::default()) + } + None => Err(actor_error!(unhandled_message; "Invalid method")), + } + } +} + +#[cfg(test)] +mod tests { + use fil_actors_runtime::EAM_ACTOR_ID; + use fvm_ipld_encoding::RawBytes; + use fvm_shared::address::Address; + use fvm_shared::error::ExitCode; + use fvm_shared::MethodNum; + + use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, MockRuntime, SYSTEM_ACTOR_CODE_ID, + }; + use fil_actors_runtime::SYSTEM_ACTOR_ADDR; + + use crate::{EthAccountActor, Method}; + + const EOA: Address = Address::new_id(1000); + + pub fn new_runtime() -> MockRuntime { + MockRuntime { + receiver: EOA, + caller: SYSTEM_ACTOR_ADDR, + caller_type: *SYSTEM_ACTOR_CODE_ID, + ..Default::default() + } + } + + #[test] + fn construct_from_system() { + let mut rt = new_runtime(); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.add_delegated_address( + EOA, + Address::new_delegated( + EAM_ACTOR_ID, + &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"), + ) + .unwrap(), + ); + rt.call::(Method::Constructor as MethodNum, &RawBytes::default()).unwrap(); + rt.verify(); + } + + #[test] + fn no_delegated_cant_deploy() { + let mut rt = new_runtime(); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "receiver must have a predictable address", + rt.call::(Method::Constructor as MethodNum, &RawBytes::default()), + ); + rt.verify(); + } +} diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index f81f8dc90..072bf8e39 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -38,7 +38,7 @@ pub(super) fn get_actor_type( let builtin_type = match builtin_type { Some(t) => match t { - Type::Account => NativeType::Account, + Type::Account | Type::EthAccount => NativeType::Account, Type::System => NativeType::System, Type::Embryo => NativeType::Embryo, Type::EVM => NativeType::EVMContract, diff --git a/build.rs b/build.rs index d232d4aa7..fe29ab044 100644 --- a/build.rs +++ b/build.rs @@ -29,6 +29,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("embryo", "embryo"), ("evm", "evm"), ("eam", "eam"), + ("ethaccount", "ethaccount"), ]; /// Default Cargo features to activate during the build. diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index 6aab3bc7f..6febc7bdf 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -23,6 +23,7 @@ pub enum Type { Embryo = 13, EVM = 14, EAM = 15, + EthAccount = 16, } impl Type { @@ -43,6 +44,7 @@ impl Type { Type::Embryo => "embryo", Type::EVM => "evm", Type::EAM => "eam", + Type::EthAccount => "ethaccount", } } } 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 483f5705b..8443ad58f 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -223,6 +223,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( Some(Type::Embryo) => {} 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 1c572b1d8..e53716290 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -27,6 +27,7 @@ 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" bimap = { version = "0.6.2" } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 6f20bc315..a7e1ad4b1 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -7,6 +7,7 @@ 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}; @@ -616,12 +617,12 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { let is_account = match target.payload() { Payload::Secp256k1(_) | Payload::BLS(_) => true, Payload::Delegated(da) - // Validate that there's an actor at the target ID (we don't care what is there, - // just that something is there). - if self.v.get_actor(Address::new_id(da.namespace())).is_some() => - { - false - } + // Validate that there's an actor at the target ID (we don't care what is there, + // just that something is there). + if self.v.get_actor(Address::new_id(da.namespace())).is_some() => + { + false + } _ => { return Err(ActorError::unchecked( ExitCode::SYS_INVALID_RECEIVER, @@ -779,6 +780,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, ¶ms), Type::EAM => EamActor::invoke_method(self, self.msg.method, ¶ms), + Type::EthAccount => EthAccountActor::invoke_method(self, self.msg.method, ¶ms), }; if res.is_ok() && !self.caller_validated { res = Err(actor_error!(assertion_failed, "failed to validate caller")); From 90d0c0c4672b207b9c3d6167bc651526243b8544 Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Thu, 15 Dec 2022 09:30:57 +1100 Subject: [PATCH 206/339] Consolidate Init actor address mapping code (#950) --- actors/init/src/lib.rs | 6 +-- actors/init/src/state.rs | 93 ++++++++++++++-------------------------- test_vm/src/lib.rs | 2 +- 3 files changed, 36 insertions(+), 65 deletions(-) diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 15ffc8d4a..03e6c7cec 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -83,7 +83,7 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. let id_address: ActorID = rt.transaction(|s: &mut State, rt| { - s.map_address_to_new_id(rt.store(), &robust_address) + s.map_addresses_to_id(rt.store(), &robust_address, None) .context("failed to allocate ID address") })?; @@ -130,8 +130,8 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. let id_address: ActorID = rt.transaction(|s: &mut State, rt| { - s.map_address_to_f4(rt.store(), &robust_address, &delegated_address) - .context("constructor failed") + s.map_addresses_to_id(rt.store(), &robust_address, Some(&delegated_address)) + .context("failed to map addresses to ID") })?; // Create an empty actor diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index c93f4f1b3..e45b7e8c6 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -46,86 +46,57 @@ impl State { }) } - /// Allocates a new ID address and stores a mapping of the argument address to it. - /// Fails if the argument address is already present in the map to facilitate a tombstone - /// for when the predictable robust address generation is implemented. + /// Maps argument addresses to to a new or existing actor ID. + /// With no delegated address, or if the delegated address is not already mapped, + /// allocates a new ID address and maps both to it. + /// If the delegated address is already present, maps the robust address to that actor ID. + /// Fails if the robust address is already mapped, providing tombstone. /// - /// Returns the newly-allocated actor ID. - pub fn map_address_to_new_id( + /// Returns the nwe or existing actor ID. + pub fn map_addresses_to_id( &mut self, store: &BS, - addr: &Address, + robust_addr: &Address, + delegated_addr: Option<&Address>, ) -> Result { - let id = self.next_id; - self.next_id += 1; - let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; - let is_new = map - .set_if_absent(addr.to_bytes().into(), id) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?; - if !is_new { - // this is impossible today as the robust address is a hash of unique inputs - // but in close future predictable address generation will make this possible - return Err(actor_error!( - forbidden, - "robust address {} is already allocated in the address map", - addr - )); - } - self.address_map = - map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; - - Ok(id) - } - - /// Allocates a new ID address and stores a mapping of the argument addresses to it. - /// Returns the newly-allocated actor ID. - pub fn map_address_to_f4( - &mut self, - store: &BS, - addr: &Address, - f4addr: &Address, - ) -> Result - where - BS: Blockstore, - { - let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; - - // Assign a new ID address, or use the one currently mapped to the f4 address. We don't - // bother checking if the target actor is an embryo here, the FVM will check that when we go to create the actor. - let f4addr_key = f4addr.to_bytes().into(); - let id: u64 = match map - .get(&f4addr_key) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup f4 address in map")? - { - Some(id) => *id, - None => { - let id = self.next_id; + let id = if let Some(delegated_addr) = delegated_addr { + // If there's a delegated address, either recall the already-mapped actor ID or + // create and map a new one. + let delegated_key = delegated_addr.to_bytes().into(); + if let Some(existing_id) = map + .get(&delegated_key) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup delegated address")? + { + *existing_id + } else { + let new_id = self.next_id; self.next_id += 1; - map.set(f4addr_key, id) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set f4 address in map")?; - id + map.set(delegated_key, new_id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to map delegated address")?; + new_id } + } else { + // With no delegated address, always create a new actor ID. + let new_id = self.next_id; + self.next_id += 1; + new_id }; - // Then go ahead and assign the f2 address. + // Map the robust address to the ID, failing if it's already mapped to anything. let is_new = map - .set_if_absent(addr.to_bytes().into(), id) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?; + .set_if_absent(robust_addr.to_bytes().into(), id) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to map robust address")?; if !is_new { - // this is impossible today as the robust address is a hash of unique inputs - // but in close future predictable address generation will make this possible return Err(actor_error!( forbidden, "robust address {} is already allocated in the address map", - addr + robust_addr )); } self.address_map = map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; - Ok(id) } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index a7e1ad4b1..7403c6392 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -640,7 +640,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } let mut st = self.v.get_state::(INIT_ACTOR_ADDR).unwrap(); - let target_id = st.map_address_to_new_id(self.v.store, target).unwrap(); + let target_id = st.map_addresses_to_id(self.v.store, target, None).unwrap(); let target_id_addr = Address::new_id(target_id); let mut init_actor = self.v.get_actor(INIT_ACTOR_ADDR).unwrap(); init_actor.head = self.v.store.put_cbor(&st, Code::Blake2b256).unwrap(); From eb3fc0dd5a23985168f4c6ec37d48e27511a8845 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 14 Dec 2022 17:11:56 -0800 Subject: [PATCH 207/339] Replace `DIFFICULTY` with `PREVRANDAO` (#942) * wip * cache result of randomness inside of interpeter add notes about randomness values in filecoin add tests for prevrandao change evm prevrandao domain tag to 10 * review fixes * fmt --- Cargo.lock | 2 + actors/evm/Cargo.toml | 2 + actors/evm/src/interpreter/execution.rs | 2 +- .../src/interpreter/instructions/context.rs | 11 ++- .../evm/src/interpreter/instructions/mod.rs | 2 +- actors/evm/src/interpreter/system.rs | 18 ++++ actors/evm/tests/context_opcodes.rs | 97 +++++++++++++++++++ runtime/src/runtime/randomness.rs | 1 + 8 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 actors/evm/tests/context_opcodes.rs diff --git a/Cargo.lock b/Cargo.lock index 2d1129070..f69167c4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1546,6 +1546,8 @@ dependencies = [ "near-blake2", "num-derive", "num-traits", + "once_cell", + "rand", "rlp", "serde", "serde_json", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index a375fa383..669b9dc16 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -41,12 +41,14 @@ hex-literal = "0.3.4" substrate-bn = { version = "0.6.0", default-features = false } near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } lazy_static = "1.4.0" +once_cell = { version = "1.16.0", default-features = false} [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } etk-asm = "^0.2.1" ethers = { version = "0.17.0", features = ["abigen"] } serde_json = "1.0" +rand = "0.8.5" [features] fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 6d0276a5c..7f53d9354 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -141,7 +141,7 @@ pub mod opcodes { 0x41: COINBASE, 0x42: TIMESTAMP, 0x43: NUMBER, - 0x44: DIFFICULTY, + 0x44: PREVRANDAO, 0x45: GASLIMIT, 0x46: CHAINID, 0x47: SELFBALANCE, diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 939c1f783..fb003e0f1 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -106,12 +106,17 @@ pub fn block_number( Ok(U256::from(system.rt.curr_epoch())) } +/// EIP-4399: DIFFICULTY -> PREVRANDAO #[inline] -pub fn difficulty( +pub fn prevrandao( _state: &mut ExecutionState, - _system: &System, + system: &mut System, ) -> Result { - Ok(U256::zero()) + // 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 as a smart contract on current 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] diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 8badacd74..a5502e36e 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -326,7 +326,7 @@ def_stdfun! { BLOCKHASH(a) => context::blockhash } def_stdfun! { COINBASE() => context::coinbase } def_stdfun! { TIMESTAMP() => context::timestamp } def_stdfun! { NUMBER() => context::block_number } -def_stdfun! { DIFFICULTY() => context::difficulty } +def_stdfun! { PREVRANDAO() => context::prevrandao } def_stdfun! { GASLIMIT() => context::gas_limit } def_stdfun! { CHAINID() => context::chain_id } def_stdfun! { BASEFEE() => context::base_fee } diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 54409af6c..3b3e98da9 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -15,6 +15,7 @@ use fvm_shared::{ MethodNum, IPLD_RAW, }; use multihash::{Code, Multihash}; +use once_cell::unsync::OnceCell; use crate::state::State; @@ -107,6 +108,8 @@ pub struct System<'r, RT: Runtime> { saved_state_root: Option, /// Read Only context (staticcall) pub readonly: bool, + /// Randomness taken from the current epoch of chain randomness + randomness: OnceCell<[u8; 32]>, } impl<'r, RT: Runtime> System<'r, RT> { @@ -128,6 +131,7 @@ impl<'r, RT: Runtime> System<'r, RT> { saved_state_root: None, bytecode: None, readonly: read_only, + randomness: OnceCell::new(), }) } @@ -152,6 +156,7 @@ impl<'r, RT: Runtime> System<'r, RT> { saved_state_root: Some(state_root), bytecode: Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)), readonly: read_only, + randomness: OnceCell::new(), }) } @@ -346,6 +351,19 @@ impl<'r, RT: Runtime> System<'r, RT> { _ => 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], StatusCode> { + const ENTROPY: &[u8] = b"prevrandao"; + self.randomness.get_or_try_init(|| { + // get randomness from current beacon epoch with entropy of "prevrandao" + Ok(self.rt.get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + self.rt.curr_epoch(), + ENTROPY, + )?) + }) + } } pub fn load_bytecode(bs: &BS, cid: &Cid) -> Result, ActorError> { 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/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, } From f24470c461e47ca0392677a2b00d92fab99b199b Mon Sep 17 00:00:00 2001 From: Aayush Date: Sun, 11 Dec 2022 19:58:56 -0500 Subject: [PATCH 208/339] Runtime: Receive ChainID from the FVM --- Cargo.lock | 8 ++-- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/embryo/Cargo.toml | 4 +- actors/ethaccount/Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- .../src/interpreter/instructions/context.rs | 5 +-- actors/evm/tests/misc.rs | 4 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- runtime/src/runtime/chainid.rs | 37 ------------------- runtime/src/runtime/fvm.rs | 5 +++ runtime/src/runtime/mod.rs | 5 ++- runtime/src/test_utils.rs | 7 ++++ state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 5 +++ 27 files changed, 51 insertions(+), 67 deletions(-) delete mode 100644 runtime/src/runtime/chainid.rs diff --git a/Cargo.lock b/Cargo.lock index f69167c4b..6a72de360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2183,9 +2183,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.17" +version = "3.0.0-alpha.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6f88e45b7984efbc2add1c98ca24155a454a21513d4c1d9ee40a1a109d4181" +checksum = "6bd17da863e532445289d3a277db8ff7e25ffcb16c9e869ebe42acc7f50dbf07" dependencies = [ "cid 0.8.6", "fvm_ipld_encoding", @@ -2198,9 +2198,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4c82720bd5a315560cb724b4806e371fede40a203955ca17690b5b668a1db5" +checksum = "6dea3259a0e147bcd22c5670441c1cbc2452e70763bf6688fb14e96f36cee2c2" dependencies = [ "anyhow", "bitflags", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 5564c9573..696e43edb 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "1.0.0" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 2c15d2347..c5a8f3be2 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index 469cb7ab6..6fdb11924 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 55e536452..491913fe8 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml index b24f41628..b02967799 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/embryo/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.17", optional = true } -fvm_shared = { version = "3.0.0-alpha.14", optional = true } +fvm_sdk = { version = "3.0.0-alpha.18", optional = true } +fvm_shared = { version = "3.0.0-alpha.15", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 90aeba0ca..dde39807d 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "lib"] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } serde = { version = "1.0.136", features = ["derive"] } fvm_ipld_encoding = "0.3.0" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", 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 669b9dc16..31c70cfc8 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } fvm_ipld_kamt = { version = "0.1.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index fb003e0f1..0858e225e 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -2,7 +2,6 @@ use fvm_shared::clock::ChainEpoch; use { crate::interpreter::{ExecutionState, StatusCode, System, U256}, - fil_actors_runtime::runtime::chainid, fil_actors_runtime::runtime::Runtime, }; @@ -131,9 +130,9 @@ pub fn gas_limit( #[inline] pub fn chain_id( _state: &mut ExecutionState, - _system: &System, + system: &System, ) -> Result { - Ok(U256::from(chainid::CHAINID)) + Ok(U256::from_u64(system.rt.chain_id().into())) } #[inline] diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index 90888c214..219eac5cc 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -5,6 +5,7 @@ use cid::Cid; use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fvm_ipld_encoding::DAG_CBOR; +use fvm_shared::chainid::ChainID; use fvm_shared::{address::Address, econ::TokenAmount}; use multihash::Multihash; @@ -99,8 +100,9 @@ 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(31415926)); + assert_eq!(U256::from_big_endian(&result), U256::from(1989)); } #[test] diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 65dcf65c8..00ea76e15 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 4c4889633..44d806b36 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index f8dc6ade7..28e79b83c 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index cc8a1c163..11f8a3b55 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "1.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", 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 1947e5a11..5ea2238d2 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } frc42_dispatch = "1.0.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 451ead215..5f52fe21d 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } frc42_dispatch = "1.0.0" fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index d2be1bf35..35de5ef69 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 2854ade66..a8ae096e1 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index e1d378529..f9c7df9d1 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -23,7 +23,7 @@ frc46_token = "1.1.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.0" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b933e54d0..9d2c138a8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } @@ -34,7 +34,7 @@ paste = "1.0.9" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.17", optional = true } +fvm_sdk = { version = "3.0.0-alpha.18", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/runtime/src/runtime/chainid.rs b/runtime/src/runtime/chainid.rs deleted file mode 100644 index 2237366b2..000000000 --- a/runtime/src/runtime/chainid.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[cfg(feature = "mainnet")] -pub const CHAINID: u64 = 314; - -// TODO buildernet -// #[cfg(feature = "buildernet")] -// pub const CHAINID: u64 = 3141; - -#[cfg(feature = "wallaby")] -pub const CHAINID: u64 = 31415; - -#[cfg(feature = "calibrationnet")] -pub const CHAINID: u64 = 314159; - -#[cfg(any(feature = "caterpillarnet", feature = "butterflynet"))] -pub const CHAINID: u64 = 3141592; - -#[cfg(any( - feature = "devnet", - feature = "devnet-wasm", - feature = "testing", - feature = "testing-fake-proofs", -))] -pub const CHAINID: u64 = 31415926; - -// default build is same as a devnet -#[cfg(not(any( - feature = "mainnet", - feature = "wallaby", - feature = "calibrationnet", - feature = "caterpillarnet", - feature = "butterflynet", - feature = "devnet", - feature = "devnet-wasm", - feature = "testing", - feature = "testing-fake-proofs", -)))] -pub const CHAINID: u64 = 31415926; diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index e266d106a..1a4158b0d 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -6,6 +6,7 @@ use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR}; use fvm_sdk as fvm; use fvm_sdk::NO_DATA_BLOCK_ID; 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::{ @@ -118,6 +119,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; diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index c60ed9d63..03b438bc8 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::{Cbor, CborStore, RawBytes}; 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; @@ -32,7 +33,6 @@ use crate::{actor_error, ActorError}; mod actor_code; pub mod builtins; -pub mod chainid; pub mod policy; mod randomness; @@ -60,6 +60,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The current chain epoch number. 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>; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 76a1cbe8b..62f4a1216 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -13,6 +13,7 @@ use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore}; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; 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; @@ -129,6 +130,7 @@ 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 delegated_addresses_source: HashMap, @@ -325,6 +327,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(), delegated_addresses_source: Default::default(), @@ -1353,6 +1356,10 @@ impl Runtime for MockRuntime { fn read_only(&self) -> bool { false } + + fn chain_id(&self) -> ChainID { + self.chain_id + } } impl Primitives for MockRuntime { diff --git a/state/Cargo.toml b/state/Cargo.toml index 71322195a..2116bf8e0 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } fvm_ipld_encoding = "0.3.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index e53716290..5b48cf595 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -38,7 +38,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.0", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.14", default-features = false } +fvm_shared = { version = "3.0.0-alpha.15", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 7403c6392..8a351f649 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -40,6 +40,7 @@ use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; use fvm_shared::address::{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; @@ -854,6 +855,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( From 4d288f57a724f2ac5e26fb98475ea62d00a8fb23 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 14 Dec 2022 20:09:41 -0800 Subject: [PATCH 209/339] fix: reject (f4) actor redeploy after selfdestruct (#952) * fix: reject (f4) actor redeploy after selfdestruct We could also reject this in the FVM, but then we'd have to tell the FVM that we're re-assigning an existing ID address. The downside of this method is that it's _slightly_ more expensive (we have to make multiple syscalls instead of one). The benefit is that it makes auditing and testing this code _much_ simpler. Before, we were enforcing half the constraints in the FVM, and the other half in the init actor, which is why we had into this bug in the first place. fixes https://github.com/filecoin-project/ref-fvm/issues/1175 * fixes * test it * fix compile --- actors/init/src/lib.rs | 31 ++++++++++-- actors/init/src/state.rs | 14 ++--- actors/init/tests/init_actor_test.rs | 76 ++++++++++++++++++++++++++++ runtime/src/test_utils.rs | 1 + test_vm/src/lib.rs | 3 +- 5 files changed, 112 insertions(+), 13 deletions(-) diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 03e6c7cec..755008ae9 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -4,13 +4,15 @@ use std::iter; use cid::Cid; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_error, cbor, restrict_internal_api, ActorContext, ActorError, EAM_ACTOR_ADDR, - SYSTEM_ACTOR_ADDR, + actor_error, cbor, restrict_internal_api, ActorContext, ActorError, AsActorError, + EAM_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; +use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; @@ -82,11 +84,17 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. - let id_address: ActorID = rt.transaction(|s: &mut State, rt| { + let (id_address, existing): (ActorID, bool) = rt.transaction(|s: &mut State, rt| { s.map_addresses_to_id(rt.store(), &robust_address, None) .context("failed to allocate ID address") })?; + if existing { + // NOTE: this case should be impossible, but we check it anyways just in case something + // changes. + return Err(ActorError::forbidden("cannot exec over an existing actor".into())); + } + // Create an empty actor rt.create_actor(params.code_cid, id_address, None)?; @@ -129,11 +137,25 @@ impl Actor { // Allocate an ID for this actor. // Store mapping of actor addresses to the actor ID. - let id_address: ActorID = rt.transaction(|s: &mut State, rt| { + let (id_address, existing): (ActorID, bool) = rt.transaction(|s: &mut State, rt| { s.map_addresses_to_id(rt.store(), &robust_address, Some(&delegated_address)) .context("failed to map addresses to ID") })?; + // If the f4 address was already assigned, make sure we're deploying over an embryo and not + // some other existing actor (and make sure the target actor wasn't deleted either). + if existing { + let code_cid = rt + .get_actor_code_cid(&id_address) + .context_code(ExitCode::USR_FORBIDDEN, "cannot redeploy a deleted actor")?; + let embryo_cid = rt.get_code_cid_for_type(Type::Embryo); + if code_cid != embryo_cid { + return Err(ActorError::forbidden(format!( + "cannot replace an existing non-embryo actor with code: {code_cid}" + ))); + } + } + // Create an empty actor rt.create_actor(params.code_cid, id_address, Some(delegated_address))?; @@ -226,7 +248,6 @@ impl ActorCode for Actor { #[cfg(not(feature = "m2-native"))] fn can_exec(rt: &impl Runtime, caller: &Cid, exec: &Cid) -> bool { - use fil_actors_runtime::runtime::builtins::Type; rt.resolve_builtin_actor_type(exec) .map(|typ| match typ { Type::Multisig | Type::PaymentChannel => true, diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index e45b7e8c6..840d14586 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -52,16 +52,16 @@ impl State { /// If the delegated address is already present, maps the robust address to that actor ID. /// Fails if the robust address is already mapped, providing tombstone. /// - /// Returns the nwe or existing actor ID. + /// Returns the actor ID and a boolean indicating whether or not the actor already exists. pub fn map_addresses_to_id( &mut self, store: &BS, robust_addr: &Address, delegated_addr: Option<&Address>, - ) -> Result { + ) -> Result<(ActorID, bool), ActorError> { let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?; - let id = if let Some(delegated_addr) = delegated_addr { + let (id, existing) = if let Some(delegated_addr) = delegated_addr { // If there's a delegated address, either recall the already-mapped actor ID or // create and map a new one. let delegated_key = delegated_addr.to_bytes().into(); @@ -69,19 +69,19 @@ impl State { .get(&delegated_key) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup delegated address")? { - *existing_id + (*existing_id, true) } else { let new_id = self.next_id; self.next_id += 1; map.set(delegated_key, new_id) .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to map delegated address")?; - new_id + (new_id, false) } } else { // With no delegated address, always create a new actor ID. let new_id = self.next_id; self.next_id += 1; - new_id + (new_id, false) }; // Map the robust address to the ID, failing if it's already mapped to anything. @@ -97,7 +97,7 @@ impl State { } self.address_map = map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?; - Ok(id) + Ok((id, existing)) } /// ResolveAddress resolves an address to an ID-address, if possible. diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 4b0025c35..f9778c02a 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -387,6 +387,82 @@ fn call_exec4() { .flatten() .expect("failed to lookup f4 address"); assert_eq!(expected_id_addr, resolved_id, "f4 address not assigned to the right actor"); + + // Try again and expect it to fail with "forbidden". + let unique_address = Address::new_actor(b"test2"); + rt.new_actor_addr = Some(unique_address); + let exec_err = + exec4_and_verify(&mut rt, subaddr, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap_err(); + + assert_eq!(exec_err.exit_code(), ExitCode::USR_FORBIDDEN); + + // Delete and try again, it should still fail. + rt.actor_code_cids.remove(&resolved_id); + let unique_address = Address::new_actor(b"test2"); + rt.new_actor_addr = Some(unique_address); + let exec_err = + exec4_and_verify(&mut rt, subaddr, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap_err(); + + assert_eq!(exec_err.exit_code(), ExitCode::USR_FORBIDDEN); +} + +// Try turning an embryo into an f4 actor. +#[test] +fn call_exec4_embryo() { + let mut rt = construct_runtime(); + construct_and_verify(&mut rt); + + // Assign addresses + let unique_address = Address::new_actor(b"test"); + rt.new_actor_addr = Some(unique_address); + + // Make the f4 addr + let subaddr = b"foobar"; + let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); + + // Register an embryo with the init actor. + let expected_id = { + let mut state: State = rt.get_state(); + let (id, existing) = state.map_addresses_to_id(rt.store(), &f4_addr, None).unwrap(); + assert!(!existing); + rt.replace_state(&state); + id + }; + + // Register it in the state-tree. + let expected_id_addr = Address::new_id(expected_id); + rt.set_address_actor_type(expected_id_addr, *EMBRYO_ACTOR_CODE_ID); + rt.add_delegated_address(expected_id_addr, f4_addr); + + // Now try to create it. + rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, Some(f4_addr)); + + let fake_params = ConstructorParams { network_name: String::from("fake_param") }; + // Expect a send to the multisig actor constructor + rt.expect_send( + expected_id_addr, + METHOD_CONSTRUCTOR, + RawBytes::serialize(&fake_params).unwrap(), + TokenAmount::zero(), + RawBytes::default(), + ExitCode::OK, + ); + + // Return should have been successful. Check the returned addresses + let exec_ret = + exec4_and_verify(&mut rt, subaddr, *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 that we assigned the right f4 address. + let init_state: State = rt.get_state(); + let resolved_id = init_state + .resolve_address(rt.store(), &f4_addr) + .ok() + .flatten() + .expect("failed to lookup f4 address"); + assert_eq!(expected_id_addr, resolved_id, "f4 address not assigned to the right actor"); } fn construct_and_verify(rt: &mut MockRuntime) { diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 62f4a1216..d0b23e929 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1205,6 +1205,7 @@ impl Runtime for MockRuntime { ExpectCreateActor { code_id, actor_id, predictable_address }, "unexpected actor being created" ); + self.set_address_actor_type(Address::new_id(actor_id), code_id); Ok(()) } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 8a351f649..5848f8434 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -641,7 +641,8 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { } let mut st = self.v.get_state::(INIT_ACTOR_ADDR).unwrap(); - let target_id = st.map_addresses_to_id(self.v.store, target, None).unwrap(); + let (target_id, existing) = st.map_addresses_to_id(self.v.store, target, None).unwrap(); + assert!(!existing, "should never have existing actor when no f4 address is specified"); let target_id_addr = Address::new_id(target_id); let mut init_actor = self.v.get_actor(INIT_ACTOR_ADDR).unwrap(); init_actor.head = self.v.store.put_cbor(&st, Code::Blake2b256).unwrap(); From 6cba788b18beb660b812a59e6086f9e4c56b900e Mon Sep 17 00:00:00 2001 From: Alex <445306+anorth@users.noreply.github.com> Date: Fri, 16 Dec 2022 08:34:53 +1100 Subject: [PATCH 210/339] Fix caller check in EVM/EAM constructors (#949) * Fix caller check in EVM/EAM constructors --- actors/eam/src/lib.rs | 4 ++-- actors/eam/tests/create.rs | 9 ++++----- actors/evm/src/lib.rs | 8 ++------ actors/evm/tests/env.rs | 6 +++--- actors/evm/tests/util.rs | 6 ++---- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 45c69c5d0..1792719d7 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -12,7 +12,7 @@ use { actor_error, cbor, runtime::builtins::Type, runtime::{ActorCode, Runtime}, - ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, + ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }, fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}, fvm_shared::{ @@ -195,7 +195,7 @@ impl EamActor { "The Ethereum Address Manager must be deployed at {EAM_ACTOR_ID}, was deployed at {actor_id}" ))); } - rt.validate_immediate_caller_type(std::iter::once(&Type::Init)) + rt.validate_immediate_caller_is(iter::once(&SYSTEM_ACTOR_ADDR)) } /// Create a new contract per the EVM's CREATE rules. diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 5ba9d7f6a..6cc27adf3 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -3,12 +3,11 @@ use eam::{ compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, }; use fil_actor_eam as eam; -use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ - expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; -use fil_actors_runtime::INIT_ACTOR_ADDR; +use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; @@ -159,9 +158,9 @@ pub fn construct_and_verify() -> MockRuntime { let mut rt = MockRuntime { receiver: Address::new_id(10), ..Default::default() }; // construct EAM singleton actor - rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); - rt.expect_validate_caller_type(vec![Type::Init]); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let result = rt.call::(eam::Method::Constructor as u64, &RawBytes::default()).unwrap(); diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 158a323aa..a86a4e4a2 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,6 @@ use std::iter; -use fil_actors_runtime::{actor_error, runtime::builtins::Type, AsActorError, EAM_ACTOR_ID}; +use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; use fvm_shared::address::{Address, Payload}; use interpreter::{address::EthAddress, system::load_bytecode}; @@ -65,11 +65,7 @@ impl EvmContractActor { RT: Runtime, RT::Blockstore: Clone, { - // TODO ideally we would be checking that we are constructed by the EAM actor, - // but instead we check for init and then assert that we have a delegated address. - // https://github.com/filecoin-project/ref-fvm/issues/746 - // rt.validate_immediate_caller_is(vec![&EAM_ACTOR_ADDR])?; - rt.validate_immediate_caller_type(iter::once(&Type::Init))?; + rt.validate_immediate_caller_is(iter::once(&INIT_ACTOR_ADDR))?; // Assert we are constructed with a delegated address from the EAM let receiver = rt.message().receiver(); diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs index 40d4d5a37..2c02566d1 100644 --- a/actors/evm/tests/env.rs +++ b/actors/evm/tests/env.rs @@ -6,8 +6,8 @@ use ethers::{ use evm::interpreter::address::EthAddress; use fil_actor_evm as evm; use fil_actors_runtime::{ - runtime::builtins::Type, 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; @@ -48,8 +48,8 @@ impl TestEnv { initcode: hex::decode(contract_hex).unwrap().into(), }; // invoke constructor - self.runtime.expect_validate_caller_type(vec![Type::Init]); - self.runtime.caller_type = *INIT_ACTOR_CODE_ID; + 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 diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 29bf340b5..d7e3c2e12 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,9 +1,7 @@ use cid::Cid; use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; -use fil_actors_runtime::{ - runtime::builtins::Type, test_utils::*, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, -}; +use fil_actors_runtime::{test_utils::*, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; use fvm_shared::{address::Address, IDENTITY_HASH, IPLD_RAW}; use lazy_static::lazy_static; @@ -24,7 +22,7 @@ pub fn init_construct_and_verify( // construct EVM actor rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); - rt.expect_validate_caller_type(vec![Type::Init]); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); initrt(&mut rt); // first actor created is 0 From aa9ab6e4f3fcdd3ff7483a1b90c1af6268d10dfd Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 15 Dec 2022 13:45:51 -0800 Subject: [PATCH 211/339] Update note on prevrandao (#959) Update context.rs --- actors/evm/src/interpreter/instructions/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 0858e225e..323456fec 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -112,7 +112,7 @@ pub fn prevrandao( 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 as a smart contract on current state + // 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)) From 0841be4ed38424612f220f4c4495cb8300d77d74 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 15 Dec 2022 17:13:00 -0500 Subject: [PATCH 212/339] Verifreg: Call AuthenticateMessage instead of validating siggys --- actors/verifreg/src/ext.rs | 16 ++++ actors/verifreg/src/lib.rs | 56 +++++++----- test_vm/tests/verifreg_remove_datacap_test.rs | 85 ++++++++++++++++++- 3 files changed, 132 insertions(+), 25 deletions(-) diff --git a/actors/verifreg/src/ext.rs b/actors/verifreg/src/ext.rs index 1d9aa1425..4ad41a028 100644 --- a/actors/verifreg/src/ext.rs +++ b/actors/verifreg/src/ext.rs @@ -1,7 +1,23 @@ +use fvm_ipld_encoding::serde_bytes; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::tuple::{Deserialize_tuple, Serialize_tuple}; use fvm_shared::address::Address; +pub mod account { + use super::*; + + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); + + #[derive(Serialize_tuple, Deserialize_tuple)] + pub struct AuthenticateMessageParams { + #[serde(with = "serde_bytes")] + pub signature: Vec, + #[serde(with = "serde_bytes")] + pub message: Vec, + } +} + pub mod datacap { use super::*; use fvm_shared::econ::TokenAmount; diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index fa45d4b21..6bfd6a5b6 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -231,8 +231,7 @@ impl Actor { )); } - // Validate and then remove the proposal. - rt.transaction(|st: &mut State, rt| { + let (verifier_1_id, verifier_2_id) = rt.transaction(|st: &mut State, rt| { rt.validate_immediate_caller_is(std::iter::once(&st.root_key))?; if params.verified_client_to_remove == VERIFIED_REGISTRY_ACTOR_ADDR { @@ -266,27 +265,32 @@ impl Actor { let verifier_1_id = use_proposal_id(&mut proposal_ids, verifier_1, client)?; let verifier_2_id = use_proposal_id(&mut proposal_ids, verifier_2, client)?; - remove_data_cap_request_is_valid( - rt, - ¶ms.verifier_request_1, - verifier_1_id, - ¶ms.data_cap_amount_to_remove, - client, - )?; - remove_data_cap_request_is_valid( - rt, - ¶ms.verifier_request_2, - verifier_2_id, - ¶ms.data_cap_amount_to_remove, - client, - )?; - + // Assume proposal ids are valid and increment them st.remove_data_cap_proposal_ids = proposal_ids .flush() .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to flush proposal ids")?; - Ok(()) + Ok((verifier_1_id, verifier_2_id)) })?; + // Now make sure the proposals were actually valid. We had to increment them first in case + // re-entrant calls do anything funny. + // + // If this fails, we'll revert and the proposals will be restored. + remove_data_cap_request_is_valid( + rt, + ¶ms.verifier_request_1, + verifier_1_id, + ¶ms.data_cap_amount_to_remove, + client, + )?; + remove_data_cap_request_is_valid( + rt, + ¶ms.verifier_request_2, + verifier_2_id, + ¶ms.data_cap_amount_to_remove, + client, + )?; + // Burn the client's data cap tokens. let balance = balance(rt, &client).context("failed to fetch balance")?; let burnt = std::cmp::min(balance, params.data_cap_amount_to_remove); @@ -426,7 +430,8 @@ impl Actor { format!("failed to write claim {}", claim_alloc.allocation_id), )?; if !inserted { - ret_gen.add_fail(ExitCode::USR_ILLEGAL_STATE); // should be unreachable since claim and alloc can't exist at once + // should be unreachable since claim and alloc can't exist at once + ret_gen.add_fail(ExitCode::USR_ILLEGAL_STATE); info!( "claim for allocation {} could not be inserted as it already exists", claim_alloc.allocation_id, @@ -876,10 +881,17 @@ fn remove_data_cap_request_is_valid( let payload = [SIGNATURE_DOMAIN_SEPARATION_REMOVE_DATA_CAP, b.bytes()].concat(); - // verify signature of proposal - rt.verify_signature(&request.signature, &request.verifier, &payload).map_err( - |e| actor_error!(illegal_argument; "invalid signature for datacap removal request: {}", e), + rt.send( + &request.verifier, + ext::account::AUTHENTICATE_MESSAGE_METHOD, + RawBytes::serialize(ext::account::AuthenticateMessageParams { + signature: request.signature.bytes.clone(), + message: payload, + })?, + TokenAmount::zero(), ) + .map_err(|e| e.wrap("proposal authentication failed"))?; + Ok(()) } // Deserializes and validates a receiver hook payload, expecting only an FRC-46 transfer. diff --git a/test_vm/tests/verifreg_remove_datacap_test.rs b/test_vm/tests/verifreg_remove_datacap_test.rs index 98012fd35..c7d48359b 100644 --- a/test_vm/tests/verifreg_remove_datacap_test.rs +++ b/test_vm/tests/verifreg_remove_datacap_test.rs @@ -1,5 +1,7 @@ use std::ops::{Div, Sub}; +use fil_actor_account::types::AuthenticateMessageParams; +use fil_actor_account::Method as AccountMethod; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::to_vec; use fvm_shared::bigint::bigint_ser::BigIntDe; @@ -157,7 +159,12 @@ fn remove_datacap_simple_successful_path() { .deserialize() .unwrap(); - expect_remove_datacap(&remove_datacap_params).matches(v.take_invocations().last().unwrap()); + expect_remove_datacap( + &remove_datacap_params, + RemoveDataCapProposalID { id: 0 }, + RemoveDataCapProposalID { id: 0 }, + ) + .matches(v.take_invocations().last().unwrap()); assert_eq!(verified_client_id_addr, remove_datacap_ret.verified_client); assert_eq!(allowance_to_remove, remove_datacap_ret.data_cap_removed); @@ -236,7 +243,12 @@ fn remove_datacap_simple_successful_path() { .deserialize() .unwrap(); - expect_remove_datacap(&remove_datacap_params).matches(v.take_invocations().last().unwrap()); + expect_remove_datacap( + &remove_datacap_params, + RemoveDataCapProposalID { id: 1 }, + RemoveDataCapProposalID { id: 1 }, + ) + .matches(v.take_invocations().last().unwrap()); assert_eq!(verified_client_id_addr, remove_datacap_ret.verified_client); assert_eq!(allowance_to_remove, remove_datacap_ret.data_cap_removed); @@ -267,6 +279,7 @@ fn remove_datacap_simple_successful_path() { assert_eq!(2u64, verifier2_proposal_id.id); v.assert_state_invariants(); } + #[test] fn remove_datacap_fails_on_verifreg() { let store = MemoryBlockstore::new(); @@ -325,13 +338,79 @@ fn remove_datacap_fails_on_verifreg() { v.assert_state_invariants(); } -fn expect_remove_datacap(params: &RemoveDataCapParams) -> ExpectInvocation { +fn expect_remove_datacap( + params: &RemoveDataCapParams, + proposal_id1: RemoveDataCapProposalID, + proposal_id2: RemoveDataCapProposalID, +) -> ExpectInvocation { + let payload1 = [ + SIGNATURE_DOMAIN_SEPARATION_REMOVE_DATA_CAP, + serialize( + &RemoveDataCapProposal { + removal_proposal_id: proposal_id1, + data_cap_amount: params.data_cap_amount_to_remove.clone(), + verified_client: params.verified_client_to_remove, + }, + "remove datacap proposal", + ) + .unwrap() + .bytes(), + ] + .concat(); + let payload2 = [ + SIGNATURE_DOMAIN_SEPARATION_REMOVE_DATA_CAP, + serialize( + &RemoveDataCapProposal { + removal_proposal_id: proposal_id2, + data_cap_amount: params.data_cap_amount_to_remove.clone(), + verified_client: params.verified_client_to_remove, + }, + "remove datacap proposal", + ) + .unwrap() + .bytes(), + ] + .concat(); ExpectInvocation { to: VERIFIED_REGISTRY_ACTOR_ADDR, method: VerifregMethod::RemoveVerifiedClientDataCap as u64, params: Some(serialize(¶ms, "remove datacap params").unwrap()), code: Some(ExitCode::OK), subinvocs: Some(vec![ + ExpectInvocation { + to: params.verifier_request_1.verifier, + method: AccountMethod::AuthenticateMessageExported as u64, + params: Some( + serialize( + &AuthenticateMessageParams { + signature: payload1.clone(), + message: payload1, + }, + "authenticate_message params", + ) + .unwrap(), + ), + code: Some(ExitCode::OK), + subinvocs: None, + ..Default::default() + }, + ExpectInvocation { + to: params.verifier_request_2.verifier, + method: AccountMethod::AuthenticateMessageExported as u64, + params: Some( + serialize( + &AuthenticateMessageParams { + signature: payload2.clone(), + message: payload2, + }, + "authenticate_message params", + ) + .unwrap(), + ), + code: Some(ExitCode::OK), + subinvocs: None, + ..Default::default() + }, ExpectInvocation { to: DATACAP_TOKEN_ACTOR_ADDR, method: DataCapMethod::BalanceExported as u64, From a501a94223cb48087def4ce536ce137d5dbff78a Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 19 Dec 2022 18:45:44 +0000 Subject: [PATCH 213/339] EVM: Re-order address and data size params of call_actor precompile (#970) re-order address and data size params of call_actor precompile --- actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- actors/evm/tests/call.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 58509bd85..90ed87ca5 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -185,8 +185,8 @@ pub(super) fn call_actor( let codec: u64 = input_params.next_param_padded()?; - let address_size = input_params.next_param_padded::()? as usize; let send_data_size = input_params.next_param_padded::()? as usize; + let address_size = input_params.next_param_padded::()? as usize; // ------ Begin Call ------- diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 91cde737a..5d267004d 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -456,8 +456,8 @@ fn test_callactor_inner(exit_code: ExitCode) { 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(&target_size.to_bytes()); contract_params.extend_from_slice(&data_size.to_bytes()); + contract_params.extend_from_slice(&target_size.to_bytes()); contract_params.extend_from_slice(&target_bytes); contract_params.extend_from_slice(&proxy_call_input_data); From b08de0d45cbc1d3e45d121923238e9ebf6b4431f Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 19 Dec 2022 22:02:54 +0000 Subject: [PATCH 214/339] bump ethers (#976) --- Cargo.lock | 562 ++++++++++++++++++++++++++++++++++++++---- actors/evm/Cargo.toml | 2 +- 2 files changed, 514 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 350e5e85a..4ac704a65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,11 +19,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if", - "cipher", + "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" @@ -206,6 +217,18 @@ dependencies = [ "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" @@ -630,6 +653,16 @@ 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" @@ -765,6 +798,15 @@ 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" @@ -855,7 +897,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher", + "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]] @@ -1067,14 +1118,36 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f65b750ac950f2f825b36d08bef4cda4112e19a7b1a68f6e2bb499413e12440" dependencies = [ - "aes", - "ctr", + "aes 0.7.5", + "ctr 0.8.0", + "digest 0.10.6", + "hex", + "hmac", + "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", "pbkdf2 0.11.0", "rand", - "scrypt", + "scrypt 0.10.0", "serde", "serde_json", "sha2 0.10.6", @@ -1089,7 +1162,24 @@ version = "17.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4966fba78396ff92db3b817ee71143eccd98acf0f876b8d600e585a670c5d1b" dependencies = [ - "ethereum-types", + "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", @@ -1107,9 +1197,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" dependencies = [ "crunchy", - "fixed-hash", + "fixed-hash 0.7.0", "impl-rlp", - "impl-serde", + "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", ] @@ -1119,11 +1224,27 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" dependencies = [ - "ethbloom", - "fixed-hash", + "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", - "primitive-types", + "impl-serde 0.4.0", + "primitive-types 0.12.1", + "scale-info", "uint", ] @@ -1133,13 +1254,28 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16142eeb3155cfa5aec6be3f828a28513a28bd995534f945fa70e7d608f16c10" dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", + "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]] @@ -1148,7 +1284,19 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e23f8992ecf45ea9dd2983696aabc566c108723585f07f5dc8c9efb24e52d3db" dependencies = [ - "ethers-core", + "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", @@ -1160,10 +1308,29 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e0010fffc97c5abcf75a30fd75676b1ed917b2b82beb8270391333618e2847d" dependencies = [ - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", + "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", @@ -1182,16 +1349,41 @@ dependencies = [ "Inflector", "cfg-if", "dunce", - "ethers-core", + "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", ] @@ -1202,8 +1394,23 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41170ccb5950f559cba5a052158a28ec2d224af3a7d5b266b3278b929538ef55" dependencies = [ - "ethers-contract-abigen", - "ethers-core", + "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", @@ -1223,7 +1430,7 @@ dependencies = [ "chrono", "convert_case 0.5.0", "elliptic-curve", - "ethabi", + "ethabi 17.2.0", "fastrlp", "generic-array 0.14.6", "hex", @@ -1243,18 +1450,66 @@ dependencies = [ "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", + "ethers-core 0.17.0", "getrandom", "reqwest", "semver", "serde", - "serde-aux", + "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", @@ -1267,11 +1522,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e7e8632d28175352b9454bbcb604643b6ca1de4d36dc99b3f86860d75c132b" dependencies = [ "async-trait", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", + "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", @@ -1292,9 +1573,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e46482e4d1e79b20c338fd9db9e166184eb387f0a4e7c05c5b5c0aa2e8c8900c" dependencies = [ "async-trait", - "auto_impl", + "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", + "ethers-core 1.0.2", "futures-core", "futures-timer", "futures-util", @@ -1330,8 +1646,26 @@ dependencies = [ "coins-bip32", "coins-bip39", "elliptic-curve", - "eth-keystore", - "ethers-core", + "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", @@ -1391,9 +1725,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "089263294bb1c38ac73649a6ad563dd9a5142c8dc0482be15b8b9acb22a1611e" dependencies = [ "arrayvec", - "auto_impl", + "auto_impl 1.0.1", "bytes", - "ethereum-types", + "ethereum-types 0.13.1", "fastrlp-derive", ] @@ -1539,17 +1873,17 @@ dependencies = [ "bytes", "cid 0.8.6", "derive_more", - "ethers", + "ethers 1.0.2", "etk-asm", "fil_actors_runtime", - "fixed-hash", + "fixed-hash 0.7.0", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_kamt", "fvm_shared", "hex", "hex-literal", - "impl-serde", + "impl-serde 0.3.2", "lazy_static", "log", "multihash 0.16.3", @@ -1861,6 +2195,18 @@ dependencies = [ "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" @@ -2503,6 +2849,15 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -2531,6 +2886,15 @@ dependencies = [ "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" @@ -2950,6 +3314,31 @@ 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" @@ -3190,10 +3579,24 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" dependencies = [ - "fixed-hash", + "fixed-hash 0.7.0", "impl-codec", "impl-rlp", - "impl-serde", + "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", ] @@ -3547,7 +3950,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" dependencies = [ - "cipher", + "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]] @@ -3559,6 +3971,30 @@ 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" @@ -3574,7 +4010,19 @@ dependencies = [ "hmac", "password-hash 0.3.2", "pbkdf2 0.10.1", - "salsa20", + "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", + "pbkdf2 0.11.0", + "salsa20 0.10.2", "sha2 0.10.6", ] @@ -3642,6 +4090,16 @@ dependencies = [ "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" @@ -3989,7 +4447,7 @@ dependencies = [ "bimap", "blake2b_simd", "cid 0.8.6", - "ethers", + "ethers 0.17.0", "fil_actor_account", "fil_actor_cron", "fil_actor_datacap", @@ -4230,6 +4688,12 @@ 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" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index e0ec72c1b..3593e6137 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -46,7 +46,7 @@ once_cell = { version = "1.16.0", default-features = false} [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } etk-asm = "^0.2.1" -ethers = { version = "0.17.0", features = ["abigen"] } +ethers = { version = "1.0.2", features = ["abigen"] } serde_json = "1.0" rand = "0.8.5" From 24cbacfaa1533e671d45eec5009eae72569cebaf Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 20 Dec 2022 00:50:56 +0000 Subject: [PATCH 215/339] EVM: Don't special case ID address range for actor type (#977) * dont special case 'system' id addresses, always check type * fix actor type precompile test * fix panic in actor type, add test for it --- actors/evm/src/interpreter/precompiles/fvm.rs | 72 +++++++++---------- actors/evm/tests/precompile.rs | 15 ++-- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 90ed87ca5..b31e0af0f 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -16,50 +16,48 @@ use super::{parameter::U256Reader, PrecompileContext, PrecompileError, Precompil /// Read right padded BE encoded low u64 ID address from a u256 word. /// Returns variant of [`BuiltinType`] encoded as a u256 word. +/// Returns nothing inputs >2^65 pub(super) fn get_actor_type( system: &mut System, input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - const LAST_SYSTEM_ACTOR_ID: u64 = 32; - - let id_bytes: [u8; 32] = read_right_pad(input, 32).as_ref().try_into().unwrap(); - let id = Parameter::::try_from(&id_bytes)?.0; + // should never panic, pad to 32 bytes then read exactly 32 bytes + let id_bytes: [u8; 32] = read_right_pad(input, 32)[..32].as_ref().try_into().unwrap(); + let id = match Parameter::::try_from(&id_bytes) { + Ok(id) => id.0, + Err(_) => return Ok(Vec::new()), + }; - if id < LAST_SYSTEM_ACTOR_ID { - // known to be system actors - Ok(NativeType::System.word_vec()) - } else { - // resolve type from code CID - let builtin_type = system - .rt - .get_actor_code_cid(&id) - .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); - - let builtin_type = match builtin_type { - Some(t) => match t { - Type::Account | Type::EthAccount => NativeType::Account, - Type::System => NativeType::System, - Type::Embryo => NativeType::Embryo, - Type::EVM => NativeType::EVMContract, - Type::Miner => NativeType::StorageProvider, - // Others - Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, - // Singletons - Type::Market - | Type::Power - | Type::Init - | Type::Cron - | Type::Reward - | Type::VerifiedRegistry - | Type::DataCap - | Type::EAM => NativeType::System, - }, - None => NativeType::NonExistent, - }; + // resolve type from code CID + let builtin_type = system + .rt + .get_actor_code_cid(&id) + .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); + + let builtin_type = match builtin_type { + Some(t) => match t { + Type::Account | Type::EthAccount => NativeType::Account, + Type::Embryo => NativeType::Embryo, + Type::EVM => NativeType::EVMContract, + Type::Miner => NativeType::StorageProvider, + // Others + Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, + // Singletons (this should be caught earlier, but we are being exhaustive) + Type::Market + | Type::Power + | Type::Init + | Type::Cron + | Type::Reward + | Type::VerifiedRegistry + | Type::DataCap + | Type::EAM + | Type::System => NativeType::System, + }, + None => NativeType::NonExistent, + }; - Ok(builtin_type.word_vec()) - } + Ok(builtin_type.word_vec()) } /// Params: diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index ce2d33d87..91848e840 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -3,15 +3,13 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ - MockRuntime, ACCOUNT_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, + MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; use fvm_shared::address::Address as FILAddress; mod util; -use util::DUMMY_ACTOR_CODE_ID; - #[allow(dead_code)] pub fn magic_precompile_contract() -> Vec { let init = r#" @@ -122,8 +120,8 @@ return rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); // f0 31 is a system actor - let system_target = FILAddress::new_id(31); - rt.set_address_actor_type(system_target, *DUMMY_ACTOR_CODE_ID); + let system_target = FILAddress::new_id(10); + rt.set_address_actor_type(system_target, *EAM_ACTOR_CODE_ID); // f0 101 is an account let account_target = FILAddress::new_id(101); @@ -160,4 +158,11 @@ return test_type(&mut rt, miner_target, NativeType::StorageProvider); test_type(&mut rt, other_target, NativeType::OtherTypes); test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); + + // invalid format address + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(&mut rt, &[0xff; 64]); + rt.verify(); + assert!(result.is_empty()); + rt.reset(); } From 655b7c102e1374489170eb6c778d77e78125c63f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Dec 2022 20:01:26 -0800 Subject: [PATCH 216/339] fix: remove EAM::CreateAccount (#979) This is just dead code at this point. fixes https://github.com/filecoin-project/ref-fvm/issues/1292 --- actors/eam/src/lib.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 1f142d414..2ec3116d5 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -34,7 +34,6 @@ pub enum Method { // TODO: Do we want to use ExportedNums for all of these, per FRC-42? Create = 2, Create2 = 3, - // CreateAccount = 4, } /// Compute the a new actor address using the EVM's CREATE rules. @@ -234,33 +233,6 @@ impl EamActor { // send to init actor create_actor(rt, caller_addr, eth_addr, params.initcode) } - - pub fn create_account( - rt: &mut impl Runtime, - params: InitAccountParams, - ) -> Result { - // First, validate that we're receiving this message from the filecoin account that maps to - // this ethereum account. - // - // We don't need to validate that the _key_ is well formed or anything, because the fact - // that we're receiving a message from the account proves that to be the case anyways. - // - // TODO: allow off-chain deployment! - let key_addr = Address::new_secp256k1(¶ms.pubkey) - .map_err(|e| ActorError::illegal_argument(format!("not a valid public key: {e}")))?; - - rt.validate_immediate_caller_is(iter::once(&key_addr))?; - - // Compute the equivalent eth address - let eth_address = EthAddress(hash_20(rt, ¶ms.pubkey[1..])); - - // TODO: Check reserved ranges (id, precompile, etc.). - - // Attempt to deploy an account there. - // TODO - create_actor(rt, EthAddress([0u8; 20]), eth_address, Vec::new()).ok(); - todo!() - } } impl ActorCode for EamActor { From 441abe04077c73c86cb36e51356024e8ff08900c Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Wed, 21 Dec 2022 12:46:01 -0500 Subject: [PATCH 217/339] update embryo actor to placeholder (#978) update embroy actor to placeholder according to https://github.com/filecoin-project/FIPs/pull/577 --- Cargo.lock | 18 +++++++++--------- Cargo.toml | 2 +- .../evm/src/interpreter/instructions/call.rs | 10 +++++----- actors/evm/src/interpreter/instructions/ext.rs | 4 ++-- actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- actors/evm/src/interpreter/precompiles/mod.rs | 2 +- actors/evm/tests/call.rs | 4 ++-- actors/evm/tests/ext_opcodes.rs | 6 +++--- actors/evm/tests/precompile.rs | 12 ++++++------ actors/init/src/lib.rs | 8 ++++---- actors/init/tests/init_actor_test.rs | 8 ++++---- actors/{embryo => placeholder}/Cargo.toml | 4 ++-- actors/{embryo => placeholder}/src/lib.rs | 2 +- build.rs | 2 +- runtime/src/runtime/builtins.rs | 4 ++-- runtime/src/test_utils.rs | 8 ++++---- state/src/check.rs | 2 +- test_vm/src/lib.rs | 8 ++++---- test_vm/tests/init_test.rs | 16 ++++++++-------- 19 files changed, 61 insertions(+), 61 deletions(-) rename actors/{embryo => placeholder}/Cargo.toml (84%) rename actors/{embryo => placeholder}/src/lib.rs (71%) diff --git a/Cargo.lock b/Cargo.lock index 4ac704a65..aa4ed5f7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1843,14 +1843,6 @@ dependencies = [ "serde_tuple", ] -[[package]] -name = "fil_actor_embryo" -version = "10.0.0-alpha.1" -dependencies = [ - "fvm_sdk", - "fvm_shared", -] - [[package]] name = "fil_actor_ethaccount" version = "10.0.0-alpha.1" @@ -2018,6 +2010,14 @@ dependencies = [ "serde", ] +[[package]] +name = "fil_actor_placeholder" +version = "10.0.0-alpha.1" +dependencies = [ + "fvm_sdk", + "fvm_shared", +] + [[package]] name = "fil_actor_power" version = "10.0.0-alpha.1" @@ -2138,7 +2138,6 @@ dependencies = [ "fil_actor_bundler", "fil_actor_cron", "fil_actor_datacap", - "fil_actor_embryo", "fil_actor_ethaccount", "fil_actor_evm", "fil_actor_init", @@ -2146,6 +2145,7 @@ dependencies = [ "fil_actor_miner", "fil_actor_multisig", "fil_actor_paych", + "fil_actor_placeholder", "fil_actor_power", "fil_actor_reward", "fil_actor_system", diff --git a/Cargo.toml b/Cargo.toml index 1615c2d24..d25edbc23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ publish = false fil_actor_account = { version = "10.0.0-alpha.1", path = "./actors/account", features = ["fil-actor"] } fil_actor_cron = { version = "10.0.0-alpha.1", path = "./actors/cron", features = ["fil-actor"] } fil_actor_datacap = { version = "10.0.0-alpha.1", path = "./actors/datacap", features = ["fil-actor"] } -fil_actor_embryo = { version = "10.0.0-alpha.1", path = "./actors/embryo", features = ["fil-actor"] } +fil_actor_placeholder = { version = "10.0.0-alpha.1", path = "./actors/placeholder", 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_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 2672ffe1b..a4af717ca 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -216,8 +216,8 @@ pub fn call_generic( let dst_addr: EthAddress = dst.into(); let dst_addr: Address = dst_addr.try_into().expect("address is a precompile"); - // Special casing for account/embryo/non-existent actors: we just do a SEND (method 0) - // which allows us to transfer funds (and create embryos) + // Special casing for account/placeholder/non-existent actors: we just do a SEND (method 0) + // which allows us to transfer funds (and create placeholders) let target_actor_code = system .rt .resolve_address(&dst_addr) @@ -236,15 +236,15 @@ pub fn call_generic( Ok(RawBytes::default()) } else { let (method, gas_limit) = if !actor_exists - || matches!(target_actor_type, Some(Type::Embryo | Type::Account)) + || matches!(target_actor_type, Some(Type::Placeholder | Type::Account)) // See https://github.com/filecoin-project/ref-fvm/issues/980 for this // hocus pocus || (input_data.is_empty() && ((gas == 0 && value > 0) || (gas == 2300 && value == 0))) { // We switch to a bare send when: // - // 1. The target is an embryo/account or doesn't exist. Otherwise, - // sendign funds to an account/embryo would fail when we try to call + // 1. The target is a placeholder/account or doesn't exist. Otherwise, + // sendign funds to an account/placeholder would fail when we try to call // InvokeContract. // 2. The gas wouldn't let code execute anyways. This lets us support // solidity's "transfer" method. diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 2ebf78fa4..e256ee44e 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -121,8 +121,8 @@ pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { .and_then(|addr| 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 embryos are accounts - Some(Type::Account | Type::Embryo) => ContractType::Account, + // TODO part of current account abstraction hack where placeholders are accounts + Some(Type::Account | Type::Placeholder) => ContractType::Account, Some(Type::EVM) => ContractType::EVM(Address::new_id(id)), // remaining builtin actors are native _ => ContractType::Native(cid), diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index b31e0af0f..259634d20 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -38,7 +38,7 @@ pub(super) fn get_actor_type( let builtin_type = match builtin_type { Some(t) => match t { Type::Account | Type::EthAccount => NativeType::Account, - Type::Embryo => NativeType::Embryo, + Type::Placeholder => NativeType::Placeholder, Type::EVM => NativeType::EVMContract, Type::Miner => NativeType::StorageProvider, // Others diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index d39a88112..161b49f7b 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -115,7 +115,7 @@ pub enum NativeType { // user actors are flattened to "system" /// System includes any singletons not otherwise defined. System = 1, - Embryo = 2, + Placeholder = 2, Account = 3, StorageProvider = 4, EVMContract = 5, diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 5d267004d..99a331b2f 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -225,12 +225,12 @@ fn test_empty_call_no_side_effects() { rt.verify(); } -// Make sure we do bare sends when calling accounts/embryo, and make sure it works. +// Make sure we do bare sends when calling accounts/placeholder, and make sure it works. #[test] fn test_call_convert_to_send() { let contract = call_proxy_contract(); - for code in [*ACCOUNT_ACTOR_CODE_ID, *EMBRYO_ACTOR_CODE_ID] { + for code in [*ACCOUNT_ACTOR_CODE_ID, *PLACEHOLDER_ACTOR_CODE_ID] { // construct the proxy let mut rt = util::construct_and_verify(contract.clone()); diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index ab88698bc..fd90302a8 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -78,8 +78,8 @@ native_account: // 0x0101 is an EVM EOA account let evm_account = FILAddress::new_id(0x0101); - // TODO this is part of the account abstraction hack, where embryos are magically accounts - rt.set_address_actor_type(evm_account, *EMBRYO_ACTOR_CODE_ID); + // 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); @@ -192,7 +192,7 @@ account: // 0x8A is an account let native_target = FILAddress::new_id(0x8A); - rt.set_address_actor_type(native_target, *EMBRYO_ACTOR_CODE_ID); + rt.set_address_actor_type(native_target, *PLACEHOLDER_ACTOR_CODE_ID); let empty_hash = empty_bytecode_hash(&mut rt); diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 91848e840..1cf25257b 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -3,8 +3,8 @@ mod asm; use evm::interpreter::U256; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ - MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, - MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, + MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, }; use fvm_shared::address::Address as FILAddress; @@ -127,9 +127,9 @@ return let account_target = FILAddress::new_id(101); rt.set_address_actor_type(account_target, *ACCOUNT_ACTOR_CODE_ID); - // f0 102 is an embryo - let embryo_target = FILAddress::new_id(102); - rt.set_address_actor_type(embryo_target, *EMBRYO_ACTOR_CODE_ID); + // f0 102 is a placeholder + let placeholder_target = FILAddress::new_id(102); + rt.set_address_actor_type(placeholder_target, *PLACEHOLDER_ACTOR_CODE_ID); // f0 103 is a storage provider let miner_target = FILAddress::new_id(103); @@ -154,7 +154,7 @@ return test_type(&mut rt, evm_target, NativeType::EVMContract); test_type(&mut rt, system_target, NativeType::System); test_type(&mut rt, account_target, NativeType::Account); - test_type(&mut rt, embryo_target, NativeType::Embryo); + test_type(&mut rt, placeholder_target, NativeType::Placeholder); test_type(&mut rt, miner_target, NativeType::StorageProvider); test_type(&mut rt, other_target, NativeType::OtherTypes); test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index 6f9518f8f..b109d77a9 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -144,16 +144,16 @@ impl Actor { .context("failed to map addresses to ID") })?; - // If the f4 address was already assigned, make sure we're deploying over an embryo and not + // If the f4 address was already assigned, make sure we're deploying over a placeholder and not // some other existing actor (and make sure the target actor wasn't deleted either). if existing { let code_cid = rt .get_actor_code_cid(&id_address) .context_code(ExitCode::USR_FORBIDDEN, "cannot redeploy a deleted actor")?; - let embryo_cid = rt.get_code_cid_for_type(Type::Embryo); - if code_cid != embryo_cid { + let placeholder_cid = rt.get_code_cid_for_type(Type::Placeholder); + if code_cid != placeholder_cid { return Err(ActorError::forbidden(format!( - "cannot replace an existing non-embryo actor with code: {code_cid}" + "cannot replace an existing non-placeholder actor with code: {code_cid}" ))); } } diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index b2c0969ad..bb50ef475 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -411,9 +411,9 @@ fn call_exec4() { assert_eq!(exec_err.exit_code(), ExitCode::USR_FORBIDDEN); } -// Try turning an embryo into an f4 actor. +// Try turning a placeholder into an f4 actor. #[test] -fn call_exec4_embryo() { +fn call_exec4_placeholder() { let mut rt = construct_runtime(); construct_and_verify(&mut rt); @@ -425,7 +425,7 @@ fn call_exec4_embryo() { let subaddr = b"foobar"; let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); - // Register an embryo with the init actor. + // Register a placeholder with the init actor. let expected_id = { let mut state: State = rt.get_state(); let (id, existing) = state.map_addresses_to_id(rt.store(), &f4_addr, None).unwrap(); @@ -436,7 +436,7 @@ fn call_exec4_embryo() { // Register it in the state-tree. let expected_id_addr = Address::new_id(expected_id); - rt.set_address_actor_type(expected_id_addr, *EMBRYO_ACTOR_CODE_ID); + rt.set_address_actor_type(expected_id_addr, *PLACEHOLDER_ACTOR_CODE_ID); rt.add_delegated_address(expected_id_addr, f4_addr); // Now try to create it. diff --git a/actors/embryo/Cargo.toml b/actors/placeholder/Cargo.toml similarity index 84% rename from actors/embryo/Cargo.toml rename to actors/placeholder/Cargo.toml index b02967799..183ca40d8 100644 --- a/actors/embryo/Cargo.toml +++ b/actors/placeholder/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "fil_actor_embryo" -description = "Builtin embryo actor for Filecoin" +name = "fil_actor_placeholder" +description = "Builtin placeholder actor for Filecoin" version = "10.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["Protocol Labs", "Filecoin Core Devs"] diff --git a/actors/embryo/src/lib.rs b/actors/placeholder/src/lib.rs similarity index 71% rename from actors/embryo/src/lib.rs rename to actors/placeholder/src/lib.rs index d33f2bfd8..996d7e260 100644 --- a/actors/embryo/src/lib.rs +++ b/actors/placeholder/src/lib.rs @@ -3,6 +3,6 @@ pub extern "C" fn invoke(_: u32) -> u32 { fvm_sdk::vm::abort( fvm_shared::error::ExitCode::USR_UNHANDLED_MESSAGE.value(), - Some("embryo actors may only receive messages on method 0"), + Some("placeholder actors may only receive messages on method 0"), ) } diff --git a/build.rs b/build.rs index fe29ab044..6278c7a3c 100644 --- a/build.rs +++ b/build.rs @@ -26,7 +26,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("reward", "reward"), ("verifreg", "verifiedregistry"), ("datacap", "datacap"), - ("embryo", "embryo"), + ("placeholder", "placeholder"), ("evm", "evm"), ("eam", "eam"), ("ethaccount", "ethaccount"), diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index 6febc7bdf..0279349e6 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -20,7 +20,7 @@ pub enum Type { Reward = 10, VerifiedRegistry = 11, DataCap = 12, - Embryo = 13, + Placeholder = 13, EVM = 14, EAM = 15, EthAccount = 16, @@ -41,7 +41,7 @@ impl Type { Type::Reward => "reward", Type::VerifiedRegistry => "verifiedregistry", Type::DataCap => "datacap", - Type::Embryo => "embryo", + Type::Placeholder => "placeholder", Type::EVM => "evm", Type::EAM => "eam", Type::EthAccount => "ethaccount", diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 9d9eb2b8e..e2c93c286 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -63,7 +63,7 @@ lazy_static::lazy_static! { pub static ref REWARD_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/reward"); 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 EMBRYO_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/embryo"); + 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 ACTOR_TYPES: BTreeMap = { @@ -80,7 +80,7 @@ lazy_static::lazy_static! { map.insert(*REWARD_ACTOR_CODE_ID, Type::Reward); map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); map.insert(*DATACAP_TOKEN_ACTOR_CODE_ID, Type::DataCap); - map.insert(*EMBRYO_ACTOR_CODE_ID, Type::Embryo); + 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 @@ -98,7 +98,7 @@ lazy_static::lazy_static! { (Type::Reward, *REWARD_ACTOR_CODE_ID), (Type::VerifiedRegistry, *VERIFREG_ACTOR_CODE_ID), (Type::DataCap, *DATACAP_TOKEN_ACTOR_CODE_ID), - (Type::Embryo, *EMBRYO_ACTOR_CODE_ID), + (Type::Placeholder, *PLACEHOLDER_ACTOR_CODE_ID), (Type::EVM, *EVM_ACTOR_CODE_ID), (Type::EAM, *EAM_ACTOR_CODE_ID), ] @@ -110,7 +110,7 @@ lazy_static::lazy_static! { map.insert(*PAYCH_ACTOR_CODE_ID, ()); map.insert(*MULTISIG_ACTOR_CODE_ID, ()); map.insert(*MINER_ACTOR_CODE_ID, ()); - map.insert(*EMBRYO_ACTOR_CODE_ID, ()); + map.insert(*PLACEHOLDER_ACTOR_CODE_ID, ()); map.insert(*EVM_ACTOR_CODE_ID, ()); map }; diff --git a/state/src/check.rs b/state/src/check.rs index ac8c461ec..aab5d4719 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -220,7 +220,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( acc.with_prefix("datacap: ").add_all(&msgs); datacap_summary = Some(summary); } - Some(Type::Embryo) => {} + Some(Type::Placeholder) => {} Some(Type::EVM) => {} Some(Type::EAM) => {} Some(Type::EthAccount) => {} diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 3db2c05d1..e06f5b8d1 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -678,7 +678,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { subinvocs }); } else { - new_ctx.create_actor(*EMBRYO_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); + new_ctx.create_actor(*PLACEHOLDER_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); } } @@ -780,8 +780,8 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { Type::VerifiedRegistry => VerifregActor::invoke_method(self, self.msg.method, params), // Type::EVM => panic!("no EVM"), Type::DataCap => DataCapActor::invoke_method(self, self.msg.method, params), - Type::Embryo => { - Err(ActorError::unhandled_message("embryo actors only handle method 0".into())) + Type::Placeholder => { + Err(ActorError::unhandled_message("placeholder actors only handle method 0".into())) } Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, params), Type::EAM => EamActor::invoke_method(self, self.msg.method, params), @@ -818,7 +818,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { } let addr = Address::new_id(actor_id); let actor = match self.v.get_actor(addr) { - Some(mut act) if act.code == *EMBRYO_ACTOR_CODE_ID => { + Some(mut act) if act.code == *PLACEHOLDER_ACTOR_CODE_ID => { act.code = code_id; act } diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs index 58782becf..2256c6033 100644 --- a/test_vm/tests/init_test.rs +++ b/test_vm/tests/init_test.rs @@ -2,7 +2,7 @@ use fil_actor_init::Exec4Return; use fil_actors_runtime::{ cbor::serialize, runtime::EMPTY_ARR_CID, - test_utils::{EAM_ACTOR_CODE_ID, EMBRYO_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID}, + 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; @@ -11,15 +11,15 @@ use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, METHOD_SE use num_traits::Zero; use test_vm::{actor, FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR, VM}; -fn assert_embryo_actor(exp_bal: TokenAmount, v: &VM, addr: Address) { +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!(*EMBRYO_ACTOR_CODE_ID, act.code); + assert_eq!(*PLACEHOLDER_ACTOR_CODE_ID, act.code); assert_eq!(exp_bal, act.balance); } #[test] -fn embryo_deploy() { +fn placeholder_deploy() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); @@ -29,7 +29,7 @@ fn embryo_deploy() { actor(*EAM_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), ); - // Create an embryo. + // Create a placeholder. let subaddr = b"foobar"; let addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); @@ -45,12 +45,12 @@ fn embryo_deploy() { .code .is_success()); let expect_id_addr = Address::new_id(FIRST_TEST_USER_ADDR); - assert_embryo_actor(TokenAmount::from_atto(42u8), &v, expect_id_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 embryo. + // Deploy a multisig to the placeholder. let msig_ctor_params = serialize( &fil_actor_multisig::ConstructorParams { signers: vec![EAM_ACTOR_ADDR], @@ -83,7 +83,7 @@ fn embryo_deploy() { assert_eq!( expect_id_addr, msig_ctor_ret.id_address, - "expected actor to be deployed over embryo" + "expected actor to be deployed over placeholder" ); // Make sure we kept the balance. From ad141a1d8998e3fb0d3122e677352dd9c7462184 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 21 Dec 2022 12:49:57 -0500 Subject: [PATCH 218/339] EthAccount: Fixup and clarify comments (#982) --- actors/ethaccount/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/actors/ethaccount/src/lib.rs b/actors/ethaccount/src/lib.rs index c621051e9..c832bc8f9 100644 --- a/actors/ethaccount/src/lib.rs +++ b/actors/ethaccount/src/lib.rs @@ -12,18 +12,19 @@ use fil_actors_runtime::{ #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EthAccountActor); -/// Ethereum Externally Owned Address actor methods. +/// Ethereum Account actor methods. #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, } -/// Ethereum Externally Owned Address actor. +/// Ethereum Account actor. pub struct EthAccountActor; impl EthAccountActor { - /// Ethereum Externally Owned Address actor constructor. + /// Ethereum Account actor constructor. + /// NOTE: This method is NOT currently called from anywhere, instead the FVM just deploys EthAccounts. pub fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; From 393d7daeb89fc54286daeb0cf5078fb01f4756d8 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 21 Dec 2022 20:27:15 -0500 Subject: [PATCH 219/339] EAM: Remove InitAccountParams (#984) --- actors/eam/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 2ec3116d5..3370d0a31 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -16,7 +16,7 @@ use { }, fvm_ipld_encoding::{strict_bytes, tuple::*, RawBytes}, fvm_shared::{ - address::{Address, Payload, SECP_PUB_LEN}, + address::{Address, Payload}, crypto::hash::SupportedHashes, ActorID, MethodNum, METHOD_CONSTRUCTOR, }, @@ -92,12 +92,6 @@ pub struct Create2Params { pub salt: [u8; 32], } -#[derive(Serialize_tuple, Deserialize_tuple)] -pub struct InitAccountParams { - #[serde(with = "strict_bytes")] - pub pubkey: [u8; SECP_PUB_LEN], -} - #[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq)] pub struct Return { pub actor_id: ActorID, From 405bec35502f6a42df92900fa62f8dabeee776b6 Mon Sep 17 00:00:00 2001 From: raulk Date: Thu, 22 Dec 2022 17:02:07 +0000 Subject: [PATCH 220/339] fix: EVM: CALLs to EthAccount should be lowered to bare transfers. (#986) * fix: EVM: CALLs to EthAccount should be lowered to bare transfers. * catch another one. --- actors/evm/src/interpreter/instructions/call.rs | 4 ++-- actors/evm/src/interpreter/instructions/ext.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index a4af717ca..e54c3f862 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -236,7 +236,7 @@ pub fn call_generic( Ok(RawBytes::default()) } else { let (method, gas_limit) = if !actor_exists - || matches!(target_actor_type, Some(Type::Placeholder | Type::Account)) + || matches!(target_actor_type, Some(Type::Placeholder | Type::Account | Type::EthAccount)) // See https://github.com/filecoin-project/ref-fvm/issues/980 for this // hocus pocus || (input_data.is_empty() && ((gas == 0 && value > 0) || (gas == 2300 && value == 0))) @@ -244,7 +244,7 @@ pub fn call_generic( // We switch to a bare send when: // // 1. The target is a placeholder/account or doesn't exist. Otherwise, - // sendign funds to an account/placeholder would fail when we try to call + // sending funds to an account/placeholder would fail when we try to call // InvokeContract. // 2. The gas wouldn't let code execute anyways. This lets us support // solidity's "transfer" method. diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index e256ee44e..4eb88e2be 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -122,7 +122,7 @@ pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { .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) => ContractType::Account, + 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), From e1ef665b2f2dca1448c04c9b439e9cdbc9e0b511 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 22 Dec 2022 20:45:02 +0000 Subject: [PATCH 221/339] EVM: Native precompile address `fe` prefix (#981) * move native precompiles to their own addresses with prefix 0xfe * fmt and remove some stale TODOs * fix review comments * comment style consisticy --- actors/evm/src/interpreter/precompiles/mod.rs | 155 ++++++++++++------ actors/evm/tests/call.rs | 4 +- actors/evm/tests/precompile.rs | 2 +- 3 files changed, 112 insertions(+), 49 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 161b49f7b..01eebae22 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -14,71 +14,93 @@ use fvm::{call_actor, get_actor_type, get_randomness, lookup_delegated_address, // really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; -pub type PrecompileResult = Result, PrecompileError>; // TODO i dont like vec - -/// Generates a list of precompile smart contracts, index + 1 is the address. (another option is to make an enum) -const fn gen_precompiles() -> [PrecompileFn; 14] { - macro_rules! precompiles { - ($($precompile:ident,)*) => { - mod trampolines { - use fil_actors_runtime::runtime::Runtime; - use crate::System; - use super::{PrecompileContext, PrecompileResult}; - $( - #[inline(always)] - pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { - super::$precompile(&mut *s, inp, ctx) - } - )* - } - [ - $(trampolines::$precompile,)* - ] +pub type PrecompileResult = Result, PrecompileError>; + +pub const NATIVE_PRECOMPILE_ADDRESS_PREFIX: u8 = 0xFE; + +macro_rules! precompiles { + ($($precompile:ident,)*) => { + mod trampolines { + use fil_actors_runtime::runtime::Runtime; + use crate::System; + use super::{PrecompileContext, PrecompileResult}; + $( + #[inline(always)] + pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { + super::$precompile(&mut *s, inp, ctx) + } + )* } + [ + $(trampolines::$precompile,)* + ] } +} +/// Generates a list of precompile smart contracts, index + 1 is the address. +const fn gen_evm_precompiles() -> [PrecompileFn; 9] { precompiles! { - ec_recover, // ecrecover 0x01 - sha256, // SHA2-256 0x02 - ripemd160, // ripemd160 0x03 - identity, // identity 0x04 - modexp, // modexp 0x05 - ec_add, // ecAdd 0x06 - ec_mul, // ecMul 0x07 - ec_pairing, // ecPairing 0x08 - blake2f, // blake2f 0x09 - // FIL precompiles - resolve_address, // resolve_address 0x0a - lookup_delegated_address, // lookup_delegated_address 0x0b - get_actor_type, // get actor type 0x0c - get_randomness, // rand 0x0d - call_actor, // call_actor 0x0e + ec_recover, // 0x01 ecrecover + sha256, // 0x02 SHA2-256 + ripemd160, // 0x03 ripemd160 + identity, // 0x04 identity + modexp, // 0x05 modexp + ec_add, // 0x06 ecAdd + ec_mul, // 0x07 ecMul + ec_pairing, // 0x08 ecPairing + blake2f, // 0x09 blake2f + } +} + +const fn gen_native_precompiles() -> [PrecompileFn; 5] { + precompiles! { + resolve_address, // 0xfe00..01 resolve_address + lookup_delegated_address, // 0xfe00..02 lookup_delegated_address + call_actor, // 0xfe00..03 call_actor + get_actor_type, // 0xfe00..04 get_actor_type + get_randomness, // 0xfe00..05 rand } } pub struct Precompiles(PhantomData); impl Precompiles { - const PRECOMPILES: [PrecompileFn; 14] = gen_precompiles(); - const MAX_PRECOMPILE: U256 = { - let mut limbs = [0u64; 4]; - limbs[0] = Self::PRECOMPILES.len() as u64; - U256(limbs) - }; - - // Precompile Context will be flattened to None if not calling the call_actor precompile + const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); + const NATIVE_PRECOMPILES: [PrecompileFn; 5] = gen_native_precompiles(); + + fn lookup_precompile(addr: &[u8; 32]) -> Option> { + // unrwap will never panic, 32 - 12 = 20 + let addr: [u8; 20] = addr[12..].try_into().unwrap(); + let [prefix, middle @ .., index] = addr; + if middle == [0u8; 18] && index > 0 { + let index = index as usize - 1; + match prefix { + NATIVE_PRECOMPILE_ADDRESS_PREFIX => Some(Self::NATIVE_PRECOMPILES[index]), + 0x00 => Some(Self::EVM_PRECOMPILES[index]), + _ => None, + } + } else { + None + } + } + + /// Precompile Context will be flattened to None if not calling the call_actor precompile. + /// Panics if address is not a precompile. pub fn call_precompile( system: &mut System, precompile_addr: U256, input: &[u8], context: PrecompileContext, ) -> PrecompileResult { - unsafe { Self::PRECOMPILES[precompile_addr.0[0] as usize - 1](system, input, context) } + unsafe { + Self::lookup_precompile(&precompile_addr.to_bytes()).unwrap()(system, input, context) + } } + /// Checks if word represents #[inline] pub fn is_precompile(addr: &U256) -> bool { - !addr.is_zero() && addr <= &Self::MAX_PRECOMPILE + !addr.is_zero() && Self::lookup_precompile(&addr.to_bytes()).is_some() } } @@ -86,7 +108,7 @@ impl Precompiles { pub enum PrecompileError { EcErr(CurveError), EcGroupErr(GroupError), - InvalidInput, // TODO merge with below? + InvalidInput, CallForbidden, IncorrectInputSize, OutOfGas, @@ -127,3 +149,44 @@ impl NativeType { U256::from(self as u32).to_bytes().to_vec() } } + +#[cfg(test)] +mod test { + use fil_actors_runtime::test_utils::MockRuntime; + + use crate::interpreter::address::EthAddress; + + use super::Precompiles; + + #[test] + fn is_native_precompile() { + let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); + assert!(Precompiles::::is_precompile(&addr.as_evm_word())); + } + + #[test] + fn is_evm_precompile() { + let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); + assert!(Precompiles::::is_precompile(&addr.as_evm_word())); + } + + #[test] + fn is_over_precompile() { + let addr = EthAddress(hex_literal::hex!("ff00000000000000000000000000000000000001")); + assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); + } + + #[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.as_evm_word())); + assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); + } + + #[test] + fn between_precompile() { + let addr = EthAddress(hex_literal::hex!("a000000000000000000000000000000000000001")); + assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); + } +} diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 99a331b2f..e174057a5 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -391,7 +391,7 @@ calldatasize push1 0x00 # dst (callactor precompile) -push1 0x0e +push20 0xfe00000000000000000000000000000000000003 # gas push4 0xffffffff @@ -501,7 +501,7 @@ fn test_callactor_inner(exit_code: ExitCode) { impl CallActorReturn { pub fn read(src: &[u8]) -> Self { use fil_actor_evm::interpreter::precompiles::parameter::assert_zero_bytes; - assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values"); + assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values, got {:?}", src); let bytes = &src[..32]; let exit_code = { diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 1cf25257b..f5d908860 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -89,7 +89,7 @@ push1 0x00 push1 0x00 # dst (get_actor_type precompile) -push1 0x0c +push20 0xfe00000000000000000000000000000000000004 # gas push1 0x00 From 1fa3f347df091c548b6568b543cdd5c67376c4e9 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 3 Jan 2023 03:38:09 +0000 Subject: [PATCH 222/339] EVM: Pass original caller through to callee for Delegate Call (#994) * fix evm_delegate_call's caller bug * use Eth address for delegate call params instead of ID address * fmt, add test for delegatecall Co-authored-by: samuerio <853713419@qq.com> --- .../evm/src/interpreter/instructions/call.rs | 3 +- actors/evm/src/interpreter/system.rs | 5 + actors/evm/src/lib.rs | 23 ++- actors/evm/tests/delegate_call.rs | 132 ++++++++++++++++++ 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 actors/evm/tests/delegate_call.rs diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index e54c3f862..2c410dbff 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -278,7 +278,8 @@ pub fn call_generic( let code = get_evm_bytecode_cid(system.rt, &dst_addr)?; // and then invoke self with delegate; readonly context is sticky - let params = DelegateCallParams { code, input: input_data.into() }; + let params = DelegateCallParams { code, input: input_data.into(), + caller: state.caller, }; system.send( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 40dde0247..4c9b03096 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -317,6 +317,11 @@ impl<'r, RT: Runtime> System<'r, RT> { } /// 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() { diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 82d5afd0d..529355de3 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -135,6 +135,7 @@ impl EvmContractActor { rt: &mut RT, input_data: &[u8], with_code: Option, + with_caller: Option, ) -> Result, ActorError> where RT: Runtime, @@ -159,9 +160,12 @@ impl EvmContractActor { None => return Ok(Vec::new()), }; - // Resolve the caller's ethereum address. If the caller doesn't have one, the caller's ID is used instead. - let caller_fil_addr = system.rt.message().caller(); - let caller_eth_addr = system.resolve_ethereum_address(&caller_fil_addr).unwrap(); + // Use passed Eth address (from delegate call). + // Otherwise resolve the caller's ethereum address. If the caller doesn't have one, the caller's Eth encoded ID is used instead. + let caller_eth_addr = match with_caller { + Some(addr) => addr, + None => system.resolve_ethereum_address(&system.rt.message().caller()).unwrap(), + }; // Resolve the receiver's ethereum address. let receiver_fil_addr = system.rt.message().receiver(); @@ -200,7 +204,7 @@ impl EvmContractActor { { let params = args.unwrap_or(IpldBlock { codec: 0, data: vec![] }); let input = handle_filecoin_method_input(method, params.codec, params.data.as_slice()); - Self::invoke_contract(rt, &input, None) + Self::invoke_contract(rt, &input, None, None) } pub fn bytecode(rt: &mut impl Runtime) -> Result { @@ -291,7 +295,7 @@ impl ActorCode for EvmContractActor { p } }; - let value = Self::invoke_contract(rt, ¶ms, None)?; + let value = Self::invoke_contract(rt, ¶ms, None, None)?; Ok(RawBytes::serialize(BytesSer(&value))?) } Some(Method::GetBytecode) => { @@ -318,7 +322,12 @@ impl ActorCode for EvmContractActor { "method expects arguments".to_string() })? .deserialize()?; - let value = Self::invoke_contract(rt, ¶ms.input, Some(params.code))?; + let value = Self::invoke_contract( + rt, + ¶ms.input, + Some(params.code), + Some(params.caller), + )?; Ok(RawBytes::serialize(BytesSer(&value))?) } None => Err(actor_error!(unhandled_message; "Invalid method")), @@ -340,6 +349,8 @@ pub struct DelegateCallParams { /// The contract invocation parameters #[serde(with = "strict_bytes")] pub input: Vec, + /// The original caller's Eth address. + pub caller: EthAddress, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/actors/evm/tests/delegate_call.rs b/actors/evm/tests/delegate_call.rs new file mode 100644 index 000000000..4dd97ea79 --- /dev/null +++ b/actors/evm/tests/delegate_call.rs @@ -0,0 +1,132 @@ +use fil_actor_evm::{ + interpreter::{address::EthAddress, U256}, + DelegateCallParams, Method, +}; +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.add_delegated_address(target, 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.add_delegated_address(caller, 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, + }; + 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.expect_gas_available(10_000_000_000u64); + rt.expect_send( + target, + Method::GetBytecode as u64, + None, + TokenAmount::zero(), + RawBytes::serialize(EMPTY_ARR_CID).expect("failed to serialize bytecode hash"), + ExitCode::OK, + ); + + rt.expect_send_generalized( + target, + Method::InvokeContractDelegate as u64, + proxy_call_input_data, + TokenAmount::zero(), + Some(0xffffffff), + SendFlags::empty(), + RawBytes::serialize(BytesSer(&return_data.to_bytes())) + .expect("failed to serialize return data"), + ExitCode::OK, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), return_data); +} From f2514a5a20fabfd3b8bca468f45a55d55a3ea5c1 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 4 Jan 2023 02:36:15 +0000 Subject: [PATCH 223/339] EVM: Disable get-randomness precompile (#995) * disable get-randomness precompile * only remove precompile --- actors/evm/src/interpreter/precompiles/fvm.rs | 3 +++ actors/evm/src/interpreter/precompiles/mod.rs | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 259634d20..e771902b9 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -60,6 +60,8 @@ pub(super) fn get_actor_type( Ok(builtin_type.word_vec()) } +/// !! DISABLED !! +/// /// Params: /// /// | Param | Value | @@ -74,6 +76,7 @@ pub(super) fn get_actor_type( /// /// Returns empty array if invalid randomness type /// Errors if unable to fetch randomness +#[allow(unused)] pub(super) fn get_randomness( system: &mut System, input: &[u8], diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 01eebae22..c8ef5258e 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -10,7 +10,7 @@ mod fvm; pub mod parameter; use evm::{blake2f, ec_add, ec_mul, ec_pairing, ec_recover, identity, modexp, ripemd160, sha256}; -use fvm::{call_actor, get_actor_type, get_randomness, lookup_delegated_address, resolve_address}; +use fvm::{call_actor, get_actor_type, lookup_delegated_address, resolve_address}; // really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; @@ -52,13 +52,12 @@ const fn gen_evm_precompiles() -> [PrecompileFn; 9] { } } -const fn gen_native_precompiles() -> [PrecompileFn; 5] { +const fn gen_native_precompiles() -> [PrecompileFn; 4] { precompiles! { resolve_address, // 0xfe00..01 resolve_address lookup_delegated_address, // 0xfe00..02 lookup_delegated_address call_actor, // 0xfe00..03 call_actor get_actor_type, // 0xfe00..04 get_actor_type - get_randomness, // 0xfe00..05 rand } } @@ -66,7 +65,7 @@ pub struct Precompiles(PhantomData); impl Precompiles { const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); - const NATIVE_PRECOMPILES: [PrecompileFn; 5] = gen_native_precompiles(); + const NATIVE_PRECOMPILES: [PrecompileFn; 4] = gen_native_precompiles(); fn lookup_precompile(addr: &[u8; 32]) -> Option> { // unrwap will never panic, 32 - 12 = 20 From 00d2e1056a2d06b0691bbab47af2e2bda146e60b Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 4 Jan 2023 05:44:31 +0000 Subject: [PATCH 224/339] EVM: Flush state before calling send in Ext opcodes (#993) * Use system.send instead of system.rt.send to properly flush state before calling into another actor. This is needed since ext* opcodes can/will be called during initcode. Ext* calling self durin initcode must have _some_ state flushed in order to fetch it, system.send will flush empty bytecode hashes to state before send for us. * fmt & fix tests * add tests for ext size/hash in initcode * clippy * fix delegate_call test --- .../evm/src/interpreter/instructions/call.rs | 2 +- .../evm/src/interpreter/instructions/ext.rs | 46 ++++++--- actors/evm/src/lib.rs | 4 +- actors/evm/tests/delegate_call.rs | 4 +- actors/evm/tests/ext_opcodes.rs | 98 ++++++++++++++++++- actors/evm/tests/util.rs | 4 + 6 files changed, 135 insertions(+), 23 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 2c410dbff..66e84772a 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -275,7 +275,7 @@ pub fn call_generic( CallKind::DelegateCall => match get_contract_type(system.rt, dst) { ContractType::EVM(dst_addr) => { // If we're calling an actual EVM actor, get its code. - let code = get_evm_bytecode_cid(system.rt, &dst_addr)?; + let 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(), diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 4eb88e2be..b551f0742 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -5,6 +5,8 @@ use cid::Cid; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::ActorError; use fvm_ipld_blockstore::Blockstore; +use fvm_shared::crypto::hash::SupportedHashes; +use fvm_shared::sys::SendFlags; use fvm_shared::{address::Address, econ::TokenAmount}; use multihash::Multihash; use num_traits::Zero; @@ -14,16 +16,16 @@ use { }; /// Keccak256 hash of `[0xfe]`, "native bytecode" -const NATIVE_BYTECODE_HASH: [u8; 32] = +pub const NATIVE_BYTECODE_HASH: [u8; 32] = hex_literal::hex!("bcc90f2d6dada5b18e155c17a1c0a55920aae94f39857d39d0d8ed07ae8f228b"); /// Keccak256 hash of `[]`, empty bytecode -const EMPTY_EVM_HASH: [u8; 32] = +pub const EMPTY_EVM_HASH: [u8; 32] = hex_literal::hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); pub fn extcodesize( _state: &mut ExecutionState, - system: &System, + 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 @@ -31,7 +33,7 @@ pub fn extcodesize( // Tracked in https://github.com/filecoin-project/ref-fvm/issues/867 let len = match get_contract_type(system.rt, addr) { ContractType::EVM(addr) => { - get_evm_bytecode(system.rt, &addr).map(|bytecode| bytecode.len())? + get_evm_bytecode(system, &addr).map(|bytecode| bytecode.len())? } ContractType::Native(_) => 1, // account, not found, and precompiles are 0 size @@ -43,7 +45,7 @@ pub fn extcodesize( pub fn extcodehash( _state: &mut ExecutionState, - system: &System, + system: &mut System, addr: U256, ) -> Result { let addr = match get_contract_type(system.rt, addr) { @@ -63,16 +65,18 @@ pub fn extcodehash( // multihash { keccak256(bytecode) } let bytecode_hash: Multihash = system - .rt .send( &addr, crate::Method::GetBytecodeHash as u64, Default::default(), TokenAmount::zero(), + None, + SendFlags::READ_ONLY, )? .deserialize()?; let digest = bytecode_hash.digest(); + debug_assert_eq!(SupportedHashes::Keccak256 as u64, bytecode_hash.code()); // Take the first 32 bytes of the Multihash let digest_len = digest.len().min(32); @@ -81,14 +85,14 @@ pub fn extcodehash( pub fn extcodecopy( state: &mut ExecutionState, - system: &System, + system: &mut System, addr: U256, dest_offset: U256, data_offset: U256, size: U256, ) -> Result<(), StatusCode> { let bytecode = match get_contract_type(system.rt, addr) { - ContractType::EVM(addr) => get_evm_bytecode(system.rt, &addr)?, + 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 @@ -130,15 +134,29 @@ pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { .unwrap_or(ContractType::NotFound) } -pub fn get_evm_bytecode_cid(rt: &impl Runtime, addr: &Address) -> Result { - Ok(rt - .send(addr, crate::Method::GetBytecode as u64, Default::default(), TokenAmount::zero())? +pub fn get_evm_bytecode_cid( + system: &mut System, + addr: &Address, +) -> Result { + Ok(system + .send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + )? .deserialize()?) } -pub fn get_evm_bytecode(rt: &impl Runtime, addr: &Address) -> Result, StatusCode> { - let cid = get_evm_bytecode_cid(rt, addr)?; - let raw_bytecode = rt +pub fn get_evm_bytecode( + system: &mut System, + addr: &Address, +) -> Result, StatusCode> { + let cid = get_evm_bytecode_cid(system, addr)?; + let raw_bytecode = system + .rt .store() .get(&cid) // TODO this is inefficient; should call stat here. .map_err(|e| StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)))? diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 529355de3..93ca63168 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -303,8 +303,8 @@ impl ActorCode for EvmContractActor { Ok(RawBytes::serialize(cid)?) } Some(Method::GetBytecodeHash) => { - let cid = Self::bytecode_hash(rt)?; - Ok(RawBytes::serialize(cid)?) + let multihash = Self::bytecode_hash(rt)?; + Ok(RawBytes::serialize(multihash)?) } Some(Method::GetStorageAt) => { let value = Self::storage_at( diff --git a/actors/evm/tests/delegate_call.rs b/actors/evm/tests/delegate_call.rs index 4dd97ea79..555adaa0c 100644 --- a/actors/evm/tests/delegate_call.rs +++ b/actors/evm/tests/delegate_call.rs @@ -106,11 +106,13 @@ fn test_delegate_call_caller() { let return_data = U256::from(0x42); rt.expect_gas_available(10_000_000_000u64); - rt.expect_send( + rt.expect_send_generalized( target, Method::GetBytecode as u64, None, TokenAmount::zero(), + None, + SendFlags::READ_ONLY, RawBytes::serialize(EMPTY_ARR_CID).expect("failed to serialize bytecode hash"), ExitCode::OK, ); diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index fd90302a8..204f6b58b 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -1,9 +1,10 @@ mod asm; use cid::Cid; +use evm::interpreter::instructions::ext::EMPTY_EVM_HASH; use evm::interpreter::U256; use fil_actor_evm as evm; -use fil_actors_runtime::runtime::{Primitives, Runtime}; +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::RawBytes; @@ -14,7 +15,8 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; mod util; -use util::DUMMY_ACTOR_CODE_ID; +use fvm_shared::sys::SendFlags; +use util::{CONTRACT_ID, DUMMY_ACTOR_CODE_ID}; #[test] fn test_extcodesize() { @@ -89,11 +91,13 @@ native_account: let method = util::dispatch_num_word(0); let expected = U256::from(0x04); { - rt.expect_send( + rt.expect_send_generalized( evm_contract, evm::Method::GetBytecode as u64, Default::default(), TokenAmount::zero(), + None, + SendFlags::READ_ONLY, RawBytes::serialize(&bytecode_cid).unwrap(), ExitCode::OK, ); @@ -204,11 +208,13 @@ account: ) .unwrap(); - rt.expect_send( + rt.expect_send_generalized( evm_target, evm::Method::GetBytecodeHash as u64, Default::default(), TokenAmount::zero(), + None, + SendFlags::READ_ONLY, RawBytes::serialize(&bytecode_hash).unwrap(), ExitCode::OK, ); @@ -347,11 +353,13 @@ precompile: let other_bytecode = vec![0x01, 0x02, 0x03, 0x04]; rt.store.put_keyed(&bytecode_cid, other_bytecode.as_slice()).unwrap(); - rt.expect_send( + rt.expect_send_generalized( evm_target, evm::Method::GetBytecode as u64, Default::default(), TokenAmount::zero(), + None, + SendFlags::READ_ONLY, RawBytes::serialize(&bytecode_cid).unwrap(), ExitCode::OK, ); @@ -377,3 +385,83 @@ precompile: 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_generalized( + CONTRACT_ID, + evm::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + RawBytes::serialize( + Multihash::wrap(SupportedHashes::Keccak256 as u64, &EMPTY_EVM_HASH).unwrap(), + ) + .unwrap(), + ExitCode::OK, + ); + + rt.store.put_keyed(&EMPTY_ARR_CID, &[]).unwrap(); + rt.expect_send_generalized( + CONTRACT_ID, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + RawBytes::serialize(&EMPTY_ARR_CID).unwrap(), + ExitCode::OK, + ); + }); + + // codehash + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); + rt.verify(); + assert_eq!(EMPTY_EVM_HASH.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/util.rs b/actors/evm/tests/util.rs index e69a1f01d..e1fb48b4d 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -15,6 +15,9 @@ pub fn construct_and_verify(initcode: Vec) -> MockRuntime { 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, @@ -31,6 +34,7 @@ pub fn init_construct_and_verify( Address::new_id(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()), From 12e271a38f76c703e70a329aec795a6bbbbb891d Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 4 Jan 2023 19:38:46 +0000 Subject: [PATCH 225/339] EAM: Check addresses for reserved range of native precompile (#997) update EAM to check for new reserved range for native precompiles addresses --- actors/eam/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 3370d0a31..d8e457953 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -58,10 +58,13 @@ pub fn compute_address_create2( pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); impl EthAddress { - /// Returns true if the EthAddress refers to a precompile. + /// 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] fn is_precompile(&self) -> bool { - self.0[..19].iter().all(|&i| i == 0) + // Exact index is not checked since it is unknown to the EAM what precompiles exist in the EVM actor. + 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. From 3b317426b711ca4988dbbc45c6532cc7e041d2e7 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 4 Jan 2023 19:43:22 +0000 Subject: [PATCH 226/339] EVM: Check that precompile address index is within bounds (#996) * check that precompile address last byte/index is within range of precompile array to avoid panic * clippy --- actors/evm/src/interpreter/precompiles/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index c8ef5258e..001c479e1 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -74,10 +74,11 @@ impl Precompiles { if middle == [0u8; 18] && index > 0 { let index = index as usize - 1; match prefix { - NATIVE_PRECOMPILE_ADDRESS_PREFIX => Some(Self::NATIVE_PRECOMPILES[index]), - 0x00 => Some(Self::EVM_PRECOMPILES[index]), + NATIVE_PRECOMPILE_ADDRESS_PREFIX => Self::NATIVE_PRECOMPILES.get(index), + 0x00 => Self::EVM_PRECOMPILES.get(index), _ => None, } + .copied() } else { None } @@ -188,4 +189,12 @@ mod test { let addr = EthAddress(hex_literal::hex!("a000000000000000000000000000000000000001")); assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); } + + #[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.as_evm_word())); + assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); + } } From c2182a9a73abd434e565ffb96bc9543dd3648fa7 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 4 Jan 2023 22:13:07 +0000 Subject: [PATCH 227/339] Enable logging with MockRuntime (#985) --- Cargo.lock | 39 +++++++++++++++++++ .../instructions/{log.rs => log_event.rs} | 0 .../evm/src/interpreter/instructions/mod.rs | 4 +- actors/evm/tests/util.rs | 8 +++- runtime/Cargo.toml | 3 +- runtime/src/test_utils.rs | 5 +++ 6 files changed, 55 insertions(+), 4 deletions(-) rename actors/evm/src/interpreter/instructions/{log.rs => log_event.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index aa4ed5f7a..7ded98466 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,6 +1112,19 @@ 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" @@ -2119,6 +2132,7 @@ dependencies = [ "num-derive", "num-traits", "paste", + "pretty_env_logger", "rand", "regex", "serde", @@ -2769,6 +2783,15 @@ 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" @@ -3573,6 +3596,16 @@ 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" @@ -3672,6 +3705,12 @@ dependencies = [ "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.22" diff --git a/actors/evm/src/interpreter/instructions/log.rs b/actors/evm/src/interpreter/instructions/log_event.rs similarity index 100% rename from actors/evm/src/interpreter/instructions/log.rs rename to actors/evm/src/interpreter/instructions/log_event.rs diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index a5502e36e..693bf8205 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -9,7 +9,7 @@ pub mod control; pub mod ext; pub mod hash; pub mod lifecycle; -pub mod log; +pub mod log_event; pub mod memory; pub mod stack; pub mod state; @@ -164,7 +164,7 @@ 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::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*])?; + log_event::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*])?; m.pc += 1; Ok(()) }} diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index e1fb48b4d..c3a43658d 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,7 +1,10 @@ use cid::Cid; use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; -use fil_actors_runtime::{test_utils::*, ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{ + test_utils::{self, *}, + ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, +}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{BytesDe, BytesSer}; use fvm_shared::{address::Address, IDENTITY_HASH, IPLD_RAW}; @@ -24,6 +27,9 @@ pub fn init_construct_and_verify( ) -> 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]); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 448d56e6f..83129a412 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -43,6 +43,7 @@ fvm_sdk = { version = "3.0.0-alpha.19", optional = true } 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" @@ -89,4 +90,4 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static"] +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static", "pretty_env_logger"] diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index e2c93c286..6ff179a7b 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -123,6 +123,11 @@ 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 ActorExit { code: u32, data: RawBytes, From 1d62c67c3d3cdc3e4e54297253203d31745d59a4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 5 Jan 2023 18:00:20 -0800 Subject: [PATCH 228/339] EVM: fix the stack underflow check on swap (#1000) And add a bunch of tests. --- actors/evm/src/interpreter/stack.rs | 76 ++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index c220304c5..d8cfa3aca 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -98,7 +98,7 @@ impl Stack { #[inline] pub fn swap_top(&mut self, i: usize) -> Result<(), StatusCode> { let len = self.stack.len(); - if len < i { + if len <= i { return Err(StatusCode::StackUnderflow); } self.stack.swap(len - i - 1, len - 1); @@ -125,3 +125,77 @@ impl Default for Stack { 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_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_stack_swap_underflow() { + let mut stack = Stack::new(); + assert_eq!(stack.swap_top(1).unwrap_err(), StatusCode::StackUnderflow); + + stack.push(1.into()).unwrap(); + assert_eq!(stack.swap_top(1).unwrap_err(), StatusCode::StackUnderflow); + + stack.push(2.into()).unwrap(); + assert_eq!(stack.swap_top(2).unwrap_err(), StatusCode::StackUnderflow); +} + +#[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] +fn test_stack_dup_underflow() { + let mut stack = Stack::new(); + assert_eq!(stack.dup(1).unwrap_err(), StatusCode::StackUnderflow); + stack.push(1.into()).unwrap(); + assert_eq!(stack.dup(2).unwrap_err(), StatusCode::StackUnderflow); +} + +#[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(), StatusCode::StackOverflow); + assert_eq!(stack.dup(1).unwrap_err(), StatusCode::StackOverflow); + stack.swap_top(1).unwrap(); + assert_eq!(stack.pop().unwrap(), 1022); +} From 8473c527ed369331ad472faf250c13bfd4be9ee5 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 6 Jan 2023 19:55:07 -0800 Subject: [PATCH 229/339] EVM: Fix precompiles to output nothing on error (#998) * move error handling logic into call instead of from trait, remove unused CallActorError * use err instead of flattening to empty vec right away * fmt * add more logging to precompile calls * add test for empty failure * fmt --- .../evm/src/interpreter/instructions/call.rs | 17 +++-- actors/evm/src/interpreter/precompiles/evm.rs | 56 +++++++-------- actors/evm/src/interpreter/precompiles/mod.rs | 18 ++--- actors/evm/tests/precompile.rs | 70 +++++++++++++++++++ 4 files changed, 112 insertions(+), 49 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 66e84772a..caf8521e2 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -200,14 +200,19 @@ pub fn call_generic( if precompiles::Precompiles::::is_precompile(&dst) { let context = PrecompileContext { call_type: kind, gas_limit: effective_gas_limit(system, gas) }; + 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(512); + log::info!(target: "evm", "Calling Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", EthAddress::try_from(dst).unwrap_or(EthAddress([0xff; 20])), context, input_hex); + } - match precompiles::Precompiles::call_precompile(system, dst, input_data, context) - .map_err(StatusCode::from) - { + match precompiles::Precompiles::call_precompile(system, dst, input_data, context) { Ok(return_data) => (1, return_data), - Err(status) => { - let msg = format!("{}", status); - (0, msg.as_bytes().to_vec()) + Err(err) => { + log::error!(target: "evm", "Precompile failed: error {:?}", err); + // precompile failed, exit with reverted and no output + (0, vec![]) } } } else { diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 10d5abd10..013f0aa33 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -164,7 +164,7 @@ pub(super) fn modexp( Ok(output) } -pub(super) fn curve_to_vec(curve: G1) -> Vec { +pub(super) fn curve_to_vec(curve: G1) -> Result, PrecompileError> { AffineG1::from_jacobian(curve) .map(|product| { let mut output = vec![0; 64]; @@ -172,7 +172,7 @@ pub(super) fn curve_to_vec(curve: G1) -> Vec { product.y().to_big_endian(&mut output[32..64]).unwrap(); output }) - .unwrap_or_else(|| vec![0; 64]) + .ok_or(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) } /// add 2 points together on an elliptic curve @@ -185,7 +185,7 @@ pub(super) fn ec_add( let point1 = input_params.next_param_padded()?; let point2 = input_params.next_param_padded()?; - Ok(curve_to_vec(point1 + point2)) + curve_to_vec(point1 + point2) } /// multiply a point on an elliptic curve by a scalar value @@ -203,7 +203,7 @@ pub(super) fn ec_mul( Fr::new_mul_factor(data.into()) }; - Ok(curve_to_vec(point * scalar)) + curve_to_vec(point * scalar) } /// pairs multple groups of twisted bn curves @@ -515,25 +515,20 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); - assert_eq!(res, expected); + let res = ec_add(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) + )); // no input test let input = []; - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); - assert_eq!(res, expected); + let res = ec_add(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) + )); + // point not on curve fail let input = hex::decode( "\ @@ -576,19 +571,20 @@ mod tests { 0200000000000000000000000000000000000000000000000000000000000000", ) .unwrap(); - let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); - assert_eq!(&res, &vec![0; 64]); + let res = ec_mul(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) + )); // no input test let input = [0u8; 0]; - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); - assert_eq!(res, expected); + let res = ec_mul(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) + )); + // point not on curve fail let input = hex::decode( "\ diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 001c479e1..72ca9badd 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use fil_actors_runtime::runtime::Runtime; use substrate_bn::{CurveError, GroupError}; -use super::{instructions::call::CallKind, StatusCode, System, U256}; +use super::{instructions::call::CallKind, System, U256}; mod evm; mod fvm; @@ -106,22 +106,14 @@ impl Precompiles { #[derive(Debug)] pub enum PrecompileError { + // EVM precompile errors EcErr(CurveError), EcGroupErr(GroupError), - InvalidInput, - CallForbidden, IncorrectInputSize, OutOfGas, - CallActorError(StatusCode), -} - -impl From for StatusCode { - fn from(src: PrecompileError) -> Self { - match src { - PrecompileError::CallActorError(e) => e, - _ => StatusCode::PrecompileFailure, - } - } + // FVM precompile errors + InvalidInput, + CallForbidden, } #[derive(Debug, PartialEq, Eq)] diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index f5d908860..b595380d0 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -166,3 +166,73 @@ return assert!(result.is_empty()); rt.reset(); } + +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_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 result = util::invoke_contract(&mut rt, &U256::from(111).to_bytes()); + rt.verify(); + assert_eq!(&[1u8], result.as_slice()); + rt.reset(); +} From b6bf61cfae111ecc42408f51764857b82f4c0b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sun, 11 Dec 2022 18:55:20 +0000 Subject: [PATCH 230/339] add Hyperspace testnet parameters and build config. --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- Makefile | 3 +++ build.rs | 2 ++ runtime/build.rs | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f27745098..c1304439e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ] + 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/.github/workflows/release.yml b/.github/workflows/release.yml index 9b007046e..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', 'wallaby', 'devnet-wasm' ] + 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/Makefile b/Makefile index ee3907a13..f6dd1a99c 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,9 @@ bundle-devnet: deps-build 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 diff --git a/build.rs b/build.rs index 6278c7a3c..42c010789 100644 --- a/build.rs +++ b/build.rs @@ -58,6 +58,8 @@ fn network_name() -> String { 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") { diff --git a/runtime/build.rs b/runtime/build.rs index e2193242a..a8040bfb8 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -13,7 +13,7 @@ static NETWORKS: &[(&[&str], &[&str])] = &[ ), (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), ( - &["wallaby"], + &["wallaby", "hyperspace"], &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"], ), (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), From 93d940b6a0dd1383efb6c80ebf70909247ce802e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sun, 11 Dec 2022 20:03:52 +0000 Subject: [PATCH 231/339] makefile: fix all-bundles to cover hyperspace & wallaby. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f6dd1a99c..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 From 56339c2d869ea9224e25a2f56b570edcf10d478a Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 9 Jan 2023 09:24:26 -0800 Subject: [PATCH 232/339] EVM: Allow Null Addresses to convert into f4 Addresses (#1003) * account for null and native precompile address in eth to f4 address conversions * fmt & err message fix * be explicit about rejecting null address in EAM * add eam test for null address --- actors/eam/src/lib.rs | 24 ++++++++++++-- actors/evm/src/interpreter/address.rs | 33 +++++++++++++++++-- actors/evm/src/interpreter/precompiles/mod.rs | 22 +++++++++++-- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index d8e457953..644762ca5 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -63,6 +63,7 @@ impl EthAddress { #[inline] 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] } @@ -73,10 +74,15 @@ impl EthAddress { self.0[0] == 0xff && self.0[1..12].iter().all(|&i| i == 0) } + #[inline] + fn is_null(&self) -> bool { + self.0 == [0; 20] + } + /// Returns true if the EthAddress is "reserved" (cannot be assigned by the EAM). #[inline] fn is_reserved(&self) -> bool { - self.is_precompile() || self.is_id() + self.is_precompile() || self.is_id() || self.is_null() } } @@ -267,13 +273,27 @@ mod test { create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() ); - // Reject Precompile. + // Reject EVM Precompile. let mut new_addr = EthAddress([0; 20]); 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([0; 20]); + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); } #[test] diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index 1e5cc2195..34886d99a 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -5,6 +5,8 @@ use fvm_ipld_encoding::{serde, strict_bytes}; use fvm_shared::address::Address; use fvm_shared::ActorID; +use super::precompiles::is_reserved_precompile_address; + /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). /// /// TODO this type will eventually handle f4 address detection. @@ -38,10 +40,10 @@ impl TryFrom for Address { impl TryFrom<&EthAddress> for Address { type Error = StatusCode; fn try_from(addr: &EthAddress) -> Result { - if addr.0[..19] == [0; 19] { + if is_reserved_precompile_address(addr.0) { return Err(StatusCode::BadAddress(format!( - "cannot convert precompile {} to an f4 address", - addr.0[19] + "Cannot convert a precompile address: {:?} to an f4 address", + addr ))); } @@ -93,6 +95,8 @@ impl AsRef<[u8]> for EthAddress { #[cfg(test)] mod tests { + use fvm_shared::address::Address; + use crate::interpreter::address::EthAddress; use crate::U256; @@ -158,4 +162,27 @@ mod tests { vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) ] => None, } + + #[test] + #[allow(unused)] + fn precompile_reserved_conversion() { + // in range precompile addresses + let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); + Address::try_from(addr).expect_err("can't convert precompile into f4!"); + let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); + Address::try_from(addr).expect_err("can't convert precompile into f4!"); + + // can convert null address + let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000000")); + let _: Address = addr.try_into().unwrap(); + // can convert 0 index native prefix + let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000000")); + let _: Address = addr.try_into().unwrap(); + + // out of range, but reserved + let addr = EthAddress(hex_literal::hex!("fe000000000000000000000000000000000000aa")); + Address::try_from(addr).expect_err("can't convert precompile into f4!"); + let addr = EthAddress(hex_literal::hex!("00000000000000000000000000000000000000aa")); + Address::try_from(addr).expect_err("can't convert precompile into f4!"); + } } diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 72ca9badd..28fc6ffcd 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -61,6 +61,13 @@ const fn gen_native_precompiles() -> [PrecompileFn; 4] { } } +pub fn is_reserved_precompile_address(addr: [u8; 20]) -> bool { + let [prefix, middle @ .., index] = addr; + (prefix == 0x00 || prefix == NATIVE_PRECOMPILE_ADDRESS_PREFIX) + && middle == [0u8; 18] + && index > 0 +} + pub struct Precompiles(PhantomData); impl Precompiles { @@ -70,8 +77,8 @@ impl Precompiles { fn lookup_precompile(addr: &[u8; 32]) -> Option> { // unrwap will never panic, 32 - 12 = 20 let addr: [u8; 20] = addr[12..].try_into().unwrap(); - let [prefix, middle @ .., index] = addr; - if middle == [0u8; 18] && index > 0 { + let [prefix, _m @ .., index] = addr; + if is_reserved_precompile_address(addr) { let index = index as usize - 1; match prefix { NATIVE_PRECOMPILE_ADDRESS_PREFIX => Self::NATIVE_PRECOMPILES.get(index), @@ -146,7 +153,7 @@ impl NativeType { mod test { use fil_actors_runtime::test_utils::MockRuntime; - use crate::interpreter::address::EthAddress; + use crate::interpreter::{address::EthAddress, precompiles::is_reserved_precompile_address}; use super::Precompiles; @@ -154,18 +161,21 @@ mod test { fn is_native_precompile() { let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); assert!(Precompiles::::is_precompile(&addr.as_evm_word())); + assert!(is_reserved_precompile_address(addr.0)); } #[test] fn is_evm_precompile() { let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); assert!(Precompiles::::is_precompile(&addr.as_evm_word())); + assert!(is_reserved_precompile_address(addr.0)); } #[test] fn is_over_precompile() { let addr = EthAddress(hex_literal::hex!("ff00000000000000000000000000000000000001")); assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); + assert!(!is_reserved_precompile_address(addr.0)); } #[test] @@ -174,12 +184,15 @@ mod test { let native_addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000000")); assert!(!Precompiles::::is_precompile(ð_addr.as_evm_word())); assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); + assert!(!is_reserved_precompile_address(eth_addr.0)); + assert!(!is_reserved_precompile_address(native_addr.0)); } #[test] fn between_precompile() { let addr = EthAddress(hex_literal::hex!("a000000000000000000000000000000000000001")); assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); + assert!(!is_reserved_precompile_address(addr.0)); } #[test] @@ -188,5 +201,8 @@ mod test { let native_addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000020")); assert!(!Precompiles::::is_precompile(ð_addr.as_evm_word())); assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); + // reserved doesn't check index is within range + assert!(is_reserved_precompile_address(eth_addr.0)); + assert!(is_reserved_precompile_address(native_addr.0)); } } From 84383b747a848e067a876b373d8f2f0b0fedb47e Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 10 Jan 2023 08:05:14 -0800 Subject: [PATCH 233/339] EVM: Pad output of resolve_address (#1005) pad output of resolve_address, update doc comments --- actors/evm/src/interpreter/precompiles/fvm.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index e771902b9..d5aeb81a3 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -116,7 +116,7 @@ pub(super) fn get_randomness( } /// Read BE encoded low u64 ID address from a u256 word -/// Looks up and returns the encoded f4 addresses of an ID address, returning empty array if not found +/// 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], @@ -133,9 +133,9 @@ pub(super) fn lookup_delegated_address( Ok(ab) } -/// Reads a FIL encoded address -/// Resolves a FIL encoded address into an ID address -/// returns BE encoded u64 or empty array if nothing found +/// Reads a FIL 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. pub(super) fn resolve_address( system: &mut System, input: &[u8], @@ -148,7 +148,11 @@ pub(super) fn resolve_address( Ok(o) => o, Err(_) => return Ok(Vec::new()), }; - Ok(system.rt.resolve_address(&addr).map(|a| a.to_be_bytes().to_vec()).unwrap_or_default()) + Ok(system + .rt + .resolve_address(&addr) + .map(|a| U256::from(a).to_bytes().to_vec()) + .unwrap_or_default()) } /// Errors: From e33ec94f0e12d07a9e1e1af80f57b0ce34134832 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 10 Jan 2023 17:28:45 -0800 Subject: [PATCH 234/339] EVM: add some tests for native precompiles (#961) * add few details in docs, simple zeroed bytes test * dont return error string to output value for precompiles * add lookup_address test * fix changes from merge, dont change precompile error return yet * fmt --- actors/evm/src/interpreter/precompiles/fvm.rs | 4 +- .../src/interpreter/precompiles/parameter.rs | 14 ++++ actors/evm/tests/precompile.rs | 77 +++++++++++++++++-- actors/evm/tests/util.rs | 8 +- 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index d5aeb81a3..ffbf1f7ad 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -133,8 +133,8 @@ pub(super) fn lookup_delegated_address( Ok(ab) } -/// Reads a FIL encoded address. -/// Resolves a FIL encoded address into an ID address. +/// Reads a FIL (i.e. f0xxx, f4x1xxx) 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. pub(super) fn resolve_address( system: &mut System, diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/interpreter/precompiles/parameter.rs index 7c10ee654..5c82b7323 100644 --- a/actors/evm/src/interpreter/precompiles/parameter.rs +++ b/actors/evm/src/interpreter/precompiles/parameter.rs @@ -213,3 +213,17 @@ impl<'a, T: Sized + Copy, const CHUNK_SIZE: usize> PaddedChunks<'a, T, CHUNK_SIZ self.next().map(|p| Parameter::::from(p).0).ok_or(PrecompileError::IncorrectInputSize) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_assert_zero_bytes() { + let mut bytes = [0u8; 32]; + assert_zero_bytes::<32>(&bytes).unwrap(); + bytes[31] = 1; + assert_zero_bytes::<31>(&bytes).unwrap(); + assert_zero_bytes::<32>(&bytes).expect_err("expected error from nonzero byte"); + } +} diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index b595380d0..f10da3999 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -1,6 +1,6 @@ mod asm; -use evm::interpreter::U256; +use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, @@ -9,6 +9,7 @@ use fil_actors_runtime::test_utils::{ use fvm_shared::address::Address as FILAddress; mod util; +use util::id_to_vec; #[allow(dead_code)] pub fn magic_precompile_contract() -> Vec { @@ -108,7 +109,7 @@ push1 0x00 return "#; - asm::new_contract("native_precompiles", init, body).unwrap() + asm::new_contract("native_actor_type", init, body).unwrap() }; use evm::interpreter::precompiles::NativeType; @@ -139,10 +140,6 @@ return let other_target = FILAddress::new_id(104); rt.set_address_actor_type(other_target, *MULTISIG_ACTOR_CODE_ID); - fn id_to_vec(src: &FILAddress) -> Vec { - U256::from(src.id().unwrap()).to_bytes().to_vec() - } - fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { rt.expect_gas_available(10_000_000_000u64); let result = util::invoke_contract(rt, &id_to_vec(&id)); @@ -217,6 +214,74 @@ return asm::new_contract("native_precompiles", init, body).unwrap() } +#[test] +fn test_native_lookup_delegated_address() { + let bytecode = { + let init = ""; + let body = r#" + +# get call payload size +calldatasize +# store payload to mem 0x00 +push1 0x00 +push1 0x00 +calldatacopy + +push1 0x20 # out size +push1 0xA0 # out off +calldatasize # in size +push1 0x00 # in off +push1 0x00 # value +# dst (lookup_delegated_address precompile) +push20 0xfe00000000000000000000000000000000000002 +push1 0x00 # gas +call + +# copy result to mem 0x00 +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +# return +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("native_lookup_delegated_address", init, body).unwrap() + }; + let mut rt = util::construct_and_verify(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.add_delegated_address(evm_target, 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.add_delegated_address(unknown_target, unknown_del); + + fn test_reslove(rt: &mut MockRuntime, id: FILAddress, expected: Vec) { + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(rt, &id_to_vec(&id)); + rt.verify(); + assert_eq!(expected, result.as_slice()); + rt.reset(); + } + + test_reslove(&mut rt, evm_target, evm_del.to_bytes()); + test_reslove(&mut rt, unknown_target, unknown_del.to_bytes()); + test_reslove(&mut rt, FILAddress::new_id(11111), Vec::new()); + + // invalid input + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(&mut rt, &[0xff; 42]); + rt.verify(); + assert_eq!(Vec::::new(), result); + rt.reset(); +} + #[test] fn test_precompile_failure() { let bytecode = resolve_address_contract(); diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index c3a43658d..25b4cde68 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,4 +1,5 @@ use cid::Cid; +use evm::interpreter::U256; use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; use fil_actors_runtime::{ @@ -81,7 +82,7 @@ pub fn invoke_contract_expect_abort(rt: &mut MockRuntime, input_data: &[u8], exp evm::Method::InvokeContract as u64, IpldBlock::serialize_cbor(&BytesSer(input_data)).unwrap(), ) - .expect_err(&format!("expected contract to fail with {}", expect)); + .expect_err(&format!("expected contract to fail with {:?}", expect)); rt.verify(); // REMOVEME so this is jank... (just copies err creation from execute in lib.rs) assert_eq!(err, ActorError::unspecified(format!("EVM execution error: {expect:?}"))) @@ -94,6 +95,11 @@ pub fn dispatch_num_word(method_num: u8) -> [u8; 32] { 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()); From f881c9c101497b7cb5132e9a7808d8cbd9ef1f11 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Jan 2023 13:50:57 -0800 Subject: [PATCH 235/339] EVM: userspace selfdestruct (#1001) Implements filecoin-project/ref-fvm#1221 --- actors/eam/src/ext.rs | 2 + actors/eam/src/lib.rs | 20 +- actors/eam/tests/create.rs | 61 ++- actors/evm/src/interpreter/execution.rs | 5 - .../evm/src/interpreter/instructions/call.rs | 35 +- .../evm/src/interpreter/instructions/ext.rs | 23 +- .../src/interpreter/instructions/lifecycle.rs | 20 +- actors/evm/src/interpreter/output.rs | 1 - actors/evm/src/interpreter/system.rs | 73 +++- actors/evm/src/lib.rs | 188 ++++---- actors/evm/src/state.rs | 30 ++ actors/evm/tests/contracts/Selfdestruct.abi | 2 +- actors/evm/tests/contracts/Selfdestruct.bin | 2 +- .../tests/contracts/Selfdestruct.signatures | 1 + actors/evm/tests/contracts/selfdestruct.hex | 2 +- actors/evm/tests/contracts/selfdestruct.sol | 3 + .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 17263 -> 17977 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 26774 -> 27932 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 13584 -> 13830 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 14451 -> 14779 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 14440 -> 14587 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 14093 -> 14204 bytes .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 19532 -> 19949 bytes .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 22174 -> 22537 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22146 -> 22838 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 16915 -> 17373 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 19133 -> 19457 bytes actors/evm/tests/selfdestruct.rs | 92 +++- runtime/src/runtime/fvm.rs | 4 + runtime/src/runtime/mod.rs | 3 + runtime/src/test_utils.rs | 4 + test_vm/src/lib.rs | 4 + test_vm/tests/evm_test.rs | 18 +- 44 files changed, 2059 insertions(+), 1774 deletions(-) diff --git a/actors/eam/src/ext.rs b/actors/eam/src/ext.rs index 0650f8695..0d087048f 100644 --- a/actors/eam/src/ext.rs +++ b/actors/eam/src/ext.rs @@ -35,4 +35,6 @@ pub mod evm { pub bytecode: RawBytes, pub input_data: RawBytes, } + + pub const RESURRECT_METHOD: u64 = 7; } diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 2c0796657..1d32d68d5 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -1,6 +1,9 @@ use std::iter; -use ext::init::{Exec4Params, Exec4Return}; +use ext::{ + evm::RESURRECT_METHOD, + init::{Exec4Params, Exec4Return}, +}; use fil_actors_runtime::{actor_dispatch_unrestricted, deserialize_block, AsActorError}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::error::ExitCode; @@ -104,7 +107,7 @@ pub struct Create2Params { #[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq)] pub struct Return { pub actor_id: ActorID, - pub robust_address: Address, + pub robust_address: Option
, pub eth_address: EthAddress, } @@ -115,7 +118,7 @@ impl Return { fn from_exec4(exec4: Exec4Return, eth_address: EthAddress) -> Self { Self { actor_id: exec4.id_address.id().unwrap(), - robust_address: exec4.robust_address, + robust_address: Some(exec4.robust_address), eth_address, } } @@ -146,8 +149,17 @@ fn create_actor( if new_addr.is_reserved() { return Err(ActorError::forbidden("cannot create address with a reserved prefix".into())); } + let constructor_params = RawBytes::serialize(EvmConstructorParams { creator, initcode: initcode.into() })?; + let value = rt.message().value_received(); + + // Try to resurrect it if it already exists. + let f4_addr = Address::new_delegated(EAM_ACTOR_ID, &new_addr.0).unwrap(); + if let Some(id) = rt.resolve_address(&f4_addr) { + rt.send(&Address::new_id(id), RESURRECT_METHOD, constructor_params.into(), value)?; + return Ok(Return { actor_id: id, robust_address: None, eth_address: new_addr }); + } let init_params = Exec4Params { code_cid: rt.get_code_cid_for_type(Type::EVM), @@ -159,7 +171,7 @@ fn create_actor( &INIT_ACTOR_ADDR, ext::init::EXEC4_METHOD, IpldBlock::serialize_cbor(&init_params)?, - rt.message().value_received(), + value, )?)?; Ok(Return::from_exec4(ret, new_addr)) diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index ef802fc3d..8166e1bc8 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -1,3 +1,4 @@ +use eam::ext::evm::RESURRECT_METHOD; use eam::ext::init::{Exec4Params, Exec4Return, EXEC4_METHOD}; use eam::{ compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, @@ -57,8 +58,11 @@ fn call_create() { .deserialize::() .unwrap(); - let expected_return = - Return { actor_id: 111, robust_address: Address::new_id(0), eth_address: new_eth_addr }; + let expected_return = Return { + actor_id: 111, + robust_address: Some(Address::new_id(0)), + eth_address: new_eth_addr, + }; assert_eq!(result, expected_return); rt.verify(); @@ -91,6 +95,57 @@ fn call_create() { } } +#[test] +fn call_resurrect() { + let mut rt = construct_and_verify(); + + let caller_id_addr = Address::new_id(110); + let caller_eth_addr = + eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let caller_f4_eth_addr = Address::new_delegated(10, &caller_eth_addr.0).unwrap(); + rt.add_delegated_address(caller_id_addr, caller_f4_eth_addr); + + rt.set_caller(*EVM_ACTOR_CODE_ID, caller_id_addr); + + let target_id_addr = Address::new_id(111); + let target_eth_addr = compute_address_create(&rt, &caller_eth_addr, 0); + let target_f4_eth_addr = Address::new_delegated(10, &target_eth_addr.0).unwrap(); + rt.add_delegated_address(target_id_addr, target_f4_eth_addr); + + rt.expect_validate_caller_any(); + + let initcode = vec![0xff]; + + let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; + + let params = EvmConstructorParams { creator: caller_eth_addr, initcode: initcode.into() }; + + rt.expect_send( + target_id_addr, + RESURRECT_METHOD, + IpldBlock::serialize_cbor(¶ms).unwrap(), + TokenAmount::from_atto(0), + None, + ExitCode::OK, + ); + + let result = rt + .call::( + eam::Method::Create as u64, + IpldBlock::serialize_cbor(&create_params).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize::() + .unwrap(); + + let expected_return = + Return { actor_id: 111, robust_address: None, eth_address: target_eth_addr }; + + assert_eq!(result, expected_return); + rt.verify(); +} + #[test] fn call_create2() { let mut rt = construct_and_verify(); @@ -149,7 +204,7 @@ fn call_create2() { let expected_return = Return { actor_id: 111, - robust_address: Address::new_id(0), + robust_address: Some(Address::new_id(0)), eth_address: EthAddress(subaddress.try_into().unwrap()), }; diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 063685db3..bfc689b97 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,7 +1,5 @@ #![allow(dead_code)] -use fvm_shared::address::Address as FilecoinAddress; - use super::address::EthAddress; use { super::instructions, @@ -20,8 +18,6 @@ pub struct ExecutionState { pub memory: Memory, pub input_data: Bytes, pub return_data: Bytes, - /// Indicates whether the contract called SELFDESTRUCT, providing the beneficiary. - pub selfdestroyed: Option, /// The EVM address of the caller. pub caller: EthAddress, /// The EVM address of the receiver. @@ -35,7 +31,6 @@ impl ExecutionState { memory: Memory::default(), input_data, return_data: Default::default(), - selfdestroyed: None, caller, receiver, } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index bbe4e4c3b..f9f22f4fd 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -279,22 +279,25 @@ pub fn call_generic( CallKind::DelegateCall => match get_contract_type(system.rt, dst) { ContractType::EVM(dst_addr) => { // If we're calling an actual EVM actor, get its code. - let 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, - }; - system.send( - &system.rt.message().receiver(), - Method::InvokeContractDelegate as u64, - IpldBlock::serialize_cbor(¶ms)?, - TokenAmount::from(&value), - Some(effective_gas_limit(system, gas)), - SendFlags::default(), - ) + 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, + }; + system.send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + IpldBlock::serialize_cbor(¶ms)?, + TokenAmount::from(&value), + Some(effective_gas_limit(system, gas)), + SendFlags::default(), + ) + } 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. diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index d1a3da42c..d86a759fe 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -136,7 +136,7 @@ pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { pub fn get_evm_bytecode_cid( system: &mut System, addr: &Address, -) -> Result { +) -> Result, ActorError> { deserialize_block(system.send( addr, crate::Method::GetBytecode as u64, @@ -151,12 +151,17 @@ pub fn get_evm_bytecode( system: &mut System, addr: &Address, ) -> Result, StatusCode> { - let cid = get_evm_bytecode_cid(system, addr)?; - let raw_bytecode = system - .rt - .store() - .get(&cid) // TODO this is inefficient; should call stat here. - .map_err(|e| StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)))? - .ok_or_else(|| ActorError::not_found("bytecode block not found".to_string()))?; - Ok(raw_bytecode) + 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. + .map_err(|e| { + StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)) + })? + .ok_or_else(|| ActorError::not_found("bytecode block not found".to_string()))?; + Ok(raw_bytecode) + } else { + Ok(Vec::new()) + } } diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index eda82eb20..1afb5b311 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,7 +1,7 @@ use bytes::Bytes; use fil_actors_runtime::deserialize_block; -use fil_actors_runtime::{BURNT_FUNDS_ACTOR_ADDR, EAM_ACTOR_ADDR}; +use fil_actors_runtime::EAM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{strict_bytes, tuple::*}; use fvm_shared::sys::SendFlags; @@ -163,16 +163,18 @@ pub fn selfdestruct( return Err(StatusCode::StaticModeViolation); } - // Try to give funds to the beneficiary. We don't use the `delete_actor` syscall to do this - // because that won't auto-create the beneficiary (and will fail if, for some reason, we can't - // send them the funds). - // - // If we fail, we'll just burn the funds. Yes, this is what the EVM does. + // Try to give funds to the beneficiary. If this fails, we just keep them. if let Ok(addr) = EthAddress::from(beneficiary).try_into() { let balance = system.rt.current_balance(); let _ = system.rt.send(&addr, METHOD_SEND, None, balance); } - // Now try to delete ourselves. If this fails, we abort execution. - system.rt.delete_actor(&BURNT_FUNDS_ACTOR_ADDR)?; - Ok(Output { outcome: Outcome::Delete, return_data: Bytes::new() }) + + // 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: Bytes::new() }) } diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index d052df8ea..fb06a2b57 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -6,7 +6,6 @@ pub enum Outcome { #[default] Return, Revert, - Delete, } /// Output of EVM execution. diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 08deb7027..9f50311bf 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -18,7 +18,7 @@ use fvm_shared::{ use multihash::{Code, Multihash}; use once_cell::unsync::OnceCell; -use crate::state::State; +use crate::state::{State, Tombstone}; use super::{address::EthAddress, Bytecode}; @@ -111,29 +111,63 @@ pub struct System<'r, RT: Runtime> { pub readonly: bool, /// Randomness taken from the current epoch of chain randomness randomness: OnceCell<[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 + tombstone: Option, } impl<'r, RT: Runtime> System<'r, RT> { - /// Create the actor. - pub fn create(rt: &'r mut RT) -> Result + fn new(rt: &'r mut RT, readonly: bool) -> Self 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")); - } let store = rt.store().clone(); - Ok(Self { + Self { rt, slots: StateKamt::new_with_config(store, KAMT_CONFIG.clone()), nonce: 1, saved_state_root: None, bytecode: None, - readonly: read_only, + readonly, randomness: OnceCell::new(), - }) + 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. @@ -141,7 +175,6 @@ impl<'r, RT: Runtime> System<'r, RT> { where RT::Blockstore: Clone, { - let read_only = rt.read_only(); let store = rt.store().clone(); let state_root = rt.get_state_root()?; let state: State = store @@ -149,6 +182,14 @@ impl<'r, RT: Runtime> System<'r, RT> { .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()) @@ -158,6 +199,7 @@ impl<'r, RT: Runtime> System<'r, RT> { bytecode: Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)), readonly: read_only, randomness: OnceCell::new(), + tombstone: state.tombstone, }) } @@ -213,6 +255,7 @@ impl<'r, RT: Runtime> System<'r, RT> { "failed to flush contract state", )?, nonce: self.nonce, + tombstone: self.tombstone, }, Code::Blake2b256, ) @@ -370,6 +413,12 @@ impl<'r, RT: Runtime> System<'r, RT> { )?) }) } + + /// Mark ourselves as "selfdestructed". + pub fn mark_selfdestructed(&mut self) { + self.saved_state_root = None; + self.tombstone = Some(crate::current_tombstone(self.rt)); + } } pub fn load_bytecode(bs: &BS, cid: &Cid) -> Result, ActorError> { diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index e38018a0d..4e43b8ba2 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,11 +1,12 @@ -use std::iter; - -use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ADDR, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; -use fvm_shared::address::{Address, Payload}; +use fvm_shared::address::Address; +use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::error::ExitCode; +use interpreter::instructions::ext::EMPTY_EVM_HASH; use interpreter::{address::EthAddress, system::load_bytecode}; +use multihash::Multihash; use crate::interpreter::output::Outcome; @@ -14,7 +15,6 @@ mod state; use { crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, - crate::state::State, bytes::Bytes, cid::Cid, fil_actors_runtime::{ @@ -28,6 +28,8 @@ use { num_traits::FromPrimitive, }; +pub use state::*; + #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EvmContractActor); @@ -57,78 +59,96 @@ pub enum Method { GetStorageAt = 4, InvokeContractDelegate = 5, GetBytecodeHash = 6, + Resurrect = 7, } 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)) +} + +pub 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 mut exec_state = ExecutionState::new(caller, receiver_eth_addr, Bytes::new()); + + // identify bytecode valid jump destinations + let initcode = Bytecode::new(initcode); + + // invoke the contract constructor + let output = execute(&initcode, &mut exec_state, system).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; + + 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(), + )), + } +} + impl EvmContractActor { pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> where RT: Runtime, RT::Blockstore: Clone, { - rt.validate_immediate_caller_is(iter::once(&INIT_ACTOR_ADDR))?; - - // Assert we are constructed with a delegated address from the EAM - let receiver = rt.message().receiver(); - let delegated_addr = - rt.lookup_delegated_address(receiver.id().unwrap()).ok_or_else(|| { - ActorError::assertion_failed(format!( - "EVM actor {} created without a delegated address", - receiver - )) - })?; - let delegated_addr = match delegated_addr.payload() { - Payload::Delegated(delegated) if delegated.namespace() == EAM_ACTOR_ID => { - // sanity check - assert_eq!(delegated.subaddress().len(), 20); - Ok(*delegated) - } - _ => Err(ActorError::assertion_failed(format!( - "EVM actor with delegated address {} created not namespaced to the EAM {}", - delegated_addr, EAM_ACTOR_ID, - ))), - }?; - let receiver_eth_addr = { - let subaddr: [u8; 20] = delegated_addr.subaddress().try_into().map_err(|_| { - ActorError::assertion_failed(format!( - "expected 20 byte EVM address, found {} bytes", - delegated_addr.subaddress().len() - )) - })?; - EthAddress(subaddr) - }; - - let mut system = System::create(rt)?; - // If we have no code, save the state and return. - if params.initcode.is_empty() { - return system.flush(); - } - - // create a new execution context - let mut exec_state = ExecutionState::new(params.creator, receiver_eth_addr, Bytes::new()); - - // identify bytecode valid jump destinations - let initcode = Bytecode::new(params.initcode.into()); - - // invoke the contract constructor - let output = execute(&initcode, &mut exec_state, &mut system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; + rt.validate_immediate_caller_is(&[INIT_ACTOR_ADDR])?; + initialize_evm_contract(&mut System::create(rt)?, params.creator, params.initcode.into()) + } - 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(), - )), - Outcome::Delete => Ok(()), - } + 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( @@ -189,7 +209,6 @@ impl EvmContractActor { "contract reverted".to_string(), IpldBlock::serialize_cbor(&BytesSer(&output.return_data)).unwrap(), )), - Outcome::Delete => Ok(Vec::new()), } } @@ -207,21 +226,31 @@ impl EvmContractActor { Self::invoke_contract(rt, &input, None, None) } - pub fn bytecode(rt: &mut impl Runtime) -> Result { + /// 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()?; - Ok(state.bytecode) + 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()?; - let state: State = rt.state()?; // return value must be either keccak("") or keccak(bytecode) - Ok(state.bytecode_hash) + let state: State = rt.state()?; + if is_dead(rt, &state) { + Ok(Multihash::wrap(SupportedHashes::Keccak256 as u64, &EMPTY_EVM_HASH).unwrap()) + } else { + Ok(state.bytecode_hash) + } } pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result @@ -233,6 +262,7 @@ impl EvmContractActor { // 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") @@ -306,8 +336,8 @@ impl ActorCode for EvmContractActor { Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) } Some(Method::GetBytecode) => { - let cid = Self::bytecode(rt)?; - Ok(IpldBlock::serialize_cbor(&cid)?) + let ret = Self::bytecode(rt)?; + Ok(IpldBlock::serialize_cbor(&ret)?) } Some(Method::GetBytecodeHash) => { let multihash = Self::bytecode_hash(rt)?; @@ -337,6 +367,16 @@ impl ActorCode for EvmContractActor { )?; 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 => Err(actor_error!(unhandled_message; "Invalid method")), } } @@ -350,6 +390,8 @@ pub struct ConstructorParams { pub initcode: RawBytes, } +pub type ResurrectParams = ConstructorParams; + #[derive(Serialize_tuple, Deserialize_tuple)] pub struct DelegateCallParams { pub code: Cid, diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 18da638eb..6624d2ffa 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -1,9 +1,20 @@ +use fvm_shared::ActorID; + use { cid::Cid, fvm_ipld_encoding::tuple::*, 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, +} + /// Data stored by an EVM contract. /// This runs on the fvm-evm-runtime actor code cid. #[derive(Debug, Serialize_tuple, Deserialize_tuple)] @@ -23,4 +34,23 @@ pub struct State { /// 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, } diff --git a/actors/evm/tests/contracts/Selfdestruct.abi b/actors/evm/tests/contracts/Selfdestruct.abi index f50f1486a..8ed3d9ccd 100644 --- a/actors/evm/tests/contracts/Selfdestruct.abi +++ b/actors/evm/tests/contracts/Selfdestruct.abi @@ -1 +1 @@ -[{"inputs":[],"name":"die","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[{"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 index 14bba8f84..fd4eaf0c4 100644 --- a/actors/evm/tests/contracts/Selfdestruct.bin +++ b/actors/evm/tests/contracts/Selfdestruct.bin @@ -1 +1 @@ -6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea264697066735822122036b3878598bb1b4b4b1c9f4835387cf15c45df30d5e4811ac8f52533a1ec3a4664736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b5060f58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806335f46994146037578063901717d114603f575b600080fd5b603d6059565b005b60456086565b6040516050919060a6565b60405180910390f35b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16ff5b60006001905090565b6000819050919050565b60a081608f565b82525050565b600060208201905060b960008301846099565b9291505056fea26469706673582212205e642139367a73faa34d4286b3d045c006891ee95bed3ffc39e14604bc9c3a7d64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Selfdestruct.signatures b/actors/evm/tests/contracts/Selfdestruct.signatures index 27716607f..e91c1e851 100644 --- a/actors/evm/tests/contracts/Selfdestruct.signatures +++ b/actors/evm/tests/contracts/Selfdestruct.signatures @@ -1,2 +1,3 @@ Function signatures: 35f46994: die() +901717d1: one() diff --git a/actors/evm/tests/contracts/selfdestruct.hex b/actors/evm/tests/contracts/selfdestruct.hex index 14bba8f84..fd4eaf0c4 100644 --- a/actors/evm/tests/contracts/selfdestruct.hex +++ b/actors/evm/tests/contracts/selfdestruct.hex @@ -1 +1 @@ -6080604052348015600f57600080fd5b5060988061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806335f4699414602d575b600080fd5b60336035565b005b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16fffea264697066735822122036b3878598bb1b4b4b1c9f4835387cf15c45df30d5e4811ac8f52533a1ec3a4664736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b5060f58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806335f46994146037578063901717d114603f575b600080fd5b603d6059565b005b60456086565b6040516050919060a6565b60405180910390f35b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16ff5b60006001905090565b6000819050919050565b60a081608f565b82525050565b600060208201905060b960008301846099565b9291505056fea26469706673582212205e642139367a73faa34d4286b3d045c006891ee95bed3ffc39e14604bc9c3a7d64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/selfdestruct.sol b/actors/evm/tests/contracts/selfdestruct.sol index 203873576..7256128f3 100644 --- a/actors/evm/tests/contracts/selfdestruct.sol +++ b/actors/evm/tests/contracts/selfdestruct.sol @@ -7,4 +7,7 @@ contract Selfdestruct { payable(address(0xFF000000000000000000000000000000000003E9)) ); } + function one() pure public returns (uint256){ + return 0x1; + } } diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 881e1fc66..28e422b8f 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2477,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2269,"get_count":3,"put_bytes":265,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2273,"get_count":3,"put_bytes":269,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2277,"get_count":3,"put_bytes":273,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2281,"get_count":3,"put_bytes":277,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2285,"get_count":3,"put_bytes":281,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2289,"get_count":3,"put_bytes":286,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2294,"get_count":3,"put_bytes":290,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2298,"get_count":3,"put_bytes":464,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":484,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":489,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":493,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":459,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2467,"get_count":4,"put_bytes":463,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2471,"get_count":4,"put_bytes":467,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2475,"get_count":4,"put_bytes":471,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2479,"get_count":4,"put_bytes":475,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2483,"get_count":4,"put_bytes":479,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2487,"get_count":4,"put_bytes":484,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":488,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2496,"get_count":4,"put_bytes":597,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":605,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":613,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2621,"get_count":5,"put_bytes":617,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2625,"get_count":5,"put_bytes":622,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2630,"get_count":5,"put_bytes":626,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":523,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2531,"get_count":4,"put_bytes":527,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2535,"get_count":4,"put_bytes":531,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2539,"get_count":4,"put_bytes":535,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2543,"get_count":4,"put_bytes":539,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2547,"get_count":4,"put_bytes":543,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2551,"get_count":4,"put_bytes":548,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2556,"get_count":4,"put_bytes":552,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2560,"get_count":4,"put_bytes":661,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2669,"get_count":5,"put_bytes":665,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2673,"get_count":5,"put_bytes":669,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2677,"get_count":5,"put_bytes":673,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2681,"get_count":5,"put_bytes":677,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2685,"get_count":5,"put_bytes":681,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2689,"get_count":5,"put_bytes":686,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2694,"get_count":5,"put_bytes":690,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2554,"get_count":4,"put_bytes":587,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2595,"get_count":4,"put_bytes":591,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2599,"get_count":4,"put_bytes":595,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2603,"get_count":4,"put_bytes":599,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2607,"get_count":4,"put_bytes":603,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2611,"get_count":4,"put_bytes":607,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2615,"get_count":4,"put_bytes":612,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2620,"get_count":4,"put_bytes":616,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2624,"get_count":4,"put_bytes":725,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2733,"get_count":5,"put_bytes":729,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2737,"get_count":5,"put_bytes":733,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2741,"get_count":5,"put_bytes":737,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2745,"get_count":5,"put_bytes":741,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2749,"get_count":5,"put_bytes":745,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2753,"get_count":5,"put_bytes":750,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2758,"get_count":5,"put_bytes":754,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2618,"get_count":4,"put_bytes":651,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2659,"get_count":4,"put_bytes":655,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2663,"get_count":4,"put_bytes":659,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2667,"get_count":4,"put_bytes":663,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2671,"get_count":4,"put_bytes":667,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2675,"get_count":4,"put_bytes":671,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2679,"get_count":4,"put_bytes":676,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2684,"get_count":4,"put_bytes":680,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2688,"get_count":4,"put_bytes":789,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2797,"get_count":5,"put_bytes":793,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2801,"get_count":5,"put_bytes":797,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2805,"get_count":5,"put_bytes":801,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2809,"get_count":5,"put_bytes":805,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2813,"get_count":5,"put_bytes":809,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2817,"get_count":5,"put_bytes":814,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2822,"get_count":5,"put_bytes":818,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2682,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":460,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2468,"get_count":4,"put_bytes":464,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":481,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2489,"get_count":4,"put_bytes":485,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":595,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":603,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2611,"get_count":5,"put_bytes":607,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2615,"get_count":5,"put_bytes":611,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2619,"get_count":5,"put_bytes":615,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2623,"get_count":5,"put_bytes":620,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":588,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2596,"get_count":5,"put_bytes":592,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2600,"get_count":5,"put_bytes":596,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2353,"get_count":3,"put_bytes":496,"put_count":3}} -{"i":1,"series":2,"stats":{"get_bytes":2504,"get_count":4,"put_bytes":500,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":2508,"get_count":4,"put_bytes":504,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":2512,"get_count":4,"put_bytes":508,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":2516,"get_count":4,"put_bytes":512,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":2520,"get_count":4,"put_bytes":516,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":2524,"get_count":4,"put_bytes":521,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":2529,"get_count":4,"put_bytes":525,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":2533,"get_count":4,"put_bytes":697,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":705,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":2713,"get_count":5,"put_bytes":709,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":2717,"get_count":5,"put_bytes":713,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":717,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":2725,"get_count":5,"put_bytes":722,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":2730,"get_count":5,"put_bytes":726,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":690,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":2698,"get_count":5,"put_bytes":694,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":2702,"get_count":5,"put_bytes":698,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":2706,"get_count":5,"put_bytes":702,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":2710,"get_count":5,"put_bytes":706,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":2714,"get_count":5,"put_bytes":710,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":2718,"get_count":5,"put_bytes":715,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":2723,"get_count":5,"put_bytes":719,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":2727,"get_count":5,"put_bytes":828,"put_count":5}} -{"i":25,"series":2,"stats":{"get_bytes":2836,"get_count":6,"put_bytes":832,"put_count":5}} -{"i":26,"series":2,"stats":{"get_bytes":2840,"get_count":6,"put_bytes":836,"put_count":5}} -{"i":27,"series":2,"stats":{"get_bytes":2844,"get_count":6,"put_bytes":840,"put_count":5}} -{"i":28,"series":2,"stats":{"get_bytes":2848,"get_count":6,"put_bytes":844,"put_count":5}} -{"i":29,"series":2,"stats":{"get_bytes":2852,"get_count":6,"put_bytes":848,"put_count":5}} -{"i":30,"series":2,"stats":{"get_bytes":2856,"get_count":6,"put_bytes":853,"put_count":5}} -{"i":31,"series":2,"stats":{"get_bytes":2861,"get_count":6,"put_bytes":857,"put_count":5}} -{"i":32,"series":2,"stats":{"get_bytes":2721,"get_count":5,"put_bytes":754,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":2762,"get_count":5,"put_bytes":758,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":2766,"get_count":5,"put_bytes":762,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":2770,"get_count":5,"put_bytes":766,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":2774,"get_count":5,"put_bytes":770,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":2778,"get_count":5,"put_bytes":774,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":2782,"get_count":5,"put_bytes":779,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":2787,"get_count":5,"put_bytes":783,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":2791,"get_count":5,"put_bytes":892,"put_count":5}} -{"i":41,"series":2,"stats":{"get_bytes":2900,"get_count":6,"put_bytes":896,"put_count":5}} -{"i":42,"series":2,"stats":{"get_bytes":2904,"get_count":6,"put_bytes":900,"put_count":5}} -{"i":43,"series":2,"stats":{"get_bytes":2908,"get_count":6,"put_bytes":904,"put_count":5}} -{"i":44,"series":2,"stats":{"get_bytes":2912,"get_count":6,"put_bytes":908,"put_count":5}} -{"i":45,"series":2,"stats":{"get_bytes":2916,"get_count":6,"put_bytes":912,"put_count":5}} -{"i":46,"series":2,"stats":{"get_bytes":2920,"get_count":6,"put_bytes":917,"put_count":5}} -{"i":47,"series":2,"stats":{"get_bytes":2925,"get_count":6,"put_bytes":921,"put_count":5}} -{"i":48,"series":2,"stats":{"get_bytes":2785,"get_count":5,"put_bytes":818,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":2826,"get_count":5,"put_bytes":822,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":2830,"get_count":5,"put_bytes":826,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":2834,"get_count":5,"put_bytes":830,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":2838,"get_count":5,"put_bytes":834,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":2842,"get_count":5,"put_bytes":838,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":2846,"get_count":5,"put_bytes":843,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":2851,"get_count":5,"put_bytes":847,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":2855,"get_count":5,"put_bytes":956,"put_count":5}} -{"i":57,"series":2,"stats":{"get_bytes":2964,"get_count":6,"put_bytes":960,"put_count":5}} -{"i":58,"series":2,"stats":{"get_bytes":2968,"get_count":6,"put_bytes":964,"put_count":5}} -{"i":59,"series":2,"stats":{"get_bytes":2972,"get_count":6,"put_bytes":968,"put_count":5}} -{"i":60,"series":2,"stats":{"get_bytes":2976,"get_count":6,"put_bytes":972,"put_count":5}} -{"i":61,"series":2,"stats":{"get_bytes":2980,"get_count":6,"put_bytes":976,"put_count":5}} -{"i":62,"series":2,"stats":{"get_bytes":2984,"get_count":6,"put_bytes":981,"put_count":5}} -{"i":63,"series":2,"stats":{"get_bytes":2989,"get_count":6,"put_bytes":985,"put_count":5}} -{"i":64,"series":2,"stats":{"get_bytes":2849,"get_count":5,"put_bytes":882,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":2890,"get_count":5,"put_bytes":886,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":2894,"get_count":5,"put_bytes":890,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":2898,"get_count":5,"put_bytes":894,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":2902,"get_count":5,"put_bytes":898,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":2906,"get_count":5,"put_bytes":902,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":2910,"get_count":5,"put_bytes":907,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":2915,"get_count":5,"put_bytes":911,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":2919,"get_count":5,"put_bytes":1020,"put_count":5}} -{"i":73,"series":2,"stats":{"get_bytes":3028,"get_count":6,"put_bytes":1024,"put_count":5}} -{"i":74,"series":2,"stats":{"get_bytes":3032,"get_count":6,"put_bytes":1028,"put_count":5}} -{"i":75,"series":2,"stats":{"get_bytes":3036,"get_count":6,"put_bytes":1032,"put_count":5}} -{"i":76,"series":2,"stats":{"get_bytes":3040,"get_count":6,"put_bytes":1036,"put_count":5}} -{"i":77,"series":2,"stats":{"get_bytes":3044,"get_count":6,"put_bytes":1040,"put_count":5}} -{"i":78,"series":2,"stats":{"get_bytes":3048,"get_count":6,"put_bytes":1045,"put_count":5}} -{"i":79,"series":2,"stats":{"get_bytes":3053,"get_count":6,"put_bytes":1049,"put_count":5}} -{"i":80,"series":2,"stats":{"get_bytes":2913,"get_count":5,"put_bytes":946,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":2954,"get_count":5,"put_bytes":950,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":2958,"get_count":5,"put_bytes":954,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":2962,"get_count":5,"put_bytes":958,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":2966,"get_count":5,"put_bytes":962,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":2970,"get_count":5,"put_bytes":966,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":2974,"get_count":5,"put_bytes":971,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":2979,"get_count":5,"put_bytes":975,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":2983,"get_count":5,"put_bytes":1084,"put_count":5}} -{"i":89,"series":2,"stats":{"get_bytes":3092,"get_count":6,"put_bytes":1088,"put_count":5}} -{"i":90,"series":2,"stats":{"get_bytes":3096,"get_count":6,"put_bytes":1092,"put_count":5}} -{"i":91,"series":2,"stats":{"get_bytes":3100,"get_count":6,"put_bytes":1096,"put_count":5}} -{"i":92,"series":2,"stats":{"get_bytes":3104,"get_count":6,"put_bytes":1100,"put_count":5}} -{"i":93,"series":2,"stats":{"get_bytes":3108,"get_count":6,"put_bytes":1104,"put_count":5}} -{"i":94,"series":2,"stats":{"get_bytes":3112,"get_count":6,"put_bytes":1109,"put_count":5}} -{"i":95,"series":2,"stats":{"get_bytes":3117,"get_count":6,"put_bytes":1113,"put_count":5}} -{"i":96,"series":2,"stats":{"get_bytes":2977,"get_count":5,"put_bytes":1010,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":3018,"get_count":5,"put_bytes":1014,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":3022,"get_count":5,"put_bytes":1018,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":3026,"get_count":5,"put_bytes":1022,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":2479,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2270,"get_count":3,"put_bytes":266,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2274,"get_count":3,"put_bytes":270,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2278,"get_count":3,"put_bytes":274,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2282,"get_count":3,"put_bytes":278,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2286,"get_count":3,"put_bytes":282,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2290,"get_count":3,"put_bytes":287,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2295,"get_count":3,"put_bytes":291,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2299,"get_count":3,"put_bytes":465,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2473,"get_count":4,"put_bytes":469,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":473,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2481,"get_count":4,"put_bytes":477,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2485,"get_count":4,"put_bytes":481,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2489,"get_count":4,"put_bytes":485,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":490,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2498,"get_count":4,"put_bytes":494,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2502,"get_count":4,"put_bytes":460,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2468,"get_count":4,"put_bytes":464,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":485,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":489,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":598,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2606,"get_count":5,"put_bytes":602,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2610,"get_count":5,"put_bytes":606,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2614,"get_count":5,"put_bytes":610,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2618,"get_count":5,"put_bytes":614,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2622,"get_count":5,"put_bytes":618,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2626,"get_count":5,"put_bytes":623,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2631,"get_count":5,"put_bytes":627,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2491,"get_count":4,"put_bytes":524,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2532,"get_count":4,"put_bytes":528,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2536,"get_count":4,"put_bytes":532,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2540,"get_count":4,"put_bytes":536,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2544,"get_count":4,"put_bytes":540,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2548,"get_count":4,"put_bytes":544,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2552,"get_count":4,"put_bytes":549,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2557,"get_count":4,"put_bytes":553,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2561,"get_count":4,"put_bytes":662,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2670,"get_count":5,"put_bytes":666,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2674,"get_count":5,"put_bytes":670,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2678,"get_count":5,"put_bytes":674,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2682,"get_count":5,"put_bytes":678,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2686,"get_count":5,"put_bytes":682,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2690,"get_count":5,"put_bytes":687,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2695,"get_count":5,"put_bytes":691,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2555,"get_count":4,"put_bytes":588,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2596,"get_count":4,"put_bytes":592,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2600,"get_count":4,"put_bytes":596,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2604,"get_count":4,"put_bytes":600,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2608,"get_count":4,"put_bytes":604,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2612,"get_count":4,"put_bytes":608,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2616,"get_count":4,"put_bytes":613,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2621,"get_count":4,"put_bytes":617,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2625,"get_count":4,"put_bytes":726,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":730,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2738,"get_count":5,"put_bytes":734,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2742,"get_count":5,"put_bytes":738,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2746,"get_count":5,"put_bytes":742,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2750,"get_count":5,"put_bytes":746,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2754,"get_count":5,"put_bytes":751,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2759,"get_count":5,"put_bytes":755,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2619,"get_count":4,"put_bytes":652,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2660,"get_count":4,"put_bytes":656,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2664,"get_count":4,"put_bytes":660,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2668,"get_count":4,"put_bytes":664,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2672,"get_count":4,"put_bytes":668,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2676,"get_count":4,"put_bytes":672,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2680,"get_count":4,"put_bytes":677,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2685,"get_count":4,"put_bytes":681,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2689,"get_count":4,"put_bytes":790,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2798,"get_count":5,"put_bytes":794,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2802,"get_count":5,"put_bytes":798,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2806,"get_count":5,"put_bytes":802,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2810,"get_count":5,"put_bytes":806,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2814,"get_count":5,"put_bytes":810,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2818,"get_count":5,"put_bytes":815,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2823,"get_count":5,"put_bytes":819,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2683,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":461,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":465,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2473,"get_count":4,"put_bytes":469,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":473,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2481,"get_count":4,"put_bytes":477,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2485,"get_count":4,"put_bytes":482,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":486,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2494,"get_count":4,"put_bytes":596,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":604,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2612,"get_count":5,"put_bytes":608,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":621,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2629,"get_count":5,"put_bytes":625,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2633,"get_count":5,"put_bytes":589,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":597,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":497,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2505,"get_count":4,"put_bytes":501,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2509,"get_count":4,"put_bytes":505,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2513,"get_count":4,"put_bytes":509,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2517,"get_count":4,"put_bytes":513,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2521,"get_count":4,"put_bytes":517,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2525,"get_count":4,"put_bytes":522,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2530,"get_count":4,"put_bytes":526,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2534,"get_count":4,"put_bytes":698,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2706,"get_count":5,"put_bytes":702,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2710,"get_count":5,"put_bytes":706,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2714,"get_count":5,"put_bytes":710,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2718,"get_count":5,"put_bytes":714,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2722,"get_count":5,"put_bytes":718,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2726,"get_count":5,"put_bytes":723,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2731,"get_count":5,"put_bytes":727,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2735,"get_count":5,"put_bytes":691,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2699,"get_count":5,"put_bytes":695,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":699,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2707,"get_count":5,"put_bytes":703,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2711,"get_count":5,"put_bytes":707,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":711,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2719,"get_count":5,"put_bytes":716,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2724,"get_count":5,"put_bytes":720,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2728,"get_count":5,"put_bytes":829,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2837,"get_count":6,"put_bytes":833,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2841,"get_count":6,"put_bytes":837,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2845,"get_count":6,"put_bytes":841,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2849,"get_count":6,"put_bytes":845,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2853,"get_count":6,"put_bytes":849,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2857,"get_count":6,"put_bytes":854,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2862,"get_count":6,"put_bytes":858,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2722,"get_count":5,"put_bytes":755,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2763,"get_count":5,"put_bytes":759,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2767,"get_count":5,"put_bytes":763,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2771,"get_count":5,"put_bytes":767,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2775,"get_count":5,"put_bytes":771,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2779,"get_count":5,"put_bytes":775,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2783,"get_count":5,"put_bytes":780,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2788,"get_count":5,"put_bytes":784,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2792,"get_count":5,"put_bytes":893,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2901,"get_count":6,"put_bytes":897,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2905,"get_count":6,"put_bytes":901,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2909,"get_count":6,"put_bytes":905,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2913,"get_count":6,"put_bytes":909,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2917,"get_count":6,"put_bytes":913,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2921,"get_count":6,"put_bytes":918,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2926,"get_count":6,"put_bytes":922,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2786,"get_count":5,"put_bytes":819,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2827,"get_count":5,"put_bytes":823,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2831,"get_count":5,"put_bytes":827,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2835,"get_count":5,"put_bytes":831,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2839,"get_count":5,"put_bytes":835,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2843,"get_count":5,"put_bytes":839,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2847,"get_count":5,"put_bytes":844,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2852,"get_count":5,"put_bytes":848,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2856,"get_count":5,"put_bytes":957,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2965,"get_count":6,"put_bytes":961,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2969,"get_count":6,"put_bytes":965,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2973,"get_count":6,"put_bytes":969,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2977,"get_count":6,"put_bytes":973,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2981,"get_count":6,"put_bytes":977,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2985,"get_count":6,"put_bytes":982,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2990,"get_count":6,"put_bytes":986,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2850,"get_count":5,"put_bytes":883,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2891,"get_count":5,"put_bytes":887,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2895,"get_count":5,"put_bytes":891,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2899,"get_count":5,"put_bytes":895,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2903,"get_count":5,"put_bytes":899,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2907,"get_count":5,"put_bytes":903,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2911,"get_count":5,"put_bytes":908,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2916,"get_count":5,"put_bytes":912,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2920,"get_count":5,"put_bytes":1021,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":3029,"get_count":6,"put_bytes":1025,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":3033,"get_count":6,"put_bytes":1029,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":3037,"get_count":6,"put_bytes":1033,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":3041,"get_count":6,"put_bytes":1037,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":3045,"get_count":6,"put_bytes":1041,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":3049,"get_count":6,"put_bytes":1046,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":3054,"get_count":6,"put_bytes":1050,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2914,"get_count":5,"put_bytes":947,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2955,"get_count":5,"put_bytes":951,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2959,"get_count":5,"put_bytes":955,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2963,"get_count":5,"put_bytes":959,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2967,"get_count":5,"put_bytes":963,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2971,"get_count":5,"put_bytes":967,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2975,"get_count":5,"put_bytes":972,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2980,"get_count":5,"put_bytes":976,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2984,"get_count":5,"put_bytes":1085,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":3093,"get_count":6,"put_bytes":1089,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":3097,"get_count":6,"put_bytes":1093,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":3101,"get_count":6,"put_bytes":1097,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":3105,"get_count":6,"put_bytes":1101,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":3109,"get_count":6,"put_bytes":1105,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":3113,"get_count":6,"put_bytes":1110,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":3118,"get_count":6,"put_bytes":1114,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2978,"get_count":5,"put_bytes":1011,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":3019,"get_count":5,"put_bytes":1015,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":3023,"get_count":5,"put_bytes":1019,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":3027,"get_count":5,"put_bytes":1023,"put_count":4}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index f80e531e59c90529254a41e20103adabd5f4c02b..1cba716bdab8634aa86498af6dca639b96d902d7 100644 GIT binary patch literal 17977 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

XJ6dibRVXMZY?Q+s$?7fN81yGh{X^Z;w z*u12}235Y{oY>DX;+iYd(|5$!4e};#>F+Kcq(I>7k}$1*5R3jpTS)6Z?d1kI79cBh zwPv9FCQrr6U>*eG)oL=(3UMEUFRqw>s13wcY48 zJ9G7aa1oN8r6^-pA}aRWF5wlVvX;1gS}YGAI*}Na5kpC*NMiS7&ta#pV0^dGKu`LC6L%HL@h1wC7#{Bzpi{EFYN#ZW5G5y`~b*O*@aYPi2mNsZX8eO zuK1`{+xaH*5ubGw{+lnF$^8eK?*4*3lAECv+v!O)sKoJfpc1k8HT;8-WwjGUuy2-wGj#tolpLQ@-o2ro`(d)2VNpGoahi?Zb z+F4_VFbe+KQ1Kl~Z*eZya6iST@^8(>Cr+$F>%7=yPH&X?H?)7d6epYJe}(b?C~6sN zL!6BtP2_-Q9^+NAq^QXVr%2R$+PwRx?*J?hh|Z7e(PxGB8^+vyjRw8u)P^NH!B3(~+bN>apzbG{ zW)AcI1H;^2+(S_h0RjNdxBaoo(~Vo0;_v_g0dDGHO#E_l$)q0?PEv;@Ycl>TcNeh`NU;2 zA9H1OfLkszWp&uI_$SX>b{!*-38#+n9WPzD$ObBRsvnF@UIVhzC?^^CNU>L9mi8I; zcF(>Wm=3B=G0vpHX0A^Mhy{gkj@)f#u^Dul_8MYc%?N7^TzDQBv8b$H3>72nqrLzP zmT)H^Ur7mLoJfGtU?=JcyVr*$CjWhRw!f~$$6y%&U^Tyah)V3$t4oWILYE^h2{9pN z=m^SPHLj)# z<43W29G%-Kaut*fblXJ8eG3m|I z`l!g^x;HlgHWl6=Y$E7dQ=c?7Bt4^pMV37}qdu0Y>2V7`Ij~K8^fJ&FfXi^;W^q-F z@9aPAG$eE%3;{d>Qlj;^n}Ob&G%=L;f8M`s=QW+_Gb9qEASxl#NXPN?q2Bbs`U3z9 zhV{O%4Z&~(k0LEDw#)9e?nYDY-r!S-w{gxtfyS}a)YhfO9iYtk;nFo~^1VMO8A940 zcUT@H)2>x$2{@p6iDfZgzvd~yDsC#kZZYAKD3n?W2<6zD2u5*V$HXlvV?wf0h|=>9hB=&={Iw~F;8)6UID&^eRNIba}4zIY+GUEa=+zXtPlD! zB8WNGXJO-z3VUKS*T9pZD!8T8h3cMw@*dRm98+`5adT5Ce7`J3@o-ir_$Z&m)su~{EO|g zC-QLJ?6`&E<#~k1XF8{IC<|U_5mve zb`&Qv4~tv@NqFv_L`0$-?ABWa90E9b$fRB%$HeiW*|q={<8CNb5VUUT2|;rJG>1F0 zZyH{rFQZa>yNnQrHkgjbUFc_T0iI*y!?HKhAGHAwdrm;cE%Or zqdSA#;zGR>$dYgEGIDA>)Y(bLYXwJ@Vv8DN4M9eAuh1zUjkwk?#N9WAr6(z{N|u;O zP!g)}=Ac?;%9{$Hz%XLEnUatX1qGF0Y`xrwYUeL9%1Vx0nWis_I_yHR%0w`eA*Rw#LWg5+`MVE8{ zCN@PHNvw0@Y@)<#7znXVc{UAC326 z%2m^?)n*v?<5gOJPpd(BGlZwS-C{F&L)LffnJO56u)1_^2Taf3eGNVtHypTUGZb~5 zV6gV&l=ds7Bf&difQ3x)h=GY<+B@xT!)s)^6H|MD3*NTy`7r~lcPKqP&oaH1R{Ddc zd-KOr*-D-eMrv{_8{+J^yekqdP&(hVxlASf@V*9&U+;v4m0V#ueImA{^{PD|BU+sP znC6w{FE$1oxO((`+JnVVw;hXc+>3)Q16aI-%f}-8uMdedxi~?UG;0M?)?H|U=?gWO z8=%!Rvpm7?0E*%eg$X&(05jHAX+#T*{KsQDS^%QMy42HyRHvhB#`S3;?TIRol@EKv z{{~14JX9jr$|^JWUrhe8*fZ>@P774o(LzFE189@KD2b}@JJAAe_fFHdb`tmdjyD>7 zh2Mrg`|DOtG^ej%U}x?TH*@3og#7Wr*`JgDYw*ji4Lc^u{&PG1f!eV?GZ6PYqyjow z`8O#e7x?-E4jJ|)`Y-GZV!(Dl062_{#t3+>Qw4_tqA*^Rm_y|?7cRh1Dlr(HKNvI^ zi3CnZ;B9sa4%eTr))2f2PD{Xl&7g>j{Wc9FYm7heD;PqtzZbw7L|mx;KUMty$;&Fz zYG?z?y_uYpzBmBL{tE#oi|5Y;1{p~6WhiDe#D zu5;Ql_42t4O$MKBCfmTpkbj8{8lRrg@UYOK@5Gbu?&Co@r4OltsDtaf%s?@f!r(y&BF=}DVhpKl6v)P# zW*lj;fb=^?i-;XbS8+fwR0q0zW4?Etyf90n1~&uWwsm)y)&3>P{uc}hCaY92=1&Pk zHax-H(G7oQ?X7;W+ixt{%M*pTF(A%b^Y(IT+WkxZ!G8gvMT3T1WT8^i%e*NrWW(&K zMu01#$-BloyAp1brd>DjOLfR&vd*~Ya&s;zWnl1MAhdqUApbu>E~k&5xl0W%%W4` zmKpGGfx2ZPf3UH_V6GT4BL;i)We_yH2i3d=0b@NP4c9Uv~ zYFjnz^0bM+D~{e1WI>RM8$jmv4p?<>oqhI}cXhLU)UxAez<gM12eT4w>3Z=3i{pF6G-Mg~ zVTn~HxHXQ}{~d+CP5a+b=tf26y;R!&T@dKmg#D!_DMw5a4fPWVw zPkP^0SIVljEVHk-iEp}8KD_<-52@*PjuJ{uJj+a= znV;`;_2`I&`|9`j*MxJ)6N;H-h){2`$oX{$3CCdN57Q~GGStRF{f$Bu(MoI>*`dj{ z5zmpFufNGEa?gIWCZby>ZF2#qC-7k)ndWa5(aBjBy7Y*%OXTzUl`^7oPMcqbHsfZ{ zfV-7_gBebiy!GOamF=%7PV%Hx+#gEznk6k&vHCA`3}&2QD#Z!^7wqmoAGa`9IIcs2 X`(IPWf#10T0;%7Bq*|u@B;@}BZYPL@ literal 17263 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 diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 187669961..90dec0a02 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":4007,"put_count":14}} -{"i":1,"series":1,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":1865,"put_count":10}} -{"i":2,"series":1,"stats":{"get_bytes":3009,"get_count":5,"put_bytes":2372,"put_count":11}} -{"i":3,"series":1,"stats":{"get_bytes":3516,"get_count":6,"put_bytes":2774,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":3774,"get_count":5,"put_bytes":3069,"put_count":10}} -{"i":5,"series":1,"stats":{"get_bytes":4213,"get_count":5,"put_bytes":3470,"put_count":10}} -{"i":6,"series":1,"stats":{"get_bytes":2558,"get_count":4,"put_bytes":1987,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":3131,"get_count":6,"put_bytes":2389,"put_count":11}} -{"i":8,"series":1,"stats":{"get_bytes":3389,"get_count":5,"put_bytes":2683,"put_count":10}} -{"i":9,"series":1,"stats":{"get_bytes":3827,"get_count":5,"put_bytes":3086,"put_count":10}} -{"i":10,"series":1,"stats":{"get_bytes":4230,"get_count":5,"put_bytes":3591,"put_count":11}} -{"i":11,"series":1,"stats":{"get_bytes":4735,"get_count":6,"put_bytes":4060,"put_count":12}} -{"i":12,"series":1,"stats":{"get_bytes":3004,"get_count":5,"put_bytes":2298,"put_count":10}} -{"i":13,"series":1,"stats":{"get_bytes":3442,"get_count":5,"put_bytes":2700,"put_count":10}} -{"i":14,"series":1,"stats":{"get_bytes":3844,"get_count":5,"put_bytes":3207,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":4351,"get_count":6,"put_bytes":3608,"put_count":11}} -{"i":16,"series":1,"stats":{"get_bytes":4608,"get_count":5,"put_bytes":3969,"put_count":11}} -{"i":17,"series":1,"stats":{"get_bytes":3057,"get_count":5,"put_bytes":2315,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":3459,"get_count":5,"put_bytes":2821,"put_count":11}} -{"i":19,"series":1,"stats":{"get_bytes":3965,"get_count":6,"put_bytes":3223,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":4223,"get_count":5,"put_bytes":3517,"put_count":10}} -{"i":21,"series":1,"stats":{"get_bytes":4661,"get_count":5,"put_bytes":3987,"put_count":11}} -{"i":22,"series":1,"stats":{"get_bytes":3075,"get_count":5,"put_bytes":2437,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":3581,"get_count":6,"put_bytes":2839,"put_count":11}} -{"i":24,"series":1,"stats":{"get_bytes":3839,"get_count":5,"put_bytes":3133,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":4277,"get_count":5,"put_bytes":3535,"put_count":10}} -{"i":26,"series":1,"stats":{"get_bytes":4679,"get_count":5,"put_bytes":4108,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":3196,"get_count":6,"put_bytes":2454,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":3454,"get_count":5,"put_bytes":2748,"put_count":10}} -{"i":29,"series":1,"stats":{"get_bytes":3892,"get_count":5,"put_bytes":3149,"put_count":10}} -{"i":30,"series":1,"stats":{"get_bytes":4293,"get_count":5,"put_bytes":3656,"put_count":11}} -{"i":31,"series":1,"stats":{"get_bytes":4800,"get_count":6,"put_bytes":4125,"put_count":12}} -{"i":32,"series":1,"stats":{"get_bytes":3069,"get_count":5,"put_bytes":2363,"put_count":10}} -{"i":33,"series":1,"stats":{"get_bytes":3507,"get_count":5,"put_bytes":2764,"put_count":10}} -{"i":34,"series":1,"stats":{"get_bytes":3908,"get_count":5,"put_bytes":3270,"put_count":11}} -{"i":35,"series":1,"stats":{"get_bytes":4414,"get_count":6,"put_bytes":3673,"put_count":11}} -{"i":36,"series":1,"stats":{"get_bytes":4673,"get_count":5,"put_bytes":4034,"put_count":11}} -{"i":37,"series":1,"stats":{"get_bytes":3122,"get_count":5,"put_bytes":2380,"put_count":10}} -{"i":38,"series":1,"stats":{"get_bytes":3524,"get_count":5,"put_bytes":2885,"put_count":11}} -{"i":39,"series":1,"stats":{"get_bytes":4029,"get_count":6,"put_bytes":3287,"put_count":11}} -{"i":40,"series":1,"stats":{"get_bytes":4287,"get_count":5,"put_bytes":3582,"put_count":10}} -{"i":41,"series":1,"stats":{"get_bytes":4726,"get_count":5,"put_bytes":4051,"put_count":11}} -{"i":42,"series":1,"stats":{"get_bytes":3139,"get_count":5,"put_bytes":2500,"put_count":11}} -{"i":43,"series":1,"stats":{"get_bytes":3644,"get_count":6,"put_bytes":2902,"put_count":11}} -{"i":44,"series":1,"stats":{"get_bytes":3902,"get_count":5,"put_bytes":3196,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":4340,"get_count":5,"put_bytes":3599,"put_count":10}} -{"i":46,"series":1,"stats":{"get_bytes":4743,"get_count":5,"put_bytes":4105,"put_count":11}} -{"i":47,"series":1,"stats":{"get_bytes":3193,"get_count":5,"put_bytes":2517,"put_count":11}} -{"i":48,"series":1,"stats":{"get_bytes":3517,"get_count":5,"put_bytes":2811,"put_count":10}} -{"i":49,"series":1,"stats":{"get_bytes":3955,"get_count":5,"put_bytes":3213,"put_count":10}} -{"i":50,"series":1,"stats":{"get_bytes":4357,"get_count":5,"put_bytes":3720,"put_count":11}} -{"i":51,"series":1,"stats":{"get_bytes":4864,"get_count":6,"put_bytes":4121,"put_count":11}} -{"i":52,"series":1,"stats":{"get_bytes":3065,"get_count":4,"put_bytes":2426,"put_count":10}} -{"i":53,"series":1,"stats":{"get_bytes":3570,"get_count":5,"put_bytes":2828,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":3972,"get_count":5,"put_bytes":3334,"put_count":11}} -{"i":55,"series":1,"stats":{"get_bytes":4478,"get_count":6,"put_bytes":3737,"put_count":11}} -{"i":56,"series":1,"stats":{"get_bytes":4737,"get_count":5,"put_bytes":4030,"put_count":10}} -{"i":57,"series":1,"stats":{"get_bytes":5174,"get_count":5,"put_bytes":4499,"put_count":11}} -{"i":58,"series":1,"stats":{"get_bytes":3587,"get_count":5,"put_bytes":2949,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":4093,"get_count":6,"put_bytes":3351,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":4351,"get_count":5,"put_bytes":3646,"put_count":10}} -{"i":61,"series":1,"stats":{"get_bytes":4790,"get_count":5,"put_bytes":4047,"put_count":10}} -{"i":62,"series":1,"stats":{"get_bytes":5191,"get_count":5,"put_bytes":4689,"put_count":13}} -{"i":63,"series":1,"stats":{"get_bytes":2938,"get_count":6,"put_bytes":2196,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":3196,"get_count":5,"put_bytes":2490,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":3634,"get_count":5,"put_bytes":2892,"put_count":10}} -{"i":66,"series":1,"stats":{"get_bytes":4036,"get_count":5,"put_bytes":3398,"put_count":11}} -{"i":67,"series":1,"stats":{"get_bytes":4542,"get_count":6,"put_bytes":3934,"put_count":13}} -{"i":68,"series":1,"stats":{"get_bytes":2878,"get_count":6,"put_bytes":2172,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":3316,"get_count":6,"put_bytes":2574,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":3718,"get_count":6,"put_bytes":3079,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":4223,"get_count":7,"put_bytes":3482,"put_count":12}} -{"i":72,"series":1,"stats":{"get_bytes":4482,"get_count":6,"put_bytes":3843,"put_count":12}} -{"i":73,"series":1,"stats":{"get_bytes":2931,"get_count":6,"put_bytes":2189,"put_count":11}} -{"i":74,"series":1,"stats":{"get_bytes":3333,"get_count":6,"put_bytes":2694,"put_count":12}} -{"i":75,"series":1,"stats":{"get_bytes":3838,"get_count":7,"put_bytes":3096,"put_count":12}} -{"i":76,"series":1,"stats":{"get_bytes":4096,"get_count":6,"put_bytes":3391,"put_count":11}} -{"i":77,"series":1,"stats":{"get_bytes":4535,"get_count":6,"put_bytes":3860,"put_count":12}} -{"i":78,"series":1,"stats":{"get_bytes":2948,"get_count":6,"put_bytes":2310,"put_count":12}} -{"i":79,"series":1,"stats":{"get_bytes":3454,"get_count":7,"put_bytes":2711,"put_count":12}} -{"i":80,"series":1,"stats":{"get_bytes":3711,"get_count":6,"put_bytes":3005,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":4149,"get_count":6,"put_bytes":3408,"put_count":11}} -{"i":82,"series":1,"stats":{"get_bytes":4552,"get_count":6,"put_bytes":3981,"put_count":13}} -{"i":83,"series":1,"stats":{"get_bytes":3069,"get_count":7,"put_bytes":2326,"put_count":12}} -{"i":84,"series":1,"stats":{"get_bytes":3326,"get_count":6,"put_bytes":2620,"put_count":11}} -{"i":85,"series":1,"stats":{"get_bytes":3764,"get_count":6,"put_bytes":3022,"put_count":11}} -{"i":86,"series":1,"stats":{"get_bytes":4166,"get_count":6,"put_bytes":3529,"put_count":12}} -{"i":87,"series":1,"stats":{"get_bytes":4673,"get_count":7,"put_bytes":3931,"put_count":12}} -{"i":88,"series":1,"stats":{"get_bytes":3019,"get_count":6,"put_bytes":2235,"put_count":11}} -{"i":89,"series":1,"stats":{"get_bytes":3379,"get_count":6,"put_bytes":2637,"put_count":11}} -{"i":90,"series":1,"stats":{"get_bytes":3781,"get_count":6,"put_bytes":3143,"put_count":12}} -{"i":91,"series":1,"stats":{"get_bytes":4287,"get_count":7,"put_bytes":3546,"put_count":12}} -{"i":92,"series":1,"stats":{"get_bytes":4546,"get_count":6,"put_bytes":3839,"put_count":11}} -{"i":93,"series":1,"stats":{"get_bytes":2927,"get_count":5,"put_bytes":2252,"put_count":11}} -{"i":94,"series":1,"stats":{"get_bytes":3396,"get_count":6,"put_bytes":2758,"put_count":12}} -{"i":95,"series":1,"stats":{"get_bytes":3902,"get_count":7,"put_bytes":3160,"put_count":12}} -{"i":96,"series":1,"stats":{"get_bytes":4160,"get_count":6,"put_bytes":3455,"put_count":11}} -{"i":97,"series":1,"stats":{"get_bytes":4599,"get_count":6,"put_bytes":3856,"put_count":11}} -{"i":98,"series":1,"stats":{"get_bytes":5000,"get_count":6,"put_bytes":4429,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":3517,"get_count":7,"put_bytes":2775,"put_count":12}} -{"i":0,"series":2,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":1887,"put_count":10}} -{"i":1,"series":2,"stats":{"get_bytes":3031,"get_count":5,"put_bytes":2289,"put_count":10}} -{"i":2,"series":2,"stats":{"get_bytes":3433,"get_count":5,"put_bytes":2796,"put_count":11}} -{"i":3,"series":2,"stats":{"get_bytes":3940,"get_count":6,"put_bytes":3198,"put_count":11}} -{"i":4,"series":2,"stats":{"get_bytes":4198,"get_count":5,"put_bytes":2018,"put_count":11}} -{"i":5,"series":2,"stats":{"get_bytes":3162,"get_count":6,"put_bytes":2420,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":3564,"get_count":6,"put_bytes":2926,"put_count":12}} -{"i":7,"series":2,"stats":{"get_bytes":4070,"get_count":7,"put_bytes":3329,"put_count":12}} -{"i":8,"series":2,"stats":{"get_bytes":4329,"get_count":6,"put_bytes":3622,"put_count":11}} -{"i":9,"series":2,"stats":{"get_bytes":4766,"get_count":6,"put_bytes":4091,"put_count":12}} -{"i":10,"series":2,"stats":{"get_bytes":3179,"get_count":6,"put_bytes":2541,"put_count":12}} -{"i":11,"series":2,"stats":{"get_bytes":3685,"get_count":7,"put_bytes":2943,"put_count":12}} -{"i":12,"series":2,"stats":{"get_bytes":3943,"get_count":6,"put_bytes":3238,"put_count":11}} -{"i":13,"series":2,"stats":{"get_bytes":4382,"get_count":6,"put_bytes":3639,"put_count":11}} -{"i":14,"series":2,"stats":{"get_bytes":4783,"get_count":6,"put_bytes":4212,"put_count":13}} -{"i":15,"series":2,"stats":{"get_bytes":3300,"get_count":7,"put_bytes":2558,"put_count":12}} -{"i":16,"series":2,"stats":{"get_bytes":3558,"get_count":6,"put_bytes":2852,"put_count":11}} -{"i":17,"series":2,"stats":{"get_bytes":3996,"get_count":6,"put_bytes":3254,"put_count":11}} -{"i":18,"series":2,"stats":{"get_bytes":4398,"get_count":6,"put_bytes":3760,"put_count":12}} -{"i":19,"series":2,"stats":{"get_bytes":4904,"get_count":7,"put_bytes":4229,"put_count":13}} -{"i":20,"series":2,"stats":{"get_bytes":3173,"get_count":6,"put_bytes":2467,"put_count":11}} -{"i":21,"series":2,"stats":{"get_bytes":3611,"get_count":6,"put_bytes":2869,"put_count":11}} -{"i":22,"series":2,"stats":{"get_bytes":4013,"get_count":6,"put_bytes":3374,"put_count":12}} -{"i":23,"series":2,"stats":{"get_bytes":4518,"get_count":7,"put_bytes":3777,"put_count":12}} -{"i":24,"series":2,"stats":{"get_bytes":4777,"get_count":6,"put_bytes":4139,"put_count":12}} -{"i":25,"series":2,"stats":{"get_bytes":3227,"get_count":6,"put_bytes":2485,"put_count":11}} -{"i":26,"series":2,"stats":{"get_bytes":3629,"get_count":6,"put_bytes":2990,"put_count":12}} -{"i":27,"series":2,"stats":{"get_bytes":4134,"get_count":7,"put_bytes":3392,"put_count":12}} -{"i":28,"series":2,"stats":{"get_bytes":4392,"get_count":6,"put_bytes":3687,"put_count":11}} -{"i":29,"series":2,"stats":{"get_bytes":4831,"get_count":6,"put_bytes":4156,"put_count":12}} -{"i":30,"series":2,"stats":{"get_bytes":3244,"get_count":6,"put_bytes":2606,"put_count":12}} -{"i":31,"series":2,"stats":{"get_bytes":3750,"get_count":7,"put_bytes":3007,"put_count":12}} -{"i":32,"series":2,"stats":{"get_bytes":4007,"get_count":6,"put_bytes":3301,"put_count":11}} -{"i":33,"series":2,"stats":{"get_bytes":4445,"get_count":6,"put_bytes":3704,"put_count":11}} -{"i":34,"series":2,"stats":{"get_bytes":4848,"get_count":6,"put_bytes":4277,"put_count":13}} -{"i":35,"series":2,"stats":{"get_bytes":3365,"get_count":7,"put_bytes":2622,"put_count":12}} -{"i":36,"series":2,"stats":{"get_bytes":3622,"get_count":6,"put_bytes":2916,"put_count":11}} -{"i":37,"series":2,"stats":{"get_bytes":4060,"get_count":6,"put_bytes":3318,"put_count":11}} -{"i":38,"series":2,"stats":{"get_bytes":4462,"get_count":6,"put_bytes":3825,"put_count":12}} -{"i":39,"series":2,"stats":{"get_bytes":4969,"get_count":7,"put_bytes":4227,"put_count":12}} -{"i":40,"series":2,"stats":{"get_bytes":3315,"get_count":6,"put_bytes":2531,"put_count":11}} -{"i":41,"series":2,"stats":{"get_bytes":3675,"get_count":6,"put_bytes":2933,"put_count":11}} -{"i":42,"series":2,"stats":{"get_bytes":4077,"get_count":6,"put_bytes":3439,"put_count":12}} -{"i":43,"series":2,"stats":{"get_bytes":4583,"get_count":7,"put_bytes":3842,"put_count":12}} -{"i":44,"series":2,"stats":{"get_bytes":4842,"get_count":6,"put_bytes":4135,"put_count":11}} -{"i":45,"series":2,"stats":{"get_bytes":3223,"get_count":5,"put_bytes":2548,"put_count":11}} -{"i":46,"series":2,"stats":{"get_bytes":3692,"get_count":6,"put_bytes":3054,"put_count":12}} -{"i":47,"series":2,"stats":{"get_bytes":4198,"get_count":7,"put_bytes":3456,"put_count":12}} -{"i":48,"series":2,"stats":{"get_bytes":4456,"get_count":6,"put_bytes":3751,"put_count":11}} -{"i":49,"series":2,"stats":{"get_bytes":4895,"get_count":6,"put_bytes":4152,"put_count":11}} -{"i":50,"series":2,"stats":{"get_bytes":5296,"get_count":6,"put_bytes":4725,"put_count":13}} -{"i":51,"series":2,"stats":{"get_bytes":3813,"get_count":7,"put_bytes":3071,"put_count":12}} -{"i":52,"series":2,"stats":{"get_bytes":4071,"get_count":6,"put_bytes":3365,"put_count":11}} -{"i":53,"series":2,"stats":{"get_bytes":4509,"get_count":6,"put_bytes":3768,"put_count":11}} -{"i":54,"series":2,"stats":{"get_bytes":4912,"get_count":6,"put_bytes":4273,"put_count":12}} -{"i":55,"series":2,"stats":{"get_bytes":5417,"get_count":7,"put_bytes":4742,"put_count":13}} -{"i":56,"series":2,"stats":{"get_bytes":3686,"get_count":6,"put_bytes":2980,"put_count":11}} -{"i":57,"series":2,"stats":{"get_bytes":4124,"get_count":6,"put_bytes":3382,"put_count":11}} -{"i":58,"series":2,"stats":{"get_bytes":4526,"get_count":6,"put_bytes":3888,"put_count":12}} -{"i":59,"series":2,"stats":{"get_bytes":5032,"get_count":7,"put_bytes":4290,"put_count":12}} -{"i":60,"series":2,"stats":{"get_bytes":5290,"get_count":6,"put_bytes":4651,"put_count":12}} -{"i":61,"series":2,"stats":{"get_bytes":3739,"get_count":6,"put_bytes":2997,"put_count":11}} -{"i":62,"series":2,"stats":{"get_bytes":4141,"get_count":6,"put_bytes":3503,"put_count":12}} -{"i":63,"series":2,"stats":{"get_bytes":4647,"get_count":7,"put_bytes":3904,"put_count":12}} -{"i":64,"series":2,"stats":{"get_bytes":4904,"get_count":6,"put_bytes":4199,"put_count":11}} -{"i":65,"series":2,"stats":{"get_bytes":5343,"get_count":6,"put_bytes":4669,"put_count":12}} -{"i":66,"series":2,"stats":{"get_bytes":3757,"get_count":6,"put_bytes":3119,"put_count":12}} -{"i":67,"series":2,"stats":{"get_bytes":4263,"get_count":7,"put_bytes":3520,"put_count":12}} -{"i":68,"series":2,"stats":{"get_bytes":4520,"get_count":6,"put_bytes":3814,"put_count":11}} -{"i":69,"series":2,"stats":{"get_bytes":4958,"get_count":6,"put_bytes":4217,"put_count":11}} -{"i":70,"series":2,"stats":{"get_bytes":5361,"get_count":6,"put_bytes":4790,"put_count":13}} -{"i":71,"series":2,"stats":{"get_bytes":3878,"get_count":7,"put_bytes":3136,"put_count":12}} -{"i":72,"series":2,"stats":{"get_bytes":4136,"get_count":6,"put_bytes":3429,"put_count":11}} -{"i":73,"series":2,"stats":{"get_bytes":4573,"get_count":6,"put_bytes":3831,"put_count":11}} -{"i":74,"series":2,"stats":{"get_bytes":4975,"get_count":6,"put_bytes":4338,"put_count":12}} -{"i":75,"series":2,"stats":{"get_bytes":5482,"get_count":7,"put_bytes":4807,"put_count":13}} -{"i":76,"series":2,"stats":{"get_bytes":3751,"get_count":6,"put_bytes":3045,"put_count":11}} -{"i":77,"series":2,"stats":{"get_bytes":4189,"get_count":6,"put_bytes":3446,"put_count":11}} -{"i":78,"series":2,"stats":{"get_bytes":4590,"get_count":6,"put_bytes":3952,"put_count":12}} -{"i":79,"series":2,"stats":{"get_bytes":5096,"get_count":7,"put_bytes":4355,"put_count":12}} -{"i":80,"series":2,"stats":{"get_bytes":5355,"get_count":6,"put_bytes":4716,"put_count":12}} -{"i":81,"series":2,"stats":{"get_bytes":3804,"get_count":6,"put_bytes":3061,"put_count":11}} -{"i":82,"series":2,"stats":{"get_bytes":4205,"get_count":6,"put_bytes":3567,"put_count":12}} -{"i":83,"series":2,"stats":{"get_bytes":4711,"get_count":7,"put_bytes":3969,"put_count":12}} -{"i":84,"series":2,"stats":{"get_bytes":4969,"get_count":6,"put_bytes":4264,"put_count":11}} -{"i":85,"series":2,"stats":{"get_bytes":5408,"get_count":6,"put_bytes":4665,"put_count":11}} -{"i":86,"series":2,"stats":{"get_bytes":3753,"get_count":5,"put_bytes":3182,"put_count":12}} -{"i":87,"series":2,"stats":{"get_bytes":4326,"get_count":7,"put_bytes":3584,"put_count":12}} -{"i":88,"series":2,"stats":{"get_bytes":4584,"get_count":6,"put_bytes":3878,"put_count":11}} -{"i":89,"series":2,"stats":{"get_bytes":5022,"get_count":6,"put_bytes":4281,"put_count":11}} -{"i":90,"series":2,"stats":{"get_bytes":5425,"get_count":6,"put_bytes":4786,"put_count":12}} -{"i":91,"series":2,"stats":{"get_bytes":5930,"get_count":7,"put_bytes":5255,"put_count":13}} -{"i":92,"series":2,"stats":{"get_bytes":4199,"get_count":6,"put_bytes":3493,"put_count":11}} -{"i":93,"series":2,"stats":{"get_bytes":4637,"get_count":6,"put_bytes":3895,"put_count":11}} -{"i":94,"series":2,"stats":{"get_bytes":5039,"get_count":6,"put_bytes":4402,"put_count":12}} -{"i":95,"series":2,"stats":{"get_bytes":5546,"get_count":7,"put_bytes":4803,"put_count":12}} -{"i":96,"series":2,"stats":{"get_bytes":5803,"get_count":6,"put_bytes":5164,"put_count":12}} -{"i":97,"series":2,"stats":{"get_bytes":4252,"get_count":6,"put_bytes":3510,"put_count":11}} -{"i":98,"series":2,"stats":{"get_bytes":4654,"get_count":6,"put_bytes":4016,"put_count":12}} -{"i":99,"series":2,"stats":{"get_bytes":5160,"get_count":7,"put_bytes":4418,"put_count":12}} +{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":4009,"put_count":14}} +{"i":1,"series":1,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":1866,"put_count":10}} +{"i":2,"series":1,"stats":{"get_bytes":3010,"get_count":5,"put_bytes":2373,"put_count":11}} +{"i":3,"series":1,"stats":{"get_bytes":3517,"get_count":6,"put_bytes":2775,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":3775,"get_count":5,"put_bytes":3070,"put_count":10}} +{"i":5,"series":1,"stats":{"get_bytes":4214,"get_count":5,"put_bytes":3471,"put_count":10}} +{"i":6,"series":1,"stats":{"get_bytes":2559,"get_count":4,"put_bytes":1988,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":3132,"get_count":6,"put_bytes":2390,"put_count":11}} +{"i":8,"series":1,"stats":{"get_bytes":3390,"get_count":5,"put_bytes":2684,"put_count":10}} +{"i":9,"series":1,"stats":{"get_bytes":3828,"get_count":5,"put_bytes":3087,"put_count":10}} +{"i":10,"series":1,"stats":{"get_bytes":4231,"get_count":5,"put_bytes":3592,"put_count":11}} +{"i":11,"series":1,"stats":{"get_bytes":4736,"get_count":6,"put_bytes":4061,"put_count":12}} +{"i":12,"series":1,"stats":{"get_bytes":3005,"get_count":5,"put_bytes":2299,"put_count":10}} +{"i":13,"series":1,"stats":{"get_bytes":3443,"get_count":5,"put_bytes":2701,"put_count":10}} +{"i":14,"series":1,"stats":{"get_bytes":3845,"get_count":5,"put_bytes":3208,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":4352,"get_count":6,"put_bytes":3609,"put_count":11}} +{"i":16,"series":1,"stats":{"get_bytes":4609,"get_count":5,"put_bytes":3970,"put_count":11}} +{"i":17,"series":1,"stats":{"get_bytes":3058,"get_count":5,"put_bytes":2316,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":3460,"get_count":5,"put_bytes":2822,"put_count":11}} +{"i":19,"series":1,"stats":{"get_bytes":3966,"get_count":6,"put_bytes":3224,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":4224,"get_count":5,"put_bytes":3518,"put_count":10}} +{"i":21,"series":1,"stats":{"get_bytes":4662,"get_count":5,"put_bytes":3988,"put_count":11}} +{"i":22,"series":1,"stats":{"get_bytes":3076,"get_count":5,"put_bytes":2438,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":3582,"get_count":6,"put_bytes":2840,"put_count":11}} +{"i":24,"series":1,"stats":{"get_bytes":3840,"get_count":5,"put_bytes":3134,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":4278,"get_count":5,"put_bytes":3536,"put_count":10}} +{"i":26,"series":1,"stats":{"get_bytes":4680,"get_count":5,"put_bytes":4109,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":3197,"get_count":6,"put_bytes":2455,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":3455,"get_count":5,"put_bytes":2749,"put_count":10}} +{"i":29,"series":1,"stats":{"get_bytes":3893,"get_count":5,"put_bytes":3150,"put_count":10}} +{"i":30,"series":1,"stats":{"get_bytes":4294,"get_count":5,"put_bytes":3657,"put_count":11}} +{"i":31,"series":1,"stats":{"get_bytes":4801,"get_count":6,"put_bytes":4126,"put_count":12}} +{"i":32,"series":1,"stats":{"get_bytes":3070,"get_count":5,"put_bytes":2364,"put_count":10}} +{"i":33,"series":1,"stats":{"get_bytes":3508,"get_count":5,"put_bytes":2765,"put_count":10}} +{"i":34,"series":1,"stats":{"get_bytes":3909,"get_count":5,"put_bytes":3271,"put_count":11}} +{"i":35,"series":1,"stats":{"get_bytes":4415,"get_count":6,"put_bytes":3674,"put_count":11}} +{"i":36,"series":1,"stats":{"get_bytes":4674,"get_count":5,"put_bytes":4035,"put_count":11}} +{"i":37,"series":1,"stats":{"get_bytes":3123,"get_count":5,"put_bytes":2381,"put_count":10}} +{"i":38,"series":1,"stats":{"get_bytes":3525,"get_count":5,"put_bytes":2886,"put_count":11}} +{"i":39,"series":1,"stats":{"get_bytes":4030,"get_count":6,"put_bytes":3288,"put_count":11}} +{"i":40,"series":1,"stats":{"get_bytes":4288,"get_count":5,"put_bytes":3583,"put_count":10}} +{"i":41,"series":1,"stats":{"get_bytes":4727,"get_count":5,"put_bytes":4052,"put_count":11}} +{"i":42,"series":1,"stats":{"get_bytes":3140,"get_count":5,"put_bytes":2501,"put_count":11}} +{"i":43,"series":1,"stats":{"get_bytes":3645,"get_count":6,"put_bytes":2903,"put_count":11}} +{"i":44,"series":1,"stats":{"get_bytes":3903,"get_count":5,"put_bytes":3197,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":4341,"get_count":5,"put_bytes":3600,"put_count":10}} +{"i":46,"series":1,"stats":{"get_bytes":4744,"get_count":5,"put_bytes":4106,"put_count":11}} +{"i":47,"series":1,"stats":{"get_bytes":3194,"get_count":5,"put_bytes":2518,"put_count":11}} +{"i":48,"series":1,"stats":{"get_bytes":3518,"get_count":5,"put_bytes":2812,"put_count":10}} +{"i":49,"series":1,"stats":{"get_bytes":3956,"get_count":5,"put_bytes":3214,"put_count":10}} +{"i":50,"series":1,"stats":{"get_bytes":4358,"get_count":5,"put_bytes":3721,"put_count":11}} +{"i":51,"series":1,"stats":{"get_bytes":4865,"get_count":6,"put_bytes":4122,"put_count":11}} +{"i":52,"series":1,"stats":{"get_bytes":3066,"get_count":4,"put_bytes":2427,"put_count":10}} +{"i":53,"series":1,"stats":{"get_bytes":3571,"get_count":5,"put_bytes":2829,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":3973,"get_count":5,"put_bytes":3335,"put_count":11}} +{"i":55,"series":1,"stats":{"get_bytes":4479,"get_count":6,"put_bytes":3738,"put_count":11}} +{"i":56,"series":1,"stats":{"get_bytes":4738,"get_count":5,"put_bytes":4031,"put_count":10}} +{"i":57,"series":1,"stats":{"get_bytes":5175,"get_count":5,"put_bytes":4500,"put_count":11}} +{"i":58,"series":1,"stats":{"get_bytes":3588,"get_count":5,"put_bytes":2950,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":4094,"get_count":6,"put_bytes":3352,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":4352,"get_count":5,"put_bytes":3647,"put_count":10}} +{"i":61,"series":1,"stats":{"get_bytes":4791,"get_count":5,"put_bytes":4048,"put_count":10}} +{"i":62,"series":1,"stats":{"get_bytes":5192,"get_count":5,"put_bytes":4690,"put_count":13}} +{"i":63,"series":1,"stats":{"get_bytes":2939,"get_count":6,"put_bytes":2197,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":3197,"get_count":5,"put_bytes":2491,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":3635,"get_count":5,"put_bytes":2893,"put_count":10}} +{"i":66,"series":1,"stats":{"get_bytes":4037,"get_count":5,"put_bytes":3399,"put_count":11}} +{"i":67,"series":1,"stats":{"get_bytes":4543,"get_count":6,"put_bytes":3935,"put_count":13}} +{"i":68,"series":1,"stats":{"get_bytes":2879,"get_count":6,"put_bytes":2173,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":3317,"get_count":6,"put_bytes":2575,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":3719,"get_count":6,"put_bytes":3080,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":4224,"get_count":7,"put_bytes":3483,"put_count":12}} +{"i":72,"series":1,"stats":{"get_bytes":4483,"get_count":6,"put_bytes":3844,"put_count":12}} +{"i":73,"series":1,"stats":{"get_bytes":2932,"get_count":6,"put_bytes":2190,"put_count":11}} +{"i":74,"series":1,"stats":{"get_bytes":3334,"get_count":6,"put_bytes":2695,"put_count":12}} +{"i":75,"series":1,"stats":{"get_bytes":3839,"get_count":7,"put_bytes":3097,"put_count":12}} +{"i":76,"series":1,"stats":{"get_bytes":4097,"get_count":6,"put_bytes":3392,"put_count":11}} +{"i":77,"series":1,"stats":{"get_bytes":4536,"get_count":6,"put_bytes":3861,"put_count":12}} +{"i":78,"series":1,"stats":{"get_bytes":2949,"get_count":6,"put_bytes":2311,"put_count":12}} +{"i":79,"series":1,"stats":{"get_bytes":3455,"get_count":7,"put_bytes":2712,"put_count":12}} +{"i":80,"series":1,"stats":{"get_bytes":3712,"get_count":6,"put_bytes":3006,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":4150,"get_count":6,"put_bytes":3409,"put_count":11}} +{"i":82,"series":1,"stats":{"get_bytes":4553,"get_count":6,"put_bytes":3982,"put_count":13}} +{"i":83,"series":1,"stats":{"get_bytes":3070,"get_count":7,"put_bytes":2327,"put_count":12}} +{"i":84,"series":1,"stats":{"get_bytes":3327,"get_count":6,"put_bytes":2621,"put_count":11}} +{"i":85,"series":1,"stats":{"get_bytes":3765,"get_count":6,"put_bytes":3023,"put_count":11}} +{"i":86,"series":1,"stats":{"get_bytes":4167,"get_count":6,"put_bytes":3530,"put_count":12}} +{"i":87,"series":1,"stats":{"get_bytes":4674,"get_count":7,"put_bytes":3932,"put_count":12}} +{"i":88,"series":1,"stats":{"get_bytes":3020,"get_count":6,"put_bytes":2236,"put_count":11}} +{"i":89,"series":1,"stats":{"get_bytes":3380,"get_count":6,"put_bytes":2638,"put_count":11}} +{"i":90,"series":1,"stats":{"get_bytes":3782,"get_count":6,"put_bytes":3144,"put_count":12}} +{"i":91,"series":1,"stats":{"get_bytes":4288,"get_count":7,"put_bytes":3547,"put_count":12}} +{"i":92,"series":1,"stats":{"get_bytes":4547,"get_count":6,"put_bytes":3840,"put_count":11}} +{"i":93,"series":1,"stats":{"get_bytes":2928,"get_count":5,"put_bytes":2253,"put_count":11}} +{"i":94,"series":1,"stats":{"get_bytes":3397,"get_count":6,"put_bytes":2759,"put_count":12}} +{"i":95,"series":1,"stats":{"get_bytes":3903,"get_count":7,"put_bytes":3161,"put_count":12}} +{"i":96,"series":1,"stats":{"get_bytes":4161,"get_count":6,"put_bytes":3456,"put_count":11}} +{"i":97,"series":1,"stats":{"get_bytes":4600,"get_count":6,"put_bytes":3857,"put_count":11}} +{"i":98,"series":1,"stats":{"get_bytes":5001,"get_count":6,"put_bytes":4430,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":3518,"get_count":7,"put_bytes":2776,"put_count":12}} +{"i":0,"series":2,"stats":{"get_bytes":2355,"get_count":3,"put_bytes":1888,"put_count":10}} +{"i":1,"series":2,"stats":{"get_bytes":3032,"get_count":5,"put_bytes":2290,"put_count":10}} +{"i":2,"series":2,"stats":{"get_bytes":3434,"get_count":5,"put_bytes":2797,"put_count":11}} +{"i":3,"series":2,"stats":{"get_bytes":3941,"get_count":6,"put_bytes":3199,"put_count":11}} +{"i":4,"series":2,"stats":{"get_bytes":4199,"get_count":5,"put_bytes":2019,"put_count":11}} +{"i":5,"series":2,"stats":{"get_bytes":3163,"get_count":6,"put_bytes":2421,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":3565,"get_count":6,"put_bytes":2927,"put_count":12}} +{"i":7,"series":2,"stats":{"get_bytes":4071,"get_count":7,"put_bytes":3330,"put_count":12}} +{"i":8,"series":2,"stats":{"get_bytes":4330,"get_count":6,"put_bytes":3623,"put_count":11}} +{"i":9,"series":2,"stats":{"get_bytes":4767,"get_count":6,"put_bytes":4092,"put_count":12}} +{"i":10,"series":2,"stats":{"get_bytes":3180,"get_count":6,"put_bytes":2542,"put_count":12}} +{"i":11,"series":2,"stats":{"get_bytes":3686,"get_count":7,"put_bytes":2944,"put_count":12}} +{"i":12,"series":2,"stats":{"get_bytes":3944,"get_count":6,"put_bytes":3239,"put_count":11}} +{"i":13,"series":2,"stats":{"get_bytes":4383,"get_count":6,"put_bytes":3640,"put_count":11}} +{"i":14,"series":2,"stats":{"get_bytes":4784,"get_count":6,"put_bytes":4213,"put_count":13}} +{"i":15,"series":2,"stats":{"get_bytes":3301,"get_count":7,"put_bytes":2559,"put_count":12}} +{"i":16,"series":2,"stats":{"get_bytes":3559,"get_count":6,"put_bytes":2853,"put_count":11}} +{"i":17,"series":2,"stats":{"get_bytes":3997,"get_count":6,"put_bytes":3255,"put_count":11}} +{"i":18,"series":2,"stats":{"get_bytes":4399,"get_count":6,"put_bytes":3761,"put_count":12}} +{"i":19,"series":2,"stats":{"get_bytes":4905,"get_count":7,"put_bytes":4230,"put_count":13}} +{"i":20,"series":2,"stats":{"get_bytes":3174,"get_count":6,"put_bytes":2468,"put_count":11}} +{"i":21,"series":2,"stats":{"get_bytes":3612,"get_count":6,"put_bytes":2870,"put_count":11}} +{"i":22,"series":2,"stats":{"get_bytes":4014,"get_count":6,"put_bytes":3375,"put_count":12}} +{"i":23,"series":2,"stats":{"get_bytes":4519,"get_count":7,"put_bytes":3778,"put_count":12}} +{"i":24,"series":2,"stats":{"get_bytes":4778,"get_count":6,"put_bytes":4140,"put_count":12}} +{"i":25,"series":2,"stats":{"get_bytes":3228,"get_count":6,"put_bytes":2486,"put_count":11}} +{"i":26,"series":2,"stats":{"get_bytes":3630,"get_count":6,"put_bytes":2991,"put_count":12}} +{"i":27,"series":2,"stats":{"get_bytes":4135,"get_count":7,"put_bytes":3393,"put_count":12}} +{"i":28,"series":2,"stats":{"get_bytes":4393,"get_count":6,"put_bytes":3688,"put_count":11}} +{"i":29,"series":2,"stats":{"get_bytes":4832,"get_count":6,"put_bytes":4157,"put_count":12}} +{"i":30,"series":2,"stats":{"get_bytes":3245,"get_count":6,"put_bytes":2607,"put_count":12}} +{"i":31,"series":2,"stats":{"get_bytes":3751,"get_count":7,"put_bytes":3008,"put_count":12}} +{"i":32,"series":2,"stats":{"get_bytes":4008,"get_count":6,"put_bytes":3302,"put_count":11}} +{"i":33,"series":2,"stats":{"get_bytes":4446,"get_count":6,"put_bytes":3705,"put_count":11}} +{"i":34,"series":2,"stats":{"get_bytes":4849,"get_count":6,"put_bytes":4278,"put_count":13}} +{"i":35,"series":2,"stats":{"get_bytes":3366,"get_count":7,"put_bytes":2623,"put_count":12}} +{"i":36,"series":2,"stats":{"get_bytes":3623,"get_count":6,"put_bytes":2917,"put_count":11}} +{"i":37,"series":2,"stats":{"get_bytes":4061,"get_count":6,"put_bytes":3319,"put_count":11}} +{"i":38,"series":2,"stats":{"get_bytes":4463,"get_count":6,"put_bytes":3826,"put_count":12}} +{"i":39,"series":2,"stats":{"get_bytes":4970,"get_count":7,"put_bytes":4228,"put_count":12}} +{"i":40,"series":2,"stats":{"get_bytes":3316,"get_count":6,"put_bytes":2532,"put_count":11}} +{"i":41,"series":2,"stats":{"get_bytes":3676,"get_count":6,"put_bytes":2934,"put_count":11}} +{"i":42,"series":2,"stats":{"get_bytes":4078,"get_count":6,"put_bytes":3440,"put_count":12}} +{"i":43,"series":2,"stats":{"get_bytes":4584,"get_count":7,"put_bytes":3843,"put_count":12}} +{"i":44,"series":2,"stats":{"get_bytes":4843,"get_count":6,"put_bytes":4136,"put_count":11}} +{"i":45,"series":2,"stats":{"get_bytes":3224,"get_count":5,"put_bytes":2549,"put_count":11}} +{"i":46,"series":2,"stats":{"get_bytes":3693,"get_count":6,"put_bytes":3055,"put_count":12}} +{"i":47,"series":2,"stats":{"get_bytes":4199,"get_count":7,"put_bytes":3457,"put_count":12}} +{"i":48,"series":2,"stats":{"get_bytes":4457,"get_count":6,"put_bytes":3752,"put_count":11}} +{"i":49,"series":2,"stats":{"get_bytes":4896,"get_count":6,"put_bytes":4153,"put_count":11}} +{"i":50,"series":2,"stats":{"get_bytes":5297,"get_count":6,"put_bytes":4726,"put_count":13}} +{"i":51,"series":2,"stats":{"get_bytes":3814,"get_count":7,"put_bytes":3072,"put_count":12}} +{"i":52,"series":2,"stats":{"get_bytes":4072,"get_count":6,"put_bytes":3366,"put_count":11}} +{"i":53,"series":2,"stats":{"get_bytes":4510,"get_count":6,"put_bytes":3769,"put_count":11}} +{"i":54,"series":2,"stats":{"get_bytes":4913,"get_count":6,"put_bytes":4274,"put_count":12}} +{"i":55,"series":2,"stats":{"get_bytes":5418,"get_count":7,"put_bytes":4743,"put_count":13}} +{"i":56,"series":2,"stats":{"get_bytes":3687,"get_count":6,"put_bytes":2981,"put_count":11}} +{"i":57,"series":2,"stats":{"get_bytes":4125,"get_count":6,"put_bytes":3383,"put_count":11}} +{"i":58,"series":2,"stats":{"get_bytes":4527,"get_count":6,"put_bytes":3889,"put_count":12}} +{"i":59,"series":2,"stats":{"get_bytes":5033,"get_count":7,"put_bytes":4291,"put_count":12}} +{"i":60,"series":2,"stats":{"get_bytes":5291,"get_count":6,"put_bytes":4652,"put_count":12}} +{"i":61,"series":2,"stats":{"get_bytes":3740,"get_count":6,"put_bytes":2998,"put_count":11}} +{"i":62,"series":2,"stats":{"get_bytes":4142,"get_count":6,"put_bytes":3504,"put_count":12}} +{"i":63,"series":2,"stats":{"get_bytes":4648,"get_count":7,"put_bytes":3905,"put_count":12}} +{"i":64,"series":2,"stats":{"get_bytes":4905,"get_count":6,"put_bytes":4200,"put_count":11}} +{"i":65,"series":2,"stats":{"get_bytes":5344,"get_count":6,"put_bytes":4670,"put_count":12}} +{"i":66,"series":2,"stats":{"get_bytes":3758,"get_count":6,"put_bytes":3120,"put_count":12}} +{"i":67,"series":2,"stats":{"get_bytes":4264,"get_count":7,"put_bytes":3521,"put_count":12}} +{"i":68,"series":2,"stats":{"get_bytes":4521,"get_count":6,"put_bytes":3815,"put_count":11}} +{"i":69,"series":2,"stats":{"get_bytes":4959,"get_count":6,"put_bytes":4218,"put_count":11}} +{"i":70,"series":2,"stats":{"get_bytes":5362,"get_count":6,"put_bytes":4791,"put_count":13}} +{"i":71,"series":2,"stats":{"get_bytes":3879,"get_count":7,"put_bytes":3137,"put_count":12}} +{"i":72,"series":2,"stats":{"get_bytes":4137,"get_count":6,"put_bytes":3430,"put_count":11}} +{"i":73,"series":2,"stats":{"get_bytes":4574,"get_count":6,"put_bytes":3832,"put_count":11}} +{"i":74,"series":2,"stats":{"get_bytes":4976,"get_count":6,"put_bytes":4339,"put_count":12}} +{"i":75,"series":2,"stats":{"get_bytes":5483,"get_count":7,"put_bytes":4808,"put_count":13}} +{"i":76,"series":2,"stats":{"get_bytes":3752,"get_count":6,"put_bytes":3046,"put_count":11}} +{"i":77,"series":2,"stats":{"get_bytes":4190,"get_count":6,"put_bytes":3447,"put_count":11}} +{"i":78,"series":2,"stats":{"get_bytes":4591,"get_count":6,"put_bytes":3953,"put_count":12}} +{"i":79,"series":2,"stats":{"get_bytes":5097,"get_count":7,"put_bytes":4356,"put_count":12}} +{"i":80,"series":2,"stats":{"get_bytes":5356,"get_count":6,"put_bytes":4717,"put_count":12}} +{"i":81,"series":2,"stats":{"get_bytes":3805,"get_count":6,"put_bytes":3062,"put_count":11}} +{"i":82,"series":2,"stats":{"get_bytes":4206,"get_count":6,"put_bytes":3568,"put_count":12}} +{"i":83,"series":2,"stats":{"get_bytes":4712,"get_count":7,"put_bytes":3970,"put_count":12}} +{"i":84,"series":2,"stats":{"get_bytes":4970,"get_count":6,"put_bytes":4265,"put_count":11}} +{"i":85,"series":2,"stats":{"get_bytes":5409,"get_count":6,"put_bytes":4666,"put_count":11}} +{"i":86,"series":2,"stats":{"get_bytes":3754,"get_count":5,"put_bytes":3183,"put_count":12}} +{"i":87,"series":2,"stats":{"get_bytes":4327,"get_count":7,"put_bytes":3585,"put_count":12}} +{"i":88,"series":2,"stats":{"get_bytes":4585,"get_count":6,"put_bytes":3879,"put_count":11}} +{"i":89,"series":2,"stats":{"get_bytes":5023,"get_count":6,"put_bytes":4282,"put_count":11}} +{"i":90,"series":2,"stats":{"get_bytes":5426,"get_count":6,"put_bytes":4787,"put_count":12}} +{"i":91,"series":2,"stats":{"get_bytes":5931,"get_count":7,"put_bytes":5256,"put_count":13}} +{"i":92,"series":2,"stats":{"get_bytes":4200,"get_count":6,"put_bytes":3494,"put_count":11}} +{"i":93,"series":2,"stats":{"get_bytes":4638,"get_count":6,"put_bytes":3896,"put_count":11}} +{"i":94,"series":2,"stats":{"get_bytes":5040,"get_count":6,"put_bytes":4403,"put_count":12}} +{"i":95,"series":2,"stats":{"get_bytes":5547,"get_count":7,"put_bytes":4804,"put_count":12}} +{"i":96,"series":2,"stats":{"get_bytes":5804,"get_count":6,"put_bytes":5165,"put_count":12}} +{"i":97,"series":2,"stats":{"get_bytes":4253,"get_count":6,"put_bytes":3511,"put_count":11}} +{"i":98,"series":2,"stats":{"get_bytes":4655,"get_count":6,"put_bytes":4017,"put_count":12}} +{"i":99,"series":2,"stats":{"get_bytes":5161,"get_count":7,"put_bytes":4419,"put_count":12}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png index 787dc9a60c77844011402fb4332e4b283019d62d..a3b21bc0b9464280934a1df4c911175152ed2d63 100644 GIT binary patch literal 27932 zcmb@tc|26_`#(NNjeQ@X>`OApmMA+ROLhmLBPxXKAzRHLQxUS3t?VNQA-e`eV_(Kj zWyzi;`}RG(-tYJC{dqiopFh5Te9vQ;Im^9W=en=^x}Vqed}d^*O-Id1jX)sibagaM z5QtL{0ztuxq=2uSQg*$BK#*x0>6>YtoSeW{GBY#(el;NwVJA(96U6FjlPaRE4WZ`z z-<7!H9@4MUuSorInY0)NqBF8<3Gug!Cy1`z6V~{XP@}c5uoErBewf!nV&K}`^ZF{+PZ2a* zTjQBwyeBz`)hDY7i2jMpiC805b63eQRzw&pYnUqh2@AX36n6V0>~H^7Pcq@p?UT%h zCrwQ!@OVy|I;+yxPS!FjJ3pU%?ree{pQvV@@HR9w@bK{1+uLVkWHdH5s;H<;O-&)v zy)8a2%w-_fX4Da0WzEgad3kxglXilzAgCVdSa~B5^eun?$X<)lb0HAC2whDzvqx`M z-nv%eFP2@;IlukYZ4LF*6GE^;MxMVp}nacCRY! zjmljTb~dWXg5=7P`kgg$1mX&S8LVtYqkZcZ{JCs8`kpOv ziQ+Y~(_W&u;lo z3{pG4_2n+eU-FuNfBhpQ9BdHY>^F1P5}oz-Mqb)XnfrZ^&S6NwCK973od#LYwkzb0 zMC0xLsfbNjVW46(-@i7YNKu?(kRvYF3M=QI95EZUdV0mapvPjwIhw?!IIXsv`+Z1v z$B_NDR@Y!rK)~#U;75U3LV+nnQ}<2YDw~iFKFzY- z1Xl2(OR+|CHrv_UeuTw);DR&)ag5lACJPW!t2c-#sZDsh=(n$3Z;csrBXE9mI(9WS ztIGMvj*=1_=!m~HF(X|CIVLRE=xQ%)w6nW%AP|k+j^Wi)WmI~<<0P+UJ?1ZwG&-&D z2bwwHQaGu&;6g8p(bBJRwa}#t0Sn@>Uaa}VDxWH|zXf9!|IkKqCr-*a=S|rvWd4H8 z=Kr`Dr6!;DP3QZu%pK5W?6JIIFCp%WbbXtW^i@RLZ8m<}`tLAnuPD=rd@gRPoB4t}&9yKsWONA*H?=eltJmb$Hr z)Zxt}kHGcNzx@vlt3(|MCI-q!R;*rpnRI<{$rsR&oOx}zX)Cws3s4H-XYo_2@54_&(+FF#B)`s(95v9M9V{)wIjajCp9Vj}P>F9LC`8yaVkp#aGclX|ri?gHVk z<@#TaUB`k3^sh}HROmWBJUqt^CqH^yoT5dr%4&TYrd2SMlN55{U6F_Zfg~P z94Ouy;O4G$P=>F^V#t&zxR=Cv=W0A!$R8rY1T*h6-C=YSSd6f?N&+0%5wYYxP%>?{ zd!$7|OF2zPjK!JoNSFMH1hSLl;T>EMIbQS+C>Gg)Eacnsnhi1{cS;uUAj1eSBZUBfoQ_ zVPh~${IFM6#kA)+VRUPPr0YLDQtfNoakC_$w~Rpuo0A~B=d8R}Nq#m@D zzxPwKQ5LQxbcS5Or1lokX$bqw(@-+q5i34am8nlXq-ntq}}+%*7waaQ9>XkV#Z zwAo0J80Ww_QlCF(LLA95G=!ce?B<#=Xu$0<`oT@J{H>6n@WiijjLVj(ufby#eTvV1 z^JbPs6B$AY?r9Op*=FsB_2n4E<0mE5Mf3anKCWGF4h#W@YWgA|GS=3%dkAcQTkLt> zqS4Uf=<^bU*gciOW^r~h4YOFEJ*mY1*i()oHd%!a|GBa3@U!vn)!X%zsbo5xXSZa3 z=-HTv1%}2r%e!s0xX|&{eS~jx`D0%@1aK(So2vtey4Q^09)(QhdsZS*Hhbkp07wym zLDa>SP>Z#1Nbi${CHtKD{`wRWC#958B9voocQ=I?=+90=AP-F_-OYvK#3jo=QM11! zUsET?$Tc|YBtHm9$-b3tyg4VuhoL;aFom*sGVAnh1QY5Jz=2S1vc$f22}tqXifF8= zqebw9Nun)ghLVzcyK_f&pf8MZkB4O#QM+l+_Zh_gXc2Cd)#;0M#7B6Xf*Xwg{ZFTS z%Y~o9;;Lh4fF8Y?va`FJHc9Y>M-nSZBSwpsq-gpD(zuQv`L2-?F|$sHU1fc`bpiX) zI0$Y}rRmY`SDrm#P|6~S%3<65ouTqyAuv`#pThU7neX{>oDQt#kmG+ddCgvYy(t#5 ze_!jyx)KPw*47ft$-!_HT^G=|z9)Xd=IV1Y*MA;Q*;B;(1#}Ei#IFX+==qN&D^F*B za2(_kgU1msh$6vus+Lo)s`&zBka;qj=2OQib&>kofWPfR~cXu|(qK z15b_DeiJ}A)MW2??I=iPZ8*-1Yl(iRLGwvfjlQY>)4E5&$5rDruJzeIZ1KvtPl9hw z2*w&fvscn~W!B$ff)x<`%V;#ZYf)OS=Yf31x92De`}tX}m%o21?d?tJKaa_#8ADJ1 z@_lDRs$Tf>1;r(E2cb+pr17p4obHJeE|PVSQ7cdDsiAT8O{sY1LGvxDnOAQ+`y)h( z6DMq-B{w~GTto{KL- zs|0%DPCvoRj^mXo5N+~+3w9*V#L?)9U#^?7{Nm#BX3(i&u-Q6Vah{Hrifp)C%jxLXG8Ng*yXFXZ=(OKj7UlJC7iAEALjnbyL=^x8W8!*a~iIxqXK z^#!_HSE%i-r*HWjjpQ*~$X6&0mW#H2INuOIO}W8vYvIGG@m2YUuJn)C8zs=et}oxH zjE?>gQ@BV?BqDCF;h|5a3-6rLem09ctGHqZIT4rI{zS34ZqMY(tQ!1P_YUb1N|5X} z0-WH5C*TV}!o4+?ac25Q0pDdtl5Zbc@?DZvQ|c-&PpCDpEIYy-nDfsZd`>qt(fR}m ziH39{tu$w?>`oKNdms5p91LOP6z$X7NpZ~LlJ9;GYcSBv{QME`!T06%*A12<0m6;) zI{hL!tO7drnV_i#W1@;lT#1m7m0hko$>-WVDn2wLF7HT)iUQls z`A27+qg%d^7w*lf0#mFOL%5o2XDH(Aiv1MxBdr-++ycDsP!a+asuI*(E~o;Zy+^u{ zIHf>G*ZE2)qK3ozEH3gx-u~MA=gnWJFv<)jK}y zka1D)Hj^#jGyQ?O4_L>xu3QPe1w6ga<#|i&JWy~@DW+BZ=Y6i->*}mK&LGFlFFjXD zFG6MJ$m*xhfd&Mg&)qFik6eWhQ|aL!I_tX2DUVdzJLcOoalF@9Y#Y2!y=pgMbQcz2 z0@*Akx?+aN^>4hg09>ldQ!@DRy1N3qDaqn%IVo$dikIShMbU$V?8k9K)UM)F3QyV` zMUhuo1p4^UBefT$LVn1+IR3yuwbw{+|COOz1HF076njqTlqy_Ka8yzmU z5b}t5n)<{aJb{;F4_1;+NZb&W>$RNE-Ga5yJnH@Rr;MnWJ+c9Z0Ck5cQ!cT2Pz_*2 z$*_blxVqm-k8F6F;(Rz>pu4lg{&FLfecOI^ikY;l4w_#V#K0;vF@Tg>=z71j;y<(I z-U%K`JWO1(_}A$9@Fj1A>iFvxcm#MXG?aND*if8`OrgN0X@l(xla+e>6M?`K=i)nJa?CD+q z#zsua^j5)a*%@^j>j>b3l-O@*@DUt-g_OpyXak20fIcW*Wv32|sL5&n=qD6X^KUTF z5L3UYt(%j9N=;)$3Y|t`im*Q>Tdp(3PU2fz5ns@L6eOnsPQ3bv>B~`t*|1mrGMiWR%_<_SoqBTw> z=CzA3p{isYi4$CBwKNVF^75O%xg1l#LnIdMS%IG3JQU2cq1i!%zEWk-IGm4A!?|Yx zN-2|Jw$AH|52=#!fMjUW8AIVVy3&t0iQ=V6p_P&IB@0xqi5STw-#t*ranB zHtpa=M8rKI;O5f7|mHQ@>(<}`F$^kO!$8Ac0T#gW_#Ppf|9mx%eFPF$ zoE03s-6lw(6!LYU0Xlmme4Q7wbxjoO^t-z522k0)oJ3Uwo3>4(PmnuZ1ur@_?$O}6 zPtQ^-^u* z^}YwcSu9jveL%YU5A4)ppmgCZX!5GOj}<^qCxy{Evmxuqh8py!5yFS9(uVGCL(t)o zupK;xE4_s&%xdJI!f7LR?Cf&&EjHBN$Zw?U|+p+fVW45U># z*auO(XzR}(p{O1NXr8xQqo*y*G?7ZqzwpblPOlaNd45yM_oC{$?X4nO$TZcIs& z3iq4q?)G;52xfMOQ6^RrU%c?v9uaLa&_C6%ARM7(2I!SzH3h~bzf*z52HlUykf(bG`c-_Z>nyko8pnWk3H+%eVI1_R+HyZe z-naqEZ({^UI4_87)GbqC*i3-~!I%pF2f8&29Fhn%`$XST^MTBLv*O{)vL)=OP$QH% zaEs2CkyL7|&_7PuN8q;xqM)dzNNj88WNzY}35zd=J8AIbY@kCfDaIEkK$`tDR#tCg zCwdKi0yQEV)PbF@2IDW^8UE}NvIW|Cf-Zsz%o9TG+dU#!r=8!;tP2NR=5K-0W+G4!1)h<)%o7{EdD+!qDPjmPFs{9ro!vrFYnno zX8P=fx%P$u_Lr%I=ppa*`%$c)4={3ZV?NoM&D8>E2kWKe{NOVwreV-H=TF$R`jccD zdIn7=g{`oNwFGSX#adS>q3k)RX7ZDONN2HvyyjY)<7ImeON>@9zX>q7V9#%%ZRFNM zOUsm4eN`Vgo-~3WfZNOF{y2Fxp)gt%d{K**g+2VV^f#_kqkNx;9@Jv8{*4gY-4nTV zHqZvxob;d)`#9BtO4Ja)2kwa{Xac-U#)T=xd?DS~w{uEh!0UB-a3TGPHsDiU^=LjD zx`;y+7&S!L@uKZ!Bp8rOWDOE%lT%DsU+!pyab`V0vw1HUQkQEvJ5Nc2bINjC=ph^aF-XS2duA=J^t2r@-na@Tmj5oiXOe`Y1^)9CP_ zr-$Cbb}`lhkczNo$F!1Wxv;Yd#2`#7@+bnDd(MZCVSJZb5_S99`Cmk=B)U=JBnoQF zO}+)Nz179&Bw4>CZ0Ny@1uE1ONMC-T=^wfB?KTbZ?bTlQi-o+Jj#|HCE~5Q-L~g-5 zi96J;1vVk}w1mI}S;7XcX~L6lT%86dd%AY2#rV1+*vpgdBL5NUfaj{Y=KG>x``3Dh z;gkIYPKK+pu>GHu!~+_|w_y8oO)g@I;3ZgdwQZeI2pxeWKe%#}%GKO|Hfmgm3Ji`w z4(k7jIOIpNqf&$iy+POc5+v?HG@IvzXV4)hHCv*ocsRkjh>Wn0WJC#gh5j*FeGHDw zGFt*{z87+#!Yz-M5Yv3<`Moo#^Q!43ju$Xnf;Xv_&`+i%mn^!mC6^6(A|>(O+38+= zJd+aajnGBnt7HPT+4WnXxbt<^#1E$%pf8*0lt?j}ouZLJj+$X_U6p|wB51?L1vI+I zWm~&jtI{7j3(pdbDaOR0waeV(e0Y0-chD|>CZ-idhVeZWf65 zQhWo$wQkw759H?9Nf$r25v)nMT#Ei@fhaM&pi+_}A$weXWOl9gxe!{~II`sV%x@&_ zeE~G{TXo(W6IOcy0B|XX$*Xmy)MwFtb}t^SjXU=^Y|yu;~x>q%;YewxQgYvVN_&p z3ER-*G1Goo(QJRM7cJFL#yK0{ph=A2kk`H{&s03OMBsODJA(Upt9UyVilhCLTpg+b z1WzPHyLy};-!fL5W19^1=}`hpU#mGsxld#;p)573F-}89te_j!9@gAAtxJ{%_M%jh zj{&dWgGA1dph{aAT8#u;XWd|3x=KNQ2|5W%b}z-4@FQbs>;ZWJUGVLq&=ZKhdKXJOHTTCwWDFYjF{ ztX4}OfwMcZj9Sd`$zcq2QSpJwrAVQSAaO~ze2=_8$xzn|68G6Y?;kWc!+1&b4UQyi zj)AIR;KepK@O-%;KYqf38m!j%s!^fMoH%>`6gVjMSG2-bY*rtdIcbj48b#10$Voo5 zw87o?o?O%_2DOl1s}Bva*CSdOAI(z5?w=iQcMh~Ve%c--GE{S$u zMS-tu-+UyD%2`xG9e2}=0b07!Gl8uV1~Cx*$$K`^ag#1!ApiLau2F)tHWt6iV(L50 zK%ZTZ7iuOht5i`+V6`w`!@PEMzmNgxFkO;kFU*it|ph?v{l$$PHX<7 z>O988qhxa+i`*r|254}iKJaQ7G~)K4EY0$yc0G92!j7_sw^RK5GE}&47vDiI2i@5d zSM>g*-5$U9))rv10U~wMT^~E&Wh5CzwuaJnOJn1U-$OG`=r=s>xxX>ZuI9)0FO|^X zCOpdE8q%3jw))mU13l>~hV5cf26N*5Kqmg;)udm`!9>6f=^b;nZjlw!&?MjOus3~0S!6Zz221D_S)E95O*c-gt{C*Y3z+16s_ZbfGYv)J~J=gA$_Zrw6GL;WsOu~Wmbf}NB)SND6W;SCPzsxEmIDd?Db5N*pJ zGuv<8-~RD?oC1qr9XXZ=Bb` zY%$OPv>M7ZK*>+F)D6A8DdM49-5K}qI^*Kx79_H;LXDe_gu+j->w*B8zVTwsomfbZ z+FB&=n>ObOilO{_jf()+WAMBR>?%v3OC7k~0tlQ}l2jh2wd>aVisRKxzPVe7OWs-v za=Fvm+Z_NcR7hQuWQbp5#t! zq&;+>f=Nbj`5TYX9JROGsaRh{qZRU(x@UhaT(=SS>}Ixb6eg8pu7Xz7h6CTgLrt@K zE3Qee^u~&BsV$-`J3330gpKPa1jV6uRlqj4#ZU%k>Ix~_bvqW4*9dAKl)jJ zotQhUFsq#AYMIqsVi@MqJrfrsrWoWPf0QhG(RlyH1;x1Igu5413~YRvr78t&vtEm@ z=F?WmJmM^z`*X!zbeN#HH9G!`o4+^<10{ziKDA8JN#}Zq!CKmN_Iz9Fi`=s+jK8-U|X9=7#2ZjVJNFTfr!wAW&^Q0ngX z*{Y}t#n|Cl0RPT&y!dH<|C<1)#BS#?p9?K3u{TDLQhx|q8;7Imu++o{70RGO&iW_l z5Cyx+Z8O}T4@CF*Z#<2A0shfzb5FfLP~%v`bHr_|#kK#MhaibWhr7&)a-Bp% zc2Y~NC^~}5uv|wOl$v0^+!Le@aNAF?lf2maj#|4;kd-dn&)iBNbnyEsDxBGAd*Dw! z(b4f1*!dzcO4h%ZkS$HcoH!+}_o9`Jw9&bNl|wc3>>!KI)@%<+ws4*fcnSI2lA45` zLZ7^-7!wCIhOkaCj+h2pKu_};7?2xrNI;@UA;}9}N3MkZfC%e0O6{eFmurk)h6y#j zT!9(T#eGH_AR(gdW(%oD=Q5u)@Q%q-17ItUxf60NPiprlH@IZQA0vl0wUpZDF3Bkv zGx@U-zrDb19=h8uF8LGgbVRUwln}ltgr!;h0cf5mz{7+pSAmV}PWr@Z9;UTW)AChE zm>7YRq{W$PACg{JH}If=p{~e7__}@(i=~t`{GFJQAlbn*_}ZR>NDoiI$)Y zW|ZZrT4iS+@O!#)1ciW;jfaLWl%@YL-4J4 zH$S>;3kE2SuQP+(3K;mkiVWe*^#jO>JX-N@*dTvK2(+NyuF?0p99!A^s)qe8W-H)E$nY9oQv7C z=$Rzwtni~WX#Ck1mc${;Ud&d%5_<2p>sDSDAHMK5HICHtRtG1wN+nhoGtENURxE|) zkL+1V77TFApjM^=3Jk8FpRie;)Nh>jb11;hCUe!G(Fg5SMFdWo2B!) znX&uS#5jI!V2&MS0E~A~FmLU&PV(cknSw_uonF{@e-cEYPGoJPAP0s&UyecqE>%Nq z%OeDbKWXCxhZ$U8Y04XoB>gtY%f=@iH? z>!m6T`e-*?K-ajytG3Ofh>}%mz2f^J>u*Du6Cv6=IgA!f?N#tzKiBGfj3V<?D;fq2iK=7ipL!t7fPJy2Me0a(4Or&0Q3DEd!TRM>v3^*`g zB&7@xsEtmKovntUCfXt?xT+E+(s{bl3H3Y1oJj`Y! zC1n+kteNYBR&HhCV^^RwEr2g+g@|g(Bm)2?vVE{tCeQDVYf~C zK%R=Y(Z$MH!-qfhs^g+f^YcWr=)jS_6-Fc4LK-2Swk)33tjjam5rKEGjHs=Q!vTWy zfq^c@oaxoMJC3Nt+5(88BvuWO8jsV|*q+LuNrO&jWjxy?#j|V&ZA4H!q8|3r<;R!bdQ9M#Jq7Mfhu8vlX)ez6KfLSL-1HZ7^3QZ+ zFdH};2n@Ye70j?ED&cUP4%#&;=Ttfe z2-XT8->aFBaM?arPYKr7JpA~|@`=x;qkS9poi|JNRf%&TEShs(Kdz@X7WFbO49330 zdD{Rj-5D8k9LS}|$>R7ccLk(u;y9TT9fs)1+I!Gdb3XN++AmK?Z+KWxp}TWTq~oWx zSS?LqCJS==9Cnl?M*~9=0Q~TEWhZ?&|D^WpV4W;HM_ze>T+>#d@s~#yz;P})Xlo!_ z0;!U#V`qQg*7|gsm)8IXV^cqAig?ox-Mf!cp>tUxb{Ei<$T%eNJkgMqu#YIfv|j$A zR6+UYR$V<`OEs(d6KIE+-;3PQS3p5>o?)W-$7uuBCnX2cUmq9GOufuVYMzk zmHtA5^%Z^zUAJ>oUEL|bOP&kwef{I)0Ss*=eSckuh05T7em3nUVx`iRP$u?t^(Caq z$l1=0;?SYO(btLMWgBV)@sE^};7$84dhiIrK!aO+>BxdodL3fQk$02z0@_Y501NNk zD@>5M3>X>;c{x~DnORG4*iKue#tD&YFGWCTXO^)KZLlw#HGK-MLyf@MN#P?1hm%~Y zFj|JsXIW5e?cdH5E|QOXsR3m5myt^qv4pbcA6Kb}i5JA;4C9OWhzx7#(5@FqgBvkY z7Q;Vsuf7yGL$hQus18`^@S=%kiYMAp@v*XUK2UZ$@ z&*)sP%mx?XXBr(LP-+L$y881JoZKSRwj`g!jIx*L@ItXwWj!_rn6y1avA)GFe0cP7 zkyTYD4~qpkgDSu-+wyYe#KPTBHa47*l$M2=?P0{^Xp9s{ANZHBBuYlYNT^q;Fsx|q zR5&H!@~^3{9#BQddie3G^o|kuH-DYZ-56{pL~rm^rNI_Hjf$1D4j6(duezym85c3| z%JKvXi?@|?6@k&I-`cU7zytL@eLnPMdlrkDA7gi3{jHiD<8%(EGD~2$Xi9#5Q#G|w z674~sb(DLGP;*dJlU_@Oc})qgBq63=U~7iv&-m-W(6^tun@QN$>KZW0(ZrtUd8u{W zD7yNAG9Q6jM^;%>`7rg*g1yJQ>5=ep6(xAYXd@SN9W(bi_RIOkDHzTb{j1KZ0GZ2E z$gXn?vo$kd#0lo`)Gv7`UE9=k~J3Q6E126OBJYm$*eIlP!hHCD^E=y1=&SBI z)_zE#^vG)$c#FS>hmK_nu&s%D(QUgt5^y-t8_wvJB<{Odhrayay7*zfg||xUp8I@T zMQTM!IveWIn|pG9WC*DbD8bSN?ERtifq^1vd7t-u=x+9Hk1sr*&Jd#8x7h^wbR=#X z=4|SEDvy1?fFAk6i$~Pd=nCMsJlfc4>|1B1Rz1j%%OG+?299&sL3v6Ho2FQVo`!k` zGyntUYLkNKV_-=aT(5q0pa!@psR9q)C(N8ex%LIakS%RH_Vw*AzeZ@D?A>Y$c2he8 z$`Ko+t=mJAt0~M4*{;yh3@)fU+;J+n`=R6m6-^7g+-T#uNxY&Fej30s0(YbJa(nG2bM%HB9}x>GKBP{wCKR_D;R25y0DUv*O8J) zI9fY26`ht*{*XEGgyty}@ZG7PC-vXlv>hLhMh z50QR<%_cQ&odyFnQHPwm%8N(CeqKl+qVWj@tggIhgaZO}`oW8S83F%B!naicL<@rO zO&x;fc#$+=f1fgwJEIQ=gBe^1qD#9F6i$aZDmHW{VE#D%GU;(u?qKemS4`KMM9u-MJ0 ztU^PzH>y&s)yyWi-+(qgSMzE@_Lv*~?|8poz~{QxTA;tcE>rA(Z+-!&GmCKYD(82o}l>y{|os zWqciWO!nysps;N2rFaEiZ616qDlwyzGc_n|D7bbnOWvZV85Zyvu=8oMFKhl7A8)~+ zugk5J6ZGtlD zgmqb5lqUZ!!r`BcCK-g3s@^y9LXNPrnSV2dXjN%@;>!N_;(fl~tOs(&@8`WpoCI`? zJO4*YaGtC$*C{*BUca1Vmw$)|FGqpB`R*U}&-gnT1XGne|7uecfKC`mo2)L~=*(!w zv1q08=*R}7KIQx(>-*$!$m=qR(}VvqhBMtiI{#Hrw6j5ehm0oJ)W;$DvSKd`M=m$3 zMEVOy%c{iQe<@r0q2Q`CGh;%0>fFB!;9Ta(@xe{~lbE+VmLU!qqVx8KaN4p`-Uv-^ zTzFV!!fQ%UDfU9&p=x$O-}0icnzD-SyM&qZFxI8Bx$-x+Su?fR#l4B>_2Y?upDi^F zg7K*ai&^d>FB-Kn`qaeMB%qKmZgeQWsV_>lYc;2)|Ey@rYQR8b-Tn$SNEVB+v%M0n zfTo}5(>h=GkCF(2_u=fo|75u{zS?w=sd7%dIv^nYKq7IIq>0P^Fr7P6G}0#QlBzZI z^>~>=%8fIe)jm$Y9G9HC{xoLteN#l~Xoo>f4a`)DqDsTfw&?iS9tpV9C1guv!$nyd zGzMfNYdA&~SMOQo<;?y(<=Ob&eV#v5clM8~OB*&(0zCeHM_v^A3b@S)yV-P1*|%U; z^rmm!vpEuQp`%q7zrcLZy!7IVbD`Ocx#X+HU|(~u4%xw*q8mx?`j>_ru;U}1G1K&mc9=lIsZIqZv9L~wr)6^ zcSXuU{%^@Z!a=Lo&-YhKj+x|o+bv80seabZYg4RJooXLn#QI;;{9rU_CNPwD_+<1G zPsnyVqh#kagR)jUxmaE+S9WGY<1iHuiuf#krfy%mL(}Qv-wN;t0pyYnsZaCAvR?bMa#g{pk~OkbF@uios+OH%)`N)4!0DSE4bK}d4DM@N?Fj%$C#*ap}fVQ97uc6 zAscBPZcGEigsJ(?4m!y(9{M#I?8IlEYxcEUG}C6S-%AG;R~i!wY=CjEE|D(|xg*6V zZv%Hk&*|6PoB1@V&ze|Xx6XW-omkynxvyQPc}7>X){2Ryuw_(J{MVc4W$Nok+ezLr zQYy9Fj$aPt+9mB0<*ZZlcpg6yrj#u$@#4yr@C*Qk=alCh6qtI7LNz~-Erdro(>v_1 zm6oKg}N}q<$9)t2Dea!GjT%p_(Z7_(7I{2f zRjI@rQf)K7zl_ROu+x-GNm@8o6heLOoA?R$$9I`oWH;Q(gj{{j?Zey#PlszvJyz2E_EV0Khr~jFu$612cHe{XCu245}18?_FdFuG{i`H%fwYJ~P z-zKVwzu-40r*>VvL`P(rU>I#Q$to`EXDybmeDfL-p8iM=78LAXuP+#~creFaLyHJ& zBnfJB5)6U|Q)Uxd@8jLzogJ zC=lNncqKU)v#o@??um-S1Sa7mpUomIU_uz5b!)B>rR8gLBM4;DP=BGQm#?T}4Q%%A zUwfzAmY(L5Ad${YvObMy`Yq#pd-c-;LSO9+(S4DXuG}<1ogM}okFR%yibtnX(1A?x ztEo=(MLoaBZPFmKCbPogN3Q;QugMT=2l-C9k(fq%27o@OnHL@oycOA^)9OSSXmp$6iT z;`1WDABk4#)_`fGUV^Q*?Ug`fz5L60-z)89#_dVGrVD{t!Zi^m3qRZYv42IUqH2Q+ zVb-fpgBek5XEOA^CN4n{c5PxMxvIhEE!3!7(~Xj(rQAESFy;vdTo38*Q5|f_f|f&X zCDI@N!yMaOE&;11rH}|jw+x!)2h3SOe6RmFwsM&nc6a`lBP>R0iqyM&`CN=08KUJ* z@UU^IeJ|6Mg*11&0Zor9-1RW82^%tO%=dc_pMm}s*^^|>BR{WQf zEkyZ!C>bj>b{50-#BR=Q6ry(BVMj2ATLE5FE=aLcNBVWn&6fo+5F)DM(C6~rEjgqZ z#*dBk5lr*RFcU%`*3uw@Tal^GDCLaPmFyzs;+f@5DVDKHGbm7wG z0)AYH+9f(*>u)gU%9#}2P7lGoR}LWp?daAw8C}AnOJ?0WMmOIt&uD1-5#ku4(*!H8 z751@OxL!Ln5YV>UXD)-eOA%_aZ&O`|xGHJ1`S3#`|El0m>j1;mxc6Ml5-~glL0@GW z=|DL@ruM1@y|$X=ZsWL^9rK;3c{*aT!pZRw%X&9+$VSpONl?Y4!$UBoA$hF%Wi%~e zae|P_Ga#!iTaUK3Ot7Y_JtJHkZO!A5Qj23F4{`o&xFOJbcs$V&319Y@d?g3zFp7qi!_Tf4t!@viI5{(3p^l=~u*>cd*O1Yy71z z$z?F*CzX3F^>_pi@#7Eg5D>~6Y$5NGcW2->#9IGI$6Q;gd`P9|eq*Q2CUW*f1oS1E zlRsS!T}jt#(1#gMoRg15Ij#L{9xtk93Z zO!Ng5>x!246w%e?avz#UCHM|phd{D5|6O&8jY$jOH`~@C(+`nFuCSqvRs5rjkE`LK*xU9c5q2f=(L-@kmN@27vSfLO`@(3LKJd8B*P zw;`=neeL~R>m79@&7qRABwm(COz^%K>IQR_?7DtoUpxMbt5gYdhkoBZY=B6`QE&^9 z{jdSLgDg;9A%n{)gy;b4iAm1bfW9@XD)7B~U4sx^Ri{7EhBGdO3wlf^Vc1*)QrrS* z{tDnCIcE07p*jU&LmKfoN$NZL2LqA@W{0dfT|f4vJ8l?W;$lopBKzL|*bac%ECeHh z%Yg41Y+y=$9uOSx63w@5Jg#@v3>_T~oP$X-kC^U!MxpLhE^)w-={GydGQ*^_SHE4y zW~J=+L?(V+eKF8_dueND?hCw+I6mu)9Yizi*pPmkJ)o1OhpYdiF72w;QZs2jVOYDL zl{}R83LkA9onDU{EYfS5X+>q9AE1N_Q~xb2ip}|I4g>Ei{9(mv*Prw^yDYlLvX^w9 zBnE-SmI*FW#d%B>uJ2waL|(47)9e}l%Vgpa$bm?ngPDr*MaUzE+ZEwELEvp!cR?3I-Li8);`Sk!117yqSB}h zdjeCWA#PdAjc}1c%U|+ee5CA!L8!teaeA@ih4|&WUl4Wa#wQ@w4&GH7yeoAY>f^oh z8L+*(B2-b+dKFV)N0o)0t&!#rhZaLZj$7fZ2Ic24Q-BY>R@Eki52@vMaf>azjhEOg z;7)Yltc3YtZ{S22wuQ6Ievw7A-yRvfD6Q$poGtGHMa2d`LQm!(ztB6cLX$bM z;ztL;MEJ^;LIZyE(xUYKe$m#HTGu;Wc4x3)>#mPN(A39Qk=^EP8KByE606Yx*-G80v)OW z+P{yWzVtUvw)s=*9kKWaa0I7Gkzj71_+cEB4bQ3$r}`QkB^cg#3U+jxsw0}B$?C&qWBKX8w8Aeb;@+SUHaBP0Ssv{Bjs%S~Uq zn7`P={2~)iNlBO<9E*Dh?(y@yX}fCE+^p-(ZF4Bk}dofW9oU5B7-amjuvSd1_yPfrz)oPlNaWcS(r>6^_%F!M*9 z+s;lHlacSSCJPg8G8jqsM}&7@oW{&LHD!?sF*wfonit7C!rlsxeK9FiRxOWb-C2ce%`Ky~+?DGl-&x8Nu0{jYx8WWW*0aNT<3EbN2U3(sG|F7@Zh)APX9@dcMpM7%j6toTp0=BR)W8=n zEl=N94NwbiLPZqc@& z>Blx=Urk>fuTAldX7OMU8gpw~sQQYLTU2@8^A+Q(Ob*sl4eS(AEz}++L)rE{WMF+t zjRYyrHGk`7Qr<;(E}Hz`mYY-{oaGd%26UEPUaxWIzHJ;`6Dv06vpFqi-AnKCBA~t$ zt|v9NfjBrKQ42YA>#t*de{Ft}u4?c(N5y1exl<0M)~r~Q-I3zJ1ip!|cpUu5;d;5v z`e{O-+)Zk+eY5>f2VA1<{0-1^3O<6%*xrIHi~~HlX?EH@u;NiUiUeI-x;NIN3U~4Rwrd7Y6BYm(LYzz{w+qpDygGx`fo>Rwvsh@Q#f*uP^cG$C7 zG&v5zbUR;zmB0p`p0<#I@#e*`4{t6$Ts1MM-HLeGDT&@TmFEt=_O-eDG?h4I?3~qyPg7;(EDDYuJZK+(nEzQ2$U_mE%F1`An5osva4L-4q93$Yadg z4N-bPZDW#jqYzqeE_~wZvs%Wx)>lC$>_b|r+UDl_VJ|AVw+X2c;U%0f#;~sUN*#!x zTBUZSUBG$DgqA}CEI-4O?gj|{H1RYot`?rKEMsFJI5hvRH42{@NXU93PmN1n4txvY z>F&Ave>3|XLYQ(rE10uEHm`O(QiA&Z8>4DD+w=(~831>-AiVCDmHTS3NXodRn*RSC~ z>Y?NBa3AiT1!Dx|=Dx+lKOl|Aw>28V*jI2Rq=7g?&Y~10+xc5_vtoi)PFX{P7yey= zt(H8zSbi2eQvYKw^kIkKe(mElkq6or$DLWcykXvuX}o z--`J~&e#1r-159IGpyx$xJGfLVjkxP1FY0mGyg}^`6x<6O`@vvY?>&p>3W1HNyYan z^kr>nHv*~6d(DWkr0&Ky$qbeS1W$VuAq-y29#g5K@Y=MZ1>poq(BsoUe3G{YVB4Zj(FX zji(<~s3A1|)+WRd>Zh^1O)t^j*s=#2ZT*EkCoU-&=t`YZ&tQmsdXA5LsA(qXVOB;{ zkNav+H{T9Q zI*hsbm?WCu9-)Ds)XCxIxdgUp`MNK?zOj(GnKkl}K$h zJ7P?0R|WN;7@=M*9h=X}FVWA*e#T5o4-$>>Lwupuz#& zX3)MaQjWZ7^hCAEKGB25X9=>GUKTAm!_rxcpPQ`JM(&;rR|;4DayB(^SL1yBY+g*# z2qOljoWH)TLoQ8T+l!9nAG9)tSC3h4;hXxq!1!0*bURN)rPo^YW~Nx1g{zidj>Xm6 zw}5QwXKMfiz3u{K$O}m6$0}UXO~}t2V){x1VPdnB-~dX1kr!?f$g z2IM>a9^hX#UxZ}U*8Yt^zWafCsAu^pu8;HNxnviS?hVSGQocV1hLDe*YoMdY%y(NR z(=fUV=(eTD6Xbl(lovMjworug|b#OS77)*klE{-$$2GZAFyji0n@uVexWg%9IkW`fVY(W9;V zrOV_|1g9s7rD-N@nho`@FPUa-B9&yjps3>$@+){C(H#JC&UfUVL8~hLW80mT=deNS z;gYXURENe_SF4W)A*h0%e{Q7Yzkkw&m(NaJKV-Iqt=G9lG!k`E(g3i1FM3OU^Y_R zdmAB1HQWB^dWGng!p!y0?4a13L=fpwISp>`LL$EMJyCus3dgS5UTb$IKG8z^}T!B1kDCJN3RR>*-Tt?!6pEoUG{Vzh4aB`Q#Zh7)XV+&EvY z%4TXJP8GtjrdCRY&R-omiD~1Lz%^As=inXb3G5_~cd`sPJ$Ry0YuRMGdvCH1KImR5 zWut8yXszPGrdbrxwwd3MVkQS>0R66iDC+~^sI#Q7`Zi67leXst(AH4)42?Y_%PXrX z=|c;;41zjE$k6lwXszg03{mV(`f50mG0ldboUcZ}FM`zNr=>n}hQzE<;bbQUoW#AvymQlY3**M>8wqVCO znrYF%F_tu^8BJ|2^JD!clJRPRGd|?Cj-b==tGJv{f%|Ba?DcU@-WaSY=z!*F9y3Gm zO`d23#2lLEK%Z>O&zu0$GaGoJzjrVZm#S4+`sNxNL`akSDj`zo$=MkK-Rcnk?l?K? zIlQXR4I8*5kk^RbFM#CGfe!>$p06+%L7mrDKzgLZ$Dw9Da$|b2MexqMYtQ?u8X5=j zlkAGaOvKAeteEt%>+TwCeYsJe*)?i#V%T!243>Xzm;~1NvG&`Xlm8dk-Z8loJ%MU_eJKuEI3(egj+;$2)2!0KOdVhC%0p5MA}W9KXxzG!0B*VAfz?p` z1t6YWt&ImGp{_2_7sdzROyX+>$ZG|o4-Wx21^d7BI26G?(+uTyYuCa{D&4;~*;*-v zZqWZYtr%*4_vU3V6Mtg&ggc9t$ z{9gRH!G%j&pfA&D*NLaP^TR1FHbRr2X`Du1RdLk;-l`brG+$fBlI_few4w2X%}>_w zx6pBl-|nJkdcF_+MP4wOs;SCTWz)L(e;33GcK}hmdn9GNwq zL_KHvy(h^1U~Hv+bZ$ECn@ojK^jXkC)Nl@l$o@MM&jK ze;YZ8!AoVe?=tJtdw^6JW+OzltIl5OQ8^A*N%{xPBp`2!w-OLjdI9jBYXZladT@WH zmZdzuK6l(5?%*#NlLk`&-cjm7!Lqj|blX{<{Ns$HhE2=REk-|Zt%7(&m{$@ogXwMI zdD zYM5g5LNN}xZH>-F)ccD^^@}@yhp-U&Hdb;<^$v0vi6driGxvVH1toO8srNsB&^&m> z;wV_%XUKObkR(+AS~qtX0;Q8gLH*57pF?1|U7as~ z&+&@ECK)ZF^YLP!*_?j~k^Z~bxba@R&szkx{iD*LowuudHv~u~zkM~*xJ|GB59kdr zgoup|!r1&pPSuI1;5eiY2XZ&^D?G4h%KD+R+>mfggp<8B>DP3Sy-|N_K<%;&OrNj! zE!tfL2=R*U8(F~Vp>75M9P4@DFr_-|6plD~EZ@w*^vxP&eD8N_d{nK6YD?d_ka!+j zWp5N$8xpIzK1f~@1rk{uC9ab?#WX!@>MlDKy{F|R>yhu)$^2xyywRa6*8ge2%mAFD z!nJo+H{3noMJ>+x&ktXb*Mgs0^Jx5a=;wl|t^6*2UFZ@Uv&==<=8Dp*UmWZ4Np_vH znT@f&ah1I5ss6Vh)Z^0wQM+FYnJSGo?63VZ6pd$JX=Gj08l7zf5!Ey1^k{6DFM|fXsTn_KU^VlEK+VONkL;j~UXQ|qf^KC5~k~aspFl5`5 z`3KgsvE7OiDdA(~CJo=WT7+2xZ!E&0Giv&8O4M zJ<39J;2@7KVAssF>Dp`#e8MDp3SyZQq)usaiu4GO=ActAhZRRGRn(Z_6Q9g1QcZvY{15H@yxg>68-m}jnH>edF}_U( zncnpsfcj~h-FA5%?Vi|uZ`vPMW-+b_vzFkc@1_e)Qfrh|JEif7_6sB_s{++-?2Nt2 z+aXNX<|i(>e$NgAuh+|8FQ(#~H@%Fu?g%}=`E zd}pz6S&*|0=y+`A(0a!m@4kBj;sh_dIeqP)gsH6=ps^e;RA7Tbo2-Chts+Y=K0smaqCzh;Fe+ z+W`xi_m7>!&Es%lUWZ1>m%S~Y5BV;_$KH9fNM7^s)Bx!k(H6Og}V_}1{7 zk=cmuLN!8JeE~#Dj}qeJ=m^66_6Ke8a;%zBQIBiSV?)tm48ki4;n|t`aEa7kA~GRA zCDjm2)Mq$aW8t9&ZE9`bHLlbkKU-FQc?d-u*^4N*;1X@C1P%v!pLJ`1rwmz<~7fswj;|y8;%spdhkbgt0 zSavrI5#D#^y8<9eHAZ4p^u3lhj|H(>OBFar^8*m9F}W}id3eX&xMyW~Bd@?aeB)@h z9U412!t2uOp+Xu3ksh5{*5fAWq)b_g;P7gQ~lK5;63nPiS}S6 zvbVM?s6wjzX3$i{UM;d`T{0#sJz3t?!bmK6;eM&DBq;p3`iA2mQQD*97qD3*U1)WO zO9TNKt){w036dK;Su(UDkUNM{s$S;axp3xqEU*S?c)HF6oA2td7}G4?$V(1xoloBTE+#Wd~-aKZ-40Ulewmg(A3{TX0( zo#d8r2^hcClttS%L=T4$j+0B@t<00{+@g-Z!aMdd0&ViB3{E`Sz8KAI}pJp|?3~#QY1nl6GjfN8ou0#pWc| zTNm-BpHP7UXc_x1i$4M(Vhay8Xk^k#yJ%pEmaxIdgZ26H0g%n!1IfLsVas`B8gbfp6IEA}(BE%Ys zt#31d4Zx~w+5l$vIy&u{6w7xx)$+n#fg)dLjtcamS&X!}A^%1a8 z5m!6OgPITcY|-wfTG6Yn<%zXoCf0>FJ?ki^C5-q77(=Gc!MaeS!GI2oU*0nT870L% z%Qw^S#trf=9H!~bZ3}Nj8Wy?U@*v&Amno#0gM}^2C3?9Cx@^I&gsMCqw^0x&)H4+! z>U^Jgu`0KZ%Hbw|Gb*g~)K>zrj?E+2eY0+vpGQ5s$|c@hNDMw3Ip@?`cDy4d3CsZs za;oSyUV4qUiu{2fHk%NI;}=~o;vHQ=IpG1fl^?p*)i&p_#l<902Do;3q}4XPkB>A) z*uv*-ls)|3)uOZhhn;Ypd_!E}uG=MrY9ybGX<_cWr;7`AS6CS&XVvFhlIc6zSalYIxLHqr8DK=|SFGFb~Ue)&@cgrmZYm`XsHCli@cifz#v)+nl65QLyTm} zb!Ko_8!UVWn8~w;@G{-0Hcc~`I8>X0c%UhyI(6CEiWw8X<3ldZao+e?%l>0*>9_ST z@W?0zt_VV2`{8M($CD#**#|zdENYPH2?;7|21OCRI&gy=Esr=m?5DKmndFzuaK3N* ze_w(-bWZ5MNlg)slAgV6(5n3BC}24>LL%*x@sqOqexjNvt;nlTQLPxA7U_8$P(@vX zE=@5cEi!!yjG3&R{@x zqX_Avk3bUhIF8d6Zp@LpBZ7XtQG@zu(?*zy{lFW*SN7gLODA}scRgZtud$DDke$bM zT-{sB`CGCYq|*PWbbh0ubVNKV_rt3iLMCmsbn|wE`o7*JcwkLZYC=6wUe^n!=GNj% z<`rK*`MaHKh!Nv+23ICIg6E6aw3Y6f-8IUl>ZfHR4x05BvsM0R&3wvlvho~>ZH;>! zy2D#4|70hlQ5@A4p$46m)`jD39l7Bu<2&Y%u|pWr{^cp434VL4LE4poKZ`1SZ0TCF z2wo4ZU|WcxN*pJh8ad_8!K&Ve&(lIh zb*hi!Tx`R2=q|(U6QI7ur-PdJ{Kbsf7vaU1;p;)wA+~o5&pKcR&}{MV)vjzBfIxYA z6b)FsH56fq0iWN)7=1wTiH_bi13CDc(@(*hS_88^IeCW{#qiWJ34atG-o#x(Wq6 z0!v}K6nv9!gg_z-u||1fUdEBx^scQn1~UjM8I?NTu$SUQ_YB+Tx4+i^LY7A3{kKHgl1)IexuaD{!tbX8}}28$#x3k zeG^?e@S^f#K^827x9K!jao>B!0!>d8x4%A@I%U4hY*U<{RqxvvYpC%Z5 zs*VNThPetP^`Y~@AQws@T*;3u0+Zp$djV{(9_XEg%gi&!`}KN6wqtS{ z6mKjq!mCn(VaQs70raVuCmwkd+Y9KhAQguxhtu~O5G%PW5P%?R0n$}5q8b!6a9ZAx z0TvidK%RB3U}W=!pp}y-lny@R7aginelDv;pg2z!l zg|(xDw|{|&X6VrUP30(UTkr~-t;#B1&Em}{dVV97%f+9@RwYrC2djH_1i(FF2{w@K z36N=dtB|M(5ihOy!E!!f2p}{9G#V zXI0>>dr*q;mD?XgYf8D7^=6cR_r!AIeAietp!)$@H z5v^4iFMX zHha=?7gba+!Ll0(Z1P{-$kXF6n<|M~r|0=+BcF zX{9f|G`OZofP}sca<*%S3qfUzJd>8!nQEis_qpZ6s8u@}7(EQA z>%yX;g_Pit=O`=*j^27Un&?&PHkYQPO(aS<=lP-^@xCq&Lf>vxsbQzxO@tCv8rk z$<;e@7pAiR?9>`CAS&h*fu**gM%LjO)L_qaV= zAlpe_J0!%kPo8?gXAH3>;bcz81N*>;qLVqd1KS$Hu2yWAL}ty$D0sJ6`T!J8VZCfj zX`eW@@Iy{4`(htepPh|zxc|5hvR-Kr^~cw70=^?W#R{j--{#YYlEfNUTavVJLr8aO_UV*OLTlZvXcLFu#r)XT zmXx&7sp(oUziyodlgRfoGZlE&^=oZsosm#}3PAM50m*HW5goiLz3~!#7VE(fM<=4~ z4*pL8yH`ctWe<(N0(<5E@`9UUFM<)o4mH107xQV#@7Z7Kw}O$}OEp)Z!4cU9Jm*lA zv+M6$jR5i_22yU1H;>_1l~(K(vDkx9v9qKGEeyPB0_KT*VVE4PB*zj;$3)(}`VM(> z9vCE1*55?UpGK56xZs=@ig;v=%u4jy-okAH1S8t2%V( zx3L4~HuZg7aH=n0V0v)4-A@TdOcX%_YNJ5S>pi*oAUzIP4}!@imMqx-qvlVnx{*Hv zWGA_*zIoEwk*TlKjKWMVyg2@fit=Br2gn*FOhJ!hWdH#Q3XKa7Vgrt9HVkCm`_{nS z_x)Be*dl8#=$&3pTvN9hkC`0R%KE=EAE^Wf1-pcu1|PwBMn%BiTwq^^khs{qyEmW} z)+1;})1&_zr4xo6seEq^#psJJcXb8wxRes-wGMsX%{>oS{CB^8iq?aAuL2G8X^nqh zA~N6AwVRuEsx)~~TPM(m`FpXL4r1OS4D{~YOGgA9t&e|RDLi}W zq8;}d9)+%_yxH{Z12Aa0bP+|kI>M6vq^?W%?o$JsBv8SIK)zAOnO+Y#ofN1|BCood9Is(5okZwB7EX;>T ziChh!*cN)ulW8|hEks<;BWe0ee0Eb6UY(#4quA!KYm&XGTPXJcFtT**T@x&oP0;3v4eYg9m$48FCZl7c2Mpqu=#_z3D4`7`?P9zZ`M zZPLROcL0+m2E>~(;C;!$Z{9k8WLpS4A#fNE(3hzXb*bQ^{>YJ`BlJRIZ#<89gdM6i zWRA#OITUQ24m=Ti#^l=3BL3@UMRGe{2w^<2lk5_B0!z$=Nl%ecJNo!`ksf9r!#9_=R+s@Baq2@vPZCF?Ug zPfzf^C+S2lYvc|zY4vVr+II>ci~r*I3bM*(TAbCM-shh$cN;%v-l9M^$LpFfHFl+f zea`9D{F@h6#1gl;$`L)8yYUS#ym#59Cn&Yd#6xFfMV{eX2sgnIKGaKgn$T?8UK{vi zcYp6{3rlvWcO8>}PEwTOJogsCELroDmf?N`Z>X|xNByJ3+#Qu>Z%Mi9ca%I*=Y!ki z15-oJ$h6i!r(>7awD)>xcJ6#Uy|i6I0m6GuPSC^D zm9G_B>*gjme4UuHyD!emzn)oJOzn%Xy&(wL+J)_;b$IZ8+kk2iC@pp|h`0NtA{rJlfmOo7oJOnSj@recdwG<0FCAjv~#TI;1 z*NyIH?5o)M^h7^>=#fp|vk&1}g^|3wPxQN+uv0I;GE2AGxtEn_$>ngAB^nhMK1w+k zfjhDCNONn_!Kfy1qtlw|0Q*%4UvNdjHIJ&wEb7#&M3vsr^O#UZxY)g5$9S92F{+{f literal 26774 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 z zs&*`N2fUhenPPjcukttYBiD2B?`TVcaUlGyEAS3-jd9*C+zGY@m{(%Mpdz94Vm#K@ zrRF1LQR;h*l>e~z#1KcPOD}obOW6kK(u7o8 zRSGXZT)h#4t1TygF==i=3}nuLg(r#ogOy!jrZ~aGnDBu|(au@OTi)@yg=>?7nwQZ= zNAAuB^!W5W4}cyUoK%&&v=j)baYRA-6W-t)BrPJ3B2MA#0p zT<-uCqQn?uX{CSb4WP8hn%Y^$_A|U?-_8IG9s{Vznum*UWv^wV znBJU_jfpQ+&H?aJ94fl-^CJ&P7JivS#)+5clGwYc8|1{$*R0UQjA)k=3%2UqjDO4U z*vC|%OQ?+)G5B(Gg(LJhHui@1tPol@XC08us&*YCjE*ujP6mWxSe?#g+D9HE8RUhgF$sV24$=Ly8dqY; zqifpH$l6Ek#dG!7F4qrsD+D+8O)d85x+(^J$wc<_55??)dEJtk$7-*|05x6l-_zMb zc&}|u=IK#i?fcZLYX6S|$LsCXzEhm}0QzYt*j;IC39JnqD97Q|IfC3cTDq{*zj~Ci znm)It&N;V&d~;t8yault7yz+r*K9g!-j|?ymt3)``{laEO*nmL9x!;c=UIz)$*-;g{MJ@;w@wTqT1rWPX2NLl?y5pj@ zR?zeyMq0Ci)k%XP95fWgctN`U#RF-r*z=)yUTfkw;y4N`t8hJGM~HT z92|57a=Z1nTk;{@RD_v);%#$mfw5i;KQ2q9d2Zse>BXKT>`r7)wD-@3=w7pL9{z*V zHqhs)eEX755I3W?!)kfkGIs5`@YhzWhV)V)8)dEiY<^gsBm6v$D2z`Z9RR8@rW=Z9 z2bor4XdftIAZGaquxIC>TQPF)8Gfj?{!p{`UY%c0Zs%pnT*gibp@X;zh3Zl59qCz5 zMG}w#uRCuF#rWt?ZpcUz6o@9!gi7oy?r}8ZO*kD{*>-z+bZpnDCn-g0h1EOmU|2_X zoP>0O2^BmT+7auwXwa|D2qQ7>%h;o*#cod<{oV0R;lf}1^L%*5r+1LA_SPpSKARW5 zH+0W3ah}=hKYM%mZ1`m`Eosu@eu5w*4+qv&*h3tXC3j$mG8;%$|EMHR7}t3Kl(5%%%1xODW90OQ}Y=7mlILVpgOG%kQ zwq+p&;`T7tV0g^NopfMXYse`6gEzV$91;fK-XA3aaPu@YNAYu%zR$Kp;nrQEM5L2bWBqI@2B&463Rm}gC_l-9?U}iG-RZjE51=8c)=c6-$9Qn z=t>6?0l)#YjFd21MFAe_`3=a!P?Sn|e=@GuAu-qWew@N+u>No7 zovb-m{BfiLhHo-r5ay_p`|CKG5uR~Od*b{V)UD`dtPW7L=5ih)b1^c#=@ErMA1wx) z9F9$Upg|WvxOIIBfAVsWYc9-GM6Bf_{mwC zkc~@2-FS^IS0nk;oD(Ca%Lj{gyTz$dJ&fl|njfeTN(BtLX%|sFSao@pkN#A|9E6%^ zDqxj_@rph_H6PL4)o9-W#42+`*4l3*qHs@A|=nK)8XK|t+wOB<52P~wO z2;r6eSjBf#@)_tySQ0<`t3-Mxk&oVx{`tPU$t(A4H~lT=IT+jbm!fmlFJWbi87 zu1Dnx0Jx-dCu^AT9AUa$DMhBpnffWQLo%{b1Yf0I-$_}(Gu&I2-UHrnn{54YkX2Zm zg6_o~!TP?tq7OaN?&?4n6!M?x$qRjS=5OCYS>*E<#@mUFqd`9eI)K-KX-~Lmy8Miy zJ-n(}4=DKACUFA7bsV0bUyDNUsz$RR_Y9$s6;ZiMF)?1e75!<>ZW6c-9@esL4Dwl` zdu7@{c;4W+O4mhhmYtxJuIe-oEpO|!DJLR^n`8R9b%5>2L(^t&r}Qx^BWj;0AQ!;<@&+9Kw&ljj~60G&TYz-a~WV2Bso2b-))$jo%t~)EP3c%}l1G{A?l$ zkl0!Myp|nQA^bK(D19m8iw+8%%5BZ2(-@ZMK5sbCvKdbg;HtMd1%+GH{K3N9 zHuNx(Y~Gu`KAV&!pa76(j?I>Ul1e=i%X7c>{&_LeFNdX>5{qA5ma(*0DQX{c%X#>j zXfN}l>GUB%bunXl+>MBj+$?gZz2f|BQMjxn zu1aK3Y}fWEB*+jJQcX$FBl!wsC3pdXciZf^e)rKS^;)xnx38QYIWtEH1Ou@*s9QV+ zEP$N{tPbzn=ZQU?ui<Ou!lH~KoLAzXQKWhdi0lJLg%;U!!3-kXCzdKKoVTKqgTW;1d> zoT=%mk{DW9Tu_hZ+|f0JXeM^!cy`O`pd(f21c+1DT>S(=ojF1F1FUf9;^AXQE?#1f zzEdI8nm7AidPX(=^{ThzY9%7S;as77w>#tj@P@f`c{3{6)3Fww1eol3#hrZ~g>Onl z*JoGspDqYweinFJ{o<+CeX8+W^5t%6h59Y%LU~OUS!~Qd^1BiZgxCr!&0$>!{+1L$ zL27C*p3(iIzB8>3$uKa!FAFjb-d|jW=oMRe?ZUJgaGFpIiXsyf& z8uAt6u9R%$u$M@gf5@3qpt5h?{Pd38;+yg)8G@+;slP4~xP)x6&6IKjjTN3o#PdF2 zFsddVA|&Ej3~Oi>`1k}=dpEQT;+u@R4q|*KTqKxw4^Q^2oJm4+GOn_$=19U{w4L;! zd$67ibMdH)Ji!H&QDh#VrSTs&Y9v+1p)(V=`q|$xiXx`mEazwx25YNi-Kuyc=1BYp zfFJgL{Sbv@ZIWPm{xKBX>kZq>y7AC2gL50T{4&in`P}dkSZlQ{yooN3Va0=+_sm7=gb3X5>y)%=_Ys3>S0Epy%I|xh4goqM8dMH;lJ&!l^$bLztDhAY+fC{*XNI`Ud zzf&fdrrdqkiNiDaE7y8z2g3o9|D%rxf7{U{gs+s8Ei%J!lpX-WF0(yS?6Vj;yIGu2 zt(hMu&a@jBq%T&za7Bz~IIyRB%^z9NmK_SW0A)!JnKnVX?O`W$AyoonVFT5X>5JZW)(Z>xkGGeR^ z^ztB1_2@-Ms>~-v3=4QZZE`{-_go)G$I0tkspW+(rrb1vhhoC-I0#r=>Fi-E80OX)tYY)ZKbpTnRuEn z#kda!;VxIZy(=)hQG$t~$efj&R%7F&s(X=(Bd$?Cv~XoESeo7gKKoIhBNM zDZ!Jhr8k0}I57jXy#IAdx?w@_up|FO2S-5)3+lV}u(h$211wh3Jgh`WtRH-L6}nj< z&I~SCSb8^=@~KO~c8G9cU8#*~JYxW@YezSBH*$5mnyUQ~L$^mScBoZLbS*BuSn8X~ zc2^-OlQ_-+?o<9=Q(^(@eph}YS+${U&jv1!Gc~8DvLk=nh!s2SfbTEwd)LtQAovT8 zkKOXv!OFgm>`AsQst8wCCb(%}IQ>_1humJTmp3fuANhc1|5!4KdD>{7F!`ig`+lUm zGASbc@;#ob@T;Xsm_WWwa-!Yu^Qp(~) z-RgE&=~#1fH)^wml)=Xi0}^_{qR}^>8npW(D8^^pVk;vZp<_#4t^tt+2)$n;M~J4^z*R#jE~f>79j1FR6VElRQTSQ7j+SPq0ts% zbRCBR6Vt!tcsOSJ2zO!L?A&7dJ9$QsK4HVYKH+wHX1>WqY{)slb-tyu#ixG4@0i^J zV^~#XBJ;;mdx?M=b@BW3xFoE#tXC>P+Gvs*7p-Hr9{5eVoOv&WY)YJAKUjPYR>dc7 z>t<2+$e8UU=d>Ms+BBnT$&j%8ad(ef$#5dEOH(bFbMnIp+V0k2j%u2F+L36k?;WCN zS}k8`UH7+3>o$jMT^~m`-tXOVgAd*F>Y_fBV4Xxd@pJD4o*jN3xQ={&p_?9@?>XXo zK#36M>W9Y%oXlo==dU^=5S{K5y%dzI^N3z0un{c&oO*zVRk_ z6ijNlPE&W#Uud|tCBNUNwP@r8^%g3r03{>L+ZW*aM~-_z`_gPpa^%o>=*B|=ZIZQp zbp}v?+s!y}0^fdDJX<)io)5t9KX+PUIAkU;)Onf84Q%K%+qc2oE~~h;eGhc5^P=%0 zbc7iu`V$q{xg`cxFmZyI@)#^dgkoHQaYn}@cZoCGV;vztoPH6AL0;GnOB3vg`@Uge zW3Z;!oG3BuD)wuWHaG*A^YmVJ&y9f|#b0ymw~CZKBGz41Q+B<=Ezlz2`pzhC*&d8IyS zgT%NK@F-r}R;C=t5LUo2OG`mInX{B~d3{^JW!p{9lhX!mH((5X==wNIE==YhhkkI3 z{#$=ZKDM{H?|NgSH41{_w~xsemf_v75|)o5b0S3!;5`4P`ENznS_kfr60&!;*ST-G zzI}p^UApO?)P09{#JSr2H^ikvIn!Minz~y`mtm}w+CT5c`I(tb& zkdJL0|6#j$ulrz8yhGX{9KJ9In7i-)=M?|+8+FV*TgwtBj5D{G{zBcx=61bsx3@1) zwn$NqKT|ZM=y%-k$#1x`LC7VbO5IeE8{PcSONQC0U{h04&y0Pul_)3A{Ai(b2V1xA z`FBZh=(F5lv?U|wg^ok-eZkr`S6ilK_3ytnDQ^W)_$jKbp|NRr7udw-iVH~mu5SZ> zy^U^uzqqk!^uX{3#Z5cFO9vWTLKO1f@c_UBoX28!6TvR}+ri$sh<`WNrf(qf@1StK z5CG!{5GQnSjQ>r+xb+`(#3ty58o#Qh)+WO74ef&HCi=q-9RTkpi~olJ5|jO)Aep8e zoNcZ=3#Q`!B*vNbPy3Kexeum&76N3T#Ql64s9v>>RyUkT`jk|oV|cC;i7c68h}jwk597XdYVQw@vX>i^my?py4OQ&5){w(yaYt0dTPHgDxE z6D0Xv6WCzIZc>-Ug8w^wVCHRi%r5M9-nV z=*p7h55=Khbdk(Xr}HYs^TU-~qx&>3_0Q{WrKIq2VNU$rzu|B=)bd&Q(JEO)IKTZN zQWLrEsS?}WaiLbSqIR@_7!8%^e34q(_m1e%^J_`}^X7D{%qyKsk18XUiYxv*(8D)@ zs1Bba!y2&yP`WlGKk341Ung$n^$ZErjLrd45 z)1Nga1fToT<_noF@sb|)u3BotpImQB2i)<`-+qS#=!C844Md!<4XoE}ufKq2D?X*t zJ$?t@P0jBe(YSb6p0MtrH$|?=?Ef%XBH$k$AucTE`oz6q^>O`8EyG0g#p^>epWD41 zhdew+5^o;GZhF@RJwA2)f5kLzzkeYx;C-&wl8g(Fla{-mAjGqUHt_RgV$ z*FZoIr(JC$uf@UMvbK9Ip>fIX@}2=FAqKn>W5bFs<+HF#E+QnF_hhkF_{j8rW7#E` zJld&)JBssg?#zsPzM3)FGtj63M@Fd@>*`HTT@lJMVIO)_1YQqd3`Wh13&0Yn$B-CTH%}uR8rnSUQ|+SzI(}!+3Kf zz^21K*xoedfk~=E`s6ma<1srgsm61CG^P5*t?~E7b8h`!QU+y4O{ic_vJ(K(NC66SFZhQG=J>Thj4zkrBg+vid&Nu#Y z#O8R5bj@k?RCBA)3=9wJU^Z*zltFp*s673FNjB!uq~((cSY9?g8isU!WO(%=)1V8-ulKY!dorI`T?&p z&BR7f5GyIv~ZG-jeP#r?0DMnB8^W=Z?d(O`=r$U^q2Q+ zI&3}PpI81BR?dI!-|;b!FQ;uzBInjqdnliq1digu@>SO3Z&jB!z7WyeBjFahznv)M z*g48)Gd*58B?15}(=Lyz2X$ZXa$H8^7uXM$nn~p+f|`XzfCKUW$Iq7a?JI3rI-a3? QsvCQm8550)4bI;9A6{w&K>z>% diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index 8427f0837..ddd82a771 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":5525,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":5526,"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 index 024f3dc8a4787619968d947089c99955e99540d0..f1668762b546fba3cc99f941d745d5e451378efd 100644 GIT binary patch literal 13830 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 diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index 526e9193d..afdc48e18 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6716,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6389,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6396,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":8459,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6403,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":8975,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":8136,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":6080,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6853,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":4797,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6717,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6390,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6397,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":8460,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6404,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":8976,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":8137,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":6081,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6854,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4798,"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 index 673b69c05e3a28bd747aca7167794ebd825e6229..4c634d964905965ba38be075a32ed99c8d5a46d3 100644 GIT binary patch literal 14779 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 E2VIVba{vGU literal 14440 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 AMF0Q* diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index 53bf29af4..d77ee4648 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":2216,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":252,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2260,"get_count":3,"put_bytes":294,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2302,"get_count":3,"put_bytes":335,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2343,"get_count":3,"put_bytes":376,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2384,"get_count":3,"put_bytes":417,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2425,"get_count":3,"put_bytes":529,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2448,"get_count":3,"put_bytes":481,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2489,"get_count":3,"put_bytes":592,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":545,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2553,"get_count":3,"put_bytes":586,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2594,"get_count":3,"put_bytes":627,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2635,"get_count":3,"put_bytes":668,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2676,"get_count":3,"put_bytes":709,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2717,"get_count":3,"put_bytes":821,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2740,"get_count":3,"put_bytes":844,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2763,"get_count":3,"put_bytes":867,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2786,"get_count":3,"put_bytes":819,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2827,"get_count":3,"put_bytes":860,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2868,"get_count":3,"put_bytes":971,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2893,"get_count":3,"put_bytes":926,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2934,"get_count":3,"put_bytes":967,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2975,"get_count":3,"put_bytes":1008,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":3016,"get_count":3,"put_bytes":1120,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":3039,"get_count":3,"put_bytes":1141,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":3062,"get_count":3,"put_bytes":1165,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":3085,"get_count":3,"put_bytes":1187,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":3108,"get_count":3,"put_bytes":1211,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":3131,"get_count":3,"put_bytes":1164,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":3172,"get_count":3,"put_bytes":1205,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":3213,"get_count":3,"put_bytes":1246,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":3254,"get_count":3,"put_bytes":1287,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":3295,"get_count":3,"put_bytes":1328,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":3336,"get_count":3,"put_bytes":1369,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":3377,"get_count":3,"put_bytes":1481,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":3400,"get_count":3,"put_bytes":1504,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":3423,"get_count":3,"put_bytes":1456,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":3464,"get_count":3,"put_bytes":1568,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":3574,"get_count":4,"put_bytes":1609,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":3487,"get_count":3,"put_bytes":1593,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":3601,"get_count":4,"put_bytes":1634,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":3599,"get_count":4,"put_bytes":1634,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":3512,"get_count":3,"put_bytes":1615,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":3624,"get_count":4,"put_bytes":1657,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":3535,"get_count":3,"put_bytes":1569,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":3707,"get_count":4,"put_bytes":1740,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":3748,"get_count":4,"put_bytes":1781,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":3663,"get_count":4,"put_bytes":1677,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":3575,"get_count":3,"put_bytes":1608,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3616,"get_count":3,"put_bytes":1719,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":3639,"get_count":3,"put_bytes":1672,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3769,"get_count":4,"put_bytes":1802,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":3680,"get_count":3,"put_bytes":1713,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3851,"get_count":4,"put_bytes":1884,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":3721,"get_count":3,"put_bytes":1824,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":3832,"get_count":4,"put_bytes":1865,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":3744,"get_count":3,"put_bytes":1777,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3785,"get_count":3,"put_bytes":1818,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3915,"get_count":4,"put_bytes":1948,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":3915,"get_count":4,"put_bytes":1948,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":3826,"get_count":3,"put_bytes":1859,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":4079,"get_count":4,"put_bytes":2183,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":3867,"get_count":3,"put_bytes":1900,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3997,"get_count":4,"put_bytes":2010,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":4018,"get_count":4,"put_bytes":2051,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":3994,"get_count":4,"put_bytes":2027,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":4077,"get_count":4,"put_bytes":2110,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":4016,"get_count":4,"put_bytes":2049,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":3906,"get_count":3,"put_bytes":2009,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":3929,"get_count":3,"put_bytes":2033,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":4040,"get_count":4,"put_bytes":2073,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":3952,"get_count":3,"put_bytes":1985,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":4123,"get_count":4,"put_bytes":2156,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":3993,"get_count":3,"put_bytes":2097,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":4104,"get_count":4,"put_bytes":2137,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":4146,"get_count":4,"put_bytes":2179,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":4016,"get_count":3,"put_bytes":2120,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":4128,"get_count":4,"put_bytes":2161,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":4039,"get_count":3,"put_bytes":2141,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":4062,"get_count":3,"put_bytes":2166,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":4214,"get_count":4,"put_bytes":2247,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":4172,"get_count":4,"put_bytes":2205,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":4215,"get_count":4,"put_bytes":2316,"put_count":4}} -{"i":87,"series":1,"stats":{"get_bytes":4320,"get_count":4,"put_bytes":2424,"put_count":4}} -{"i":88,"series":1,"stats":{"get_bytes":4085,"get_count":3,"put_bytes":2187,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":4236,"get_count":4,"put_bytes":2271,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":4238,"get_count":4,"put_bytes":2271,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":4237,"get_count":4,"put_bytes":2270,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":4279,"get_count":4,"put_bytes":2312,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":4197,"get_count":4,"put_bytes":2230,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":4320,"get_count":4,"put_bytes":2353,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":4108,"get_count":3,"put_bytes":2212,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2252,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":4131,"get_count":3,"put_bytes":2232,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":4242,"get_count":4,"put_bytes":2275,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":4283,"get_count":4,"put_bytes":2316,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":4283,"get_count":4,"put_bytes":2317,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":4242,"get_count":4,"put_bytes":2275,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":4154,"get_count":3,"put_bytes":2257,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":4389,"get_count":4,"put_bytes":2490,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2299,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":4389,"get_count":4,"put_bytes":2422,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2299,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":4263,"get_count":4,"put_bytes":2299,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":4348,"get_count":4,"put_bytes":2381,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":4306,"get_count":4,"put_bytes":2339,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":4177,"get_count":3,"put_bytes":2281,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":4329,"get_count":4,"put_bytes":2362,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":4370,"get_count":4,"put_bytes":2403,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":4412,"get_count":4,"put_bytes":2445,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":4329,"get_count":4,"put_bytes":2431,"put_count":4}} -{"i":28,"series":2,"stats":{"get_bytes":4453,"get_count":4,"put_bytes":2486,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":4200,"get_count":3,"put_bytes":2304,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":4393,"get_count":4,"put_bytes":2426,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2467,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2498,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":4393,"get_count":4,"put_bytes":2426,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":4375,"get_count":4,"put_bytes":2408,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2496,"put_count":4}} -{"i":43,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":4310,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4374,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2496,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":4311,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2456,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":4434,"get_count":4,"put_bytes":2538,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2413,"put_count":4}} -{"i":58,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} -{"i":60,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} -{"i":63,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2620,"put_count":4}} -{"i":64,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2539,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4416,"get_count":4,"put_bytes":2519,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2573,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4476,"get_count":4,"put_bytes":2509,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4517,"get_count":4,"put_bytes":2550,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4223,"get_count":3,"put_bytes":2327,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2573,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4480,"get_count":4,"put_bytes":2513,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4563,"get_count":4,"put_bytes":2596,"put_count":3}} -{"i":81,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":83,"series":2,"stats":{"get_bytes":4540,"get_count":4,"put_bytes":2643,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} -{"i":85,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":86,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} -{"i":87,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} -{"i":88,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} -{"i":89,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":90,"series":2,"stats":{"get_bytes":4462,"get_count":4,"put_bytes":2496,"put_count":3}} -{"i":91,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":92,"series":2,"stats":{"get_bytes":4522,"get_count":4,"put_bytes":2555,"put_count":3}} -{"i":93,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":94,"series":2,"stats":{"get_bytes":4499,"get_count":4,"put_bytes":2532,"put_count":3}} -{"i":95,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":96,"series":2,"stats":{"get_bytes":4246,"get_count":3,"put_bytes":2348,"put_count":3}} -{"i":97,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} -{"i":98,"series":2,"stats":{"get_bytes":4545,"get_count":4,"put_bytes":2578,"put_count":3}} -{"i":99,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2432,"put_count":3}} +{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":2217,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":253,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2261,"get_count":3,"put_bytes":295,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2303,"get_count":3,"put_bytes":336,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2344,"get_count":3,"put_bytes":377,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2385,"get_count":3,"put_bytes":418,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2426,"get_count":3,"put_bytes":530,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2449,"get_count":3,"put_bytes":482,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2490,"get_count":3,"put_bytes":593,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2513,"get_count":3,"put_bytes":546,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2554,"get_count":3,"put_bytes":587,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2595,"get_count":3,"put_bytes":628,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2636,"get_count":3,"put_bytes":669,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2677,"get_count":3,"put_bytes":710,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2718,"get_count":3,"put_bytes":822,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2741,"get_count":3,"put_bytes":845,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2764,"get_count":3,"put_bytes":868,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2787,"get_count":3,"put_bytes":820,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2828,"get_count":3,"put_bytes":861,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2869,"get_count":3,"put_bytes":972,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2894,"get_count":3,"put_bytes":927,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2935,"get_count":3,"put_bytes":968,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2976,"get_count":3,"put_bytes":1009,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":3017,"get_count":3,"put_bytes":1121,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":3040,"get_count":3,"put_bytes":1142,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":3063,"get_count":3,"put_bytes":1166,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":3086,"get_count":3,"put_bytes":1188,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":3109,"get_count":3,"put_bytes":1212,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":3132,"get_count":3,"put_bytes":1165,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":3173,"get_count":3,"put_bytes":1206,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":3214,"get_count":3,"put_bytes":1247,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3255,"get_count":3,"put_bytes":1288,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3296,"get_count":3,"put_bytes":1329,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3337,"get_count":3,"put_bytes":1370,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3378,"get_count":3,"put_bytes":1482,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":3401,"get_count":3,"put_bytes":1505,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":3424,"get_count":3,"put_bytes":1457,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3465,"get_count":3,"put_bytes":1569,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":3575,"get_count":4,"put_bytes":1610,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":3488,"get_count":3,"put_bytes":1594,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":3602,"get_count":4,"put_bytes":1635,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":3600,"get_count":4,"put_bytes":1635,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":3513,"get_count":3,"put_bytes":1616,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":3625,"get_count":4,"put_bytes":1658,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":3536,"get_count":3,"put_bytes":1570,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3708,"get_count":4,"put_bytes":1741,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":3664,"get_count":4,"put_bytes":1678,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":3576,"get_count":3,"put_bytes":1609,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3617,"get_count":3,"put_bytes":1720,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":3640,"get_count":3,"put_bytes":1673,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3770,"get_count":4,"put_bytes":1803,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":3681,"get_count":3,"put_bytes":1714,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3852,"get_count":4,"put_bytes":1885,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":3722,"get_count":3,"put_bytes":1825,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":3833,"get_count":4,"put_bytes":1866,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":3745,"get_count":3,"put_bytes":1778,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3786,"get_count":3,"put_bytes":1819,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3916,"get_count":4,"put_bytes":1949,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":3916,"get_count":4,"put_bytes":1949,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":3827,"get_count":3,"put_bytes":1860,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":4080,"get_count":4,"put_bytes":2184,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":3868,"get_count":3,"put_bytes":1901,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3998,"get_count":4,"put_bytes":2011,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":4019,"get_count":4,"put_bytes":2052,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":3996,"get_count":4,"put_bytes":2029,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":4078,"get_count":4,"put_bytes":2111,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":3996,"get_count":4,"put_bytes":2029,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":4017,"get_count":4,"put_bytes":2050,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":3907,"get_count":3,"put_bytes":2010,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":3930,"get_count":3,"put_bytes":2034,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":4041,"get_count":4,"put_bytes":2074,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":3953,"get_count":3,"put_bytes":1986,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":4124,"get_count":4,"put_bytes":2157,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":3994,"get_count":3,"put_bytes":2098,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":4105,"get_count":4,"put_bytes":2138,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":4147,"get_count":4,"put_bytes":2180,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":4147,"get_count":4,"put_bytes":2180,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":4147,"get_count":4,"put_bytes":2180,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":4017,"get_count":3,"put_bytes":2121,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2162,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":4040,"get_count":3,"put_bytes":2142,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":4063,"get_count":3,"put_bytes":2167,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":4215,"get_count":4,"put_bytes":2248,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":4173,"get_count":4,"put_bytes":2206,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":4216,"get_count":4,"put_bytes":2317,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":4321,"get_count":4,"put_bytes":2425,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":4086,"get_count":3,"put_bytes":2188,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":4237,"get_count":4,"put_bytes":2272,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":4280,"get_count":4,"put_bytes":2313,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":4239,"get_count":4,"put_bytes":2272,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":4238,"get_count":4,"put_bytes":2271,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":4280,"get_count":4,"put_bytes":2313,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":4280,"get_count":4,"put_bytes":2313,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":4198,"get_count":4,"put_bytes":2231,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":4321,"get_count":4,"put_bytes":2354,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":4109,"get_count":3,"put_bytes":2213,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":4220,"get_count":4,"put_bytes":2253,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":4132,"get_count":3,"put_bytes":2233,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":4308,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":4243,"get_count":4,"put_bytes":2276,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2317,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":4284,"get_count":4,"put_bytes":2318,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":4243,"get_count":4,"put_bytes":2276,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":4155,"get_count":3,"put_bytes":2258,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":4331,"get_count":4,"put_bytes":2364,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":4308,"get_count":4,"put_bytes":2341,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4390,"get_count":4,"put_bytes":2491,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":4308,"get_count":4,"put_bytes":2341,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4267,"get_count":4,"put_bytes":2300,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4390,"get_count":4,"put_bytes":2423,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4331,"get_count":4,"put_bytes":2364,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4267,"get_count":4,"put_bytes":2300,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4264,"get_count":4,"put_bytes":2300,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4349,"get_count":4,"put_bytes":2382,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4178,"get_count":3,"put_bytes":2282,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4331,"get_count":4,"put_bytes":2364,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4372,"get_count":4,"put_bytes":2405,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2405,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2405,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2404,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4331,"get_count":4,"put_bytes":2364,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4413,"get_count":4,"put_bytes":2446,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2432,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":4454,"get_count":4,"put_bytes":2487,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4201,"get_count":3,"put_bytes":2305,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4313,"get_count":4,"put_bytes":2346,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2428,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4313,"get_count":4,"put_bytes":2346,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2499,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2497,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":4477,"get_count":4,"put_bytes":2510,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4311,"get_count":4,"put_bytes":2346,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4459,"get_count":4,"put_bytes":2492,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4477,"get_count":4,"put_bytes":2510,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4375,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2428,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2497,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2346,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4313,"get_count":4,"put_bytes":2346,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4354,"get_count":4,"put_bytes":2457,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2539,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":4477,"get_count":4,"put_bytes":2510,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4313,"get_count":4,"put_bytes":2414,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":4523,"get_count":4,"put_bytes":2556,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4518,"get_count":4,"put_bytes":2621,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":4459,"get_count":4,"put_bytes":2492,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4477,"get_count":4,"put_bytes":2510,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4518,"get_count":4,"put_bytes":2621,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":4518,"get_count":4,"put_bytes":2621,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2540,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2520,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":4459,"get_count":4,"put_bytes":2492,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4541,"get_count":4,"put_bytes":2574,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4477,"get_count":4,"put_bytes":2510,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4395,"get_count":4,"put_bytes":2428,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4436,"get_count":4,"put_bytes":2469,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4518,"get_count":4,"put_bytes":2551,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4354,"get_count":4,"put_bytes":2387,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4224,"get_count":3,"put_bytes":2328,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4541,"get_count":4,"put_bytes":2574,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4482,"get_count":4,"put_bytes":2515,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4564,"get_count":4,"put_bytes":2597,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":4541,"get_count":4,"put_bytes":2644,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4523,"get_count":4,"put_bytes":2556,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":4482,"get_count":4,"put_bytes":2515,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":4523,"get_count":4,"put_bytes":2556,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":4523,"get_count":4,"put_bytes":2556,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":4336,"get_count":4,"put_bytes":2369,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":4463,"get_count":4,"put_bytes":2497,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":4523,"get_count":4,"put_bytes":2556,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":4377,"get_count":4,"put_bytes":2410,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":4500,"get_count":4,"put_bytes":2533,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":4418,"get_count":4,"put_bytes":2451,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":4247,"get_count":3,"put_bytes":2349,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":4482,"get_count":4,"put_bytes":2515,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":4546,"get_count":4,"put_bytes":2579,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":4400,"get_count":4,"put_bytes":2433,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png index 8db808d1100d382069533e991b546c69c9d825da..9ed9b6df6df7415a0a329921b555cd061c95e5e5 100644 GIT binary patch literal 19949 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 literal 19532 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 z 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 Ku`+{mxBmf6S{X|K literal 22146 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 diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index 128049dd7..9945e2665 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6895,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6916,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6963,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6854,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6957,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":6813,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6915,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6775,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6607,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":6813,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":7034,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":7045,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6813,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6811,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":6835,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6818,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":6770,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6893,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6899,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6764,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6852,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6911,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6998,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":6824,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6747,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6752,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6876,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6958,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":6689,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":6537,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":6706,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":6624,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":6754,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":6788,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":6783,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":6906,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":6624,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":6824,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":7021,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":6836,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":6793,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":6911,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":6806,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":6816,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":6618,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6770,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":6731,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6896,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6917,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6964,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6855,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6958,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":6814,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6916,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6776,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6608,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6579,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":6814,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":7035,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":7046,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6814,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6812,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":6836,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6819,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6743,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":6771,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6894,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6853,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6912,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6999,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":6825,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6830,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6748,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6753,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6877,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6959,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":6690,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":6538,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":6707,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":6755,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":6789,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":6907,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":6825,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":6620,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":7022,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":6901,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":6579,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":6837,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":6830,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":6794,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":6901,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":6743,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":6912,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":6817,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":6561,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6771,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":6732,"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 index cc7c980803ace27edb267c1f72c9d5cf5b37e804..40530f0ec7ed29ff08bfc2508a81a248ac17dd27 100644 GIT binary patch literal 17373 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 Sp41-)P(5v9ts>1Ek^dV8;huc} literal 16915 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⋙Vnev`LO6I+Zi4u!*6=sHxc8^|CzEwa6TB5V`47+4MU~)Qz0iw5 z*HU3TV}>J!`9547c)JJopoVL5;acPu{FTeSG=QMKKnt(nm8~B~1GmMm&XK}w@LA!B z4&FA*5wh#FyS>SiJdZ}qdm6@#gO~>k!_!j9%v0v75mUczU_V$Gnc4`fW=xAr*9}@P zKHMy=mDpbo_+DDH#jCwWd%iA4o6XEL%YHVVQ=p-xSaTM4X(7ScFeu_y#huk(23+;i zuUoy!2_rs1j!DN(z2?BGoy?$?XLm$=EwV-0-|%OP`;O>*|Mgbz+|i|$_~*S^BnWFy zfnodVb1(q325L&o`!gwE?l{91qgPk|%VzSscwB3GkWn2xpe8yhYUN(%O$6%|i>=$e zD&UPs|Ck+8)wvOqSaa7qyQ?SIf$*Ea0FyFdM&kN+Nh*W}BZU7nV z&|PclSN5ZhU&HYWUyXsPL90E9`xy0|eWx~)wIvY=WA~~HZKbuX>D51 zpAZ-(bub}8>?%@#QU{56we`p1qjBFXKMh{TH;#JQ>M52(M@gKFZNJyCD;3KQe1KI^q_6IW$en>q^~paKkF~}8 zcnjMx6%QZEO<;;>O$)}XzPE~AEkS0Rlp=q)UXk_D#Z*8KVUJblBzFE!TBqV(`E|eg ziPu?oUNmeVCgUG$=#9T|{PB=`ljNS5OGP0GCI~eC=3|uIdpCXi@pf-pvBna^aAUz{ zYCG4|(H)feo<*XL+*Bz%Qp%2VNAh_76A_~;fcqlF5r$L!drUj?lR)hb6mRA*btWiPx32i4e_ffHbxcZ z6c|JEqAjx&X0t-)J+mNvm+bJJF}2X*r50}@s*USKQK@wjT~8^xUY!mkcj`!d{9rO! zW8#`HbgIe5*wp6^et(!>u&J}yMt@j~j4{~T75PvkmMmy&LC{yj6>J%qR}}oVR$Z+G zr>^*to(m2Ynr(fxn4*v}cs+<4TYfDfaa+H=j(^-8cxrDS-Eu(5B4oPBPtDjAJQJPT z(?+J19$*u7VAzvSRAo1Kxp8`2!xct&&TXHLjK6E$WsV zFhfu9yI;3RSroxs=8mZ66(kWZV|k|4(dQ~v_`ZW))W(lR``%C-Y}eD`yI+sUZ1auD zS7WRH@0${O+?gRRG}$cyw=oW;7JT-n?c4YHbCQyuOk06|=O|dFhrB%#aYqhGh8i}@ zZ#Gu@9>KA}>dyHG`n5rCZ5%pLk5SV^l24ElBR$^u&1CDGZ+R7|$@UJ^X33WaW%Two z10?xs##KIrHEtB%s0+Om^JJ!`+6=f>*0ue(`M`jGivev2h|!wa+HJkF;jJ83!SRB+ z?}ygq=<6blM|$jo03`^tBFn|^#bhZioH=!UTl@`*Q40RY;`-f|%@k|i`xHVoO})WiEWM z14n-^y|ZcheK;ob$`?nupC`uD*SMD6{<GBMFTq zpnP!Y*W1layU{8!rTNC+(redl1sIIS;a+yuJso3Kx~PhzGpjnmUz-%0u!|KCRSrkI zZ+Be6prU-*w5@2-I@_NwkO*CAMtRSD#lO`QYfVUEv$NS|Lwp&~SPmIrOhvwMrW)YB ztlwEDTf@qlqbBVzHcjaH)qg--Q2N$oCodbohKpdX1_+#pzaaw}8 z_rV2NCD96c9<20(21z0U$6VlG5!<=;qGkNaKD=* z%V&RNViqiD2%fiq4L526hMJM^_3?i4qw~4`LvUNMxn^2|?dI=Vs&ce5(wlBT3VYIf z10OeUxRs}wKwKNXv-g|Dxh(GNyBU4F+X^e5cEM0$xGkPoEB>pzXxBAPZ0c(_+TA-c z>~Qe!ABoA+f%gSJX5Iq)^4E8IkFD8b*TCDqa-EchjoH@{@z_Go0JZ7+w|CNP<5A z@gxW(oGd-bGSe+Uxw{30DhYmFhA+(shjv^IsqqG1jrs&%hy~C)_L9=KO_L9MMedX6 z)Q{rog{%+<K7PllA|4V~G=(nwrTZFHl;=Z5d;9;(1&HB|s3 zLzE_z_4?CxgTTaP{H8t$cq~G)oI23M4xdC)6V^GovD%Y8_IQu6O+BMMPGM}tNqOu@ z_;p1Ow#J0{2!$3ykRn+PpwcV|muip?$5A@mlBE;!g)0YNbDP{c^2?!rh70fJzGLKlZKDmStXk ziF4x?)!`~Qahxp_?Aw0u_k34`qRi)=c0yJK3A4i+;UqGo=9&{Bb<*2csv0*aX-Cij zjS~aA@|}wkHYs)NMBv12Qyxu|r^ckJFYL6r;-lbu&ph)o$jYe_|70K7y1;sTaI!f%sm3RaUF5{q zFz5mnmOOhTUG6*=841s}3q$K0M~-bkRs}B=Ps|z zc?-~r-MNN8kWHd7yV#8!rntAU=(T>+=N^)$wFfWf^9V^40cr0g#9frjiGc1%*nA)T zQF65#YyE8_{P@c6;l~?_a%~;1c*`TR+LmA4deX!wdp3_G1*KZ?iL|)=>+{&ddgtq) zhR#I!cBukeu(VmH)VB*-rzv5|QZiK;xEN-{Z2H0E!Y{bT1O1Q3@F2`F==GpTWqJ9j z**aEjje!uOx99(E{kP(>Qkxf?3tKDjP1Abfw7b^KrXD&N**;yfKF@)wP;~jdp-?YF zhFG*>CCQvE6O%{H*<%~4i+#wtln2$WF9b+74ssvqrx<6X2OPf;hA*I={9GCh6#4l% zm3obiFjF-?$*u6aA?i84l(O0ikQYC`dQoP~u5UyWC{}9-=i!CB0K~?dO~UZlOyia% z6`+dxJ(aEjz;TFM2|dnMg4Zr|Z2j6&TVrAj0-n61h8|@KQhtqgO|dBm|8hp;0}r-u zC3l?z+G%^=c~&m(Mb;uGJ{!2olrjs4!Z|N!U63LTifuTB74i1lyQ=b~cT&rba)8ye zg4RP371XynzTiu(ry%9WH8PEy`#z1uJ!GCVW}u)`$QG0dT5UN`l1YBD zb<;(-gZIO%2*?liAg&~R=XuArz2yh4<`=9m5K77I=*^T%jN_6fR`grWw+}GYy~Z)d z-Sl(6#{(Z>cArmq@+Fv-|C-M)5z}4isH0anpTL{vz_HxX{jDpKyKd*fod8=nw(m#= zq5Wqx>gJf|pIAF9B+-a_G_IGo@%Xm{QYw0PffP~0hY#zz1+@Hl7A>V1F0uW5?mWnW zO?r}w3zVz$B|(fZ$qHk+>NhKKD>L7cDc05Q3=UbG>a9I~M1mN-QHMKF)8K)(EouTX z(~!tta&!T{sS#JYS^wa}a?M`n<&ceP`-)Ft7|yubCU(GRC4~)E%XP3v023cR(xPX* zxUi`k3}~zCxhcrh=Mq&p=jsgsVokIrHyrV5%^@B)@ih%G&HG{N?p8vP8?9yTHE`nd z-3=?Z*No`4S<&BQ%k5?Ivm5CV2NjUn3>$Z$^|@vsTq?9#pNfoEtM=J2%eWu^RcV4` znfC~3ukj}^w@|rq2zL+fap}$zFsR`UCu;#%xa4z_yum6(tq_>I1RA!yBo+mO&`KG$ zzXFhR{SgesxZOQ4diG((gQw4M*xdJ(NU23Yjsu<{h~Bq-i2q0>#B4yiEMfbd10H+L z6`E6%HQwE?*i5g5e7QK_iu1ZP-Xf2f27P$fM`r<5k@>z__X?E~-tzK-1b&k}3&$c) zeB;Xz^(tPoa(5Weg}J&Y`(ni%jU#C_ zfMl5=k_>^dsE#)Sx>&yl%m!|bMqqK$0nNn*V=nn`TB$jIet#l$Z^U8Y zE6S6ZkiX!@84LRb@gs<2u93!#wTI^Q;A1y0@FS z!k>F07+2H-D`WM5$$yblzEoDLEgQU|W(K78Ey3M3N@8pM1uLb!9!U*%J(_IL{&c`I zPbBS3>lSpJhQ@gyf_wk~q5W9wAcbU^z94n2;nZS>PNx`N{#g|JrSV4tLp;siFiuHm zl9Le}6Q#0L7AZy5=4H;XPMqc>>ij#OK{-p z?W~H_1TeM_Oo4_VU;#DPeqzQP&yb7MU_+Cb4mU6qo+i^D*qRoJc-`a1s)>(&(*0xw zT=ONpBSk8+PFoZhm5JXB9PVG~DwHIx)$$3XJV{nRnI8#>zxm6}Dl_zLz zb;XAT&Dfifi|h8F2L1UKgCg1WRWs~F0%!I$;L5Ji#;M#39N3EI#pGQQRKeePF=qSl zAl%!}UOm;k(=fbTn>tYKujZ3JF9dlJYP4%xRZoK*nGVJos4H-Fr!H(-ssLhORAKuT z7{quHRJQ&3hPcr+ei%au)`z{ojgy%eCtF%TlZaCRWJnQWyoJLNNU2i~dDQR%6{xQQ zERb+x8{I_WP<_56%S}jlBM;W_94j5+6LO^CT?Ed-zki*`wqj{BDb?#*(c_xee+Dcn zX;ZAhHso9oDLd8^=|X!F6ke;$hX5jS>JEv(9kw9yU-u~xCU`!S{epcx^QXRQjsx`n(R!PjU=ib6vj@MYGx=Le*Su*jX@_@uO&b*g3Cwx9ac-70A^Dj-DblJ< z@xFF(oA}n^tOZf*A_HSCuI8m`TJhuEU|zfq+3=Au8bf{@(-Kz(d3nu&1^}_mw+)N- zgsqKU284Vo7ZwdIU6-|$V)b-_OU4UfZm!iL_)Yn+4mXeI6*(_JUfyLR|X$wWq& zbF1mjOq;&0XxG;C!iQ&>h6J1q7d#_tTjN9Z@%6r>Y1)T2=ZRC19ICQPDRS{e4~IF{^xj>K zsKA6*h*}+sG;(2k3RfFXqcnn{V9ZT-*1mu!YP?5Q^(Pk#fNgTc8Io;PE({iWEM{>e zT_pnel%g7QphA$mrDTFlHF@qJd2Z=|cUlT>Nhyz%ve=Kpd${mebZs3JXnnqUWWTULy4gjl*zK^2pHA=NQ{7DdBs8sQmMTAGW zRl@KQqMvfzb!6Vpag2}O)xWAKx0VsT5@)=qIP(%YJi@c4DMMf$uc&By5gBj9AOA3bCD`|H;7@^~#W<2Q%( zi+hj*w9_1p!z$Vc!abwDiW9Db(QGPPil&~Rx9BK>ZNc=#PPd!Z9zVFh#tYiP^B5$+pkaUD? zqJWk#%cs&B^;Q5!uE3EZcyr~UX#FkVV4;(e1oFDI#p%#eW$Ot^ccz#;QdE(1jD)8~ zo(!@ww*cb7IvpTJ7jo;2Omi$P3AJ=NKOe7^QOWC1s{Xeghk^-! z8A(oC&O@3en5Iib5x(WijT>fK{GRtO!5bLM7J?LezSe5Qs$^Xy1b+@UmJ?3UHE+n^ z9LPH1=fAM%f3Gkx+Uyb|ixUG(o1xvsk8N8o1B{Ce|}6r0dd#r=CZV< zco;F{Rdj#(^N9~Nm-_n}YRCOixiJvnF%nY8i_KB|)XyDwb8hpH&p^VJcDIjTmM!EB z4FPXMj<>wo-h&^PC2-5m(4fl*z9qQ8w5g+GACRK>ODYs}O(Y1Vt$ClUEt!5K&d#=v ztj0+ujo{F`r35w_^qq7A@-9}+m}n7d!Y10GgO&*I@rVa#5f?GaKbEUc4^qk2q5Wto_#PcN`jbH#f2o(H`U{rZOTW~^B28rnPtZV&E^uHHkOo^4N%Y8 zwGo|GLs1kuq`mdn@}a1!cP(^6h_MBhZQBpdlRMhQ;sSjtl-`jc{F-fSMCuHRGp!hY zmP~!hD-#zc-zO#K!jN;%ez=H(<@ z9e|{C9N{j3oI4qC=8qSs&+C0m1lFh5+}=TEYFJ#nx)73#lP2)SKX2|8(h>&D29XA2%ZFn$;P5dw#Zu3@@vvHtxUs?q+bwSt zt}as>0E!wvYqy#_=!jz^a-A2hwLUez=1=`<_FuICgWbZ~f(BraRzstImOOrQ z{Ati}-j+l8!%3cdwCH6+9^{<=S5pCO-34bMtp&(E2FCmk`riOYKB|rWt%g+KPM;BF ztf;-l-gnSmaZEDg^P(1VF4AwL;0rU_&NK_ZDLT!MoJ(x7ft+&YHzdo$Qt;ykh^;%* zB3aW6YAqLL3fh+gH`_ne-{4l(S=2&FH7+Tl<`OGbMe7ssE4~O&LngfqFaSd=(=*lv zSFDhx1;N@UG_`*GTCy^48y?(dLhYOGZBQUqQ0Z-Ch(6+3-D}`}E_D@-QPUZl|+^d6zFirwdz9IOs1Z}+afAKY)6R*BcW5O>`2R@ z)T8}qhUav~os{W@D=i^Mx7dAfFa^1F+g7n#$EcTNn}MMEq`WcqI(M}R5Thr&>=4&M zv$X>vcidSJ*1$I{3t9r+%n?=b=My!a_QCk#j{|BB689r zn5yiE^nOf%5mi}dMz`H!^eDf3yNlGVuJ5o`Sqh8uYGF2ckpB9*3oz+RF-G1}H0!lX zg7Cfa6zO&a47#U`j=^zYhn3*BK;fjLaNMin-W|~2#1yVs8;G^5jvKSrPzJ{wM-AHU z0GVwlG-V`?ak2l~wbTH2 z_j2t|H&UKPk0L98mM3g2cYxh0dTN48jvXyJ;EtB~#LgBB6i$%D*Sv6UF0K+yH*WE; zon~uW#|Tt!mX$9Ff)*)b9xVhhG|F2w_*-&HeNj8#P7i#wrYSIXUP&6l%f!E|rRu6b z@dbTyPG8R=8RTVkZ3 zT*D~jSu3Y5KU(JyfNRO7)1VPtNU3j*(+5v-%N#>f5^%e$ZazWm*ej7bKoREX9S-ZH z33Lhc(2>HitQ}nNU$TPO3+BC0sOjioaG9Np@CPDcS;7mgfd?~GGz85t4!G=R6n<01 z1n^W&qd|NA9X$iLo73Q%JIjvsdtnNsCjIicv~umj44(OpI%l_E7^BriUB?+S&?1C2 z=Anz2_lK+_sy&N8!=X^p0wTm$3I*rB$l;v|WI(0N?*ZfpTM;~&?vS`vsn@O(iI64% zveT?Iq+QE@wweojNxAQkB4pJ=d8fw}>PdvSNrY@{;PAFtwIW}l3>v~7^`<_4Scc;w zgIS}_ZGcweIqv=37rcjplGj~8$luS7V);D|Ek}J_6pm@9LwaB9Cr4!8tVA7)UvdYn z2O{S3s@3_h{+@?U;!*is*>nEo{xn=ze!mhNi>TdeMuLKzG8je{-)KAk1wa1X6|z~n!)X9CL!)sLA=wz6m6i6vPaRPn)MjXAFu(5E$UxL{ z95rE}WRJA#$AJ`npbLX9D9BgNaauy>!mX}Z1bq1%7YA7JjhLP=bIxbhg?23j)0E!A zN^531WCzX(RFP)iI-?rg)ZTA%^KDf2K_<|{{~i}pSLNI{&4^6g?e-PJ8%;!58{?pe z=us&yaK_m?rCQeoVAG;lUO>)u4rEJV_miHGV+apxQtb7zooHV^$30*o5V&s^iGOpC zY$FqT#70mUT*en{DfDdC9c##tE}P7G)kMHR4H0Z(>~Uzn9kRj?sYa@wiUikb*XYa+ z*$qZukizq~$kJfo^;M?8k5i*3BREo#F?XoV?n~Q7=-RFGnjz<2Cd~CcQ~{u)C+5yC z1F+{7AXB0X@I7dWUq*TJeh`8j8FIEhVMWSkxOWk1z`4cT%De*y&mYQ+Hz2Dd$H_ow zv#EF;U#3MzV-`8%hr)Sr#?liAmq?axv(=%DXgO2^9FJ6cBKZQFiC<>5GPrGkf>chk!c>=paGFy|{w(W~U-S96u3a z$4ZnxC+K~Fit~)EE<<*Gy{IgUkaj~)G6u4TDnM6+UlvaK7T|aT@4|MkbFdD|1T(sn zx7z$!lhbvXAU)oqZWdudO5Hh{#*go49#lNkJZ*)dJZgti*#a$=huEu18%@k)L8h@{H*u!oW0;u=wM!3o{rbZQW`X|Kv9p z2Q@+R_nZiC28Ch_wI%RihE7ixhV%}11D-=Eovz)}d75r8y8gzBoGux-s(=5;IKdh( zmvG@PvA3X06nNJuz2Kg24cisjmpC838z%yt9&6NuRd#GkU~VO~pCmE3YB2SP8S|o1 z1yE&&e+7t6-QgA(Y6ADQB(kn_5;D*lN|?SNCS8ud?Kt%YKb{`8y-h<+;DzG8)P%8% zy*VW*pCs%N;#--0jnJs<*i;q3kxn+X)n%rL_rr0`TU#14bqiHM24p%)p`%8pHH}FU zXnpw}jo@M&?k`fm8M{Sifu<(tL*|zBoxYv0yKmIjr`Fx1VD;XsZpxJQQY54zU*pc( z$R?33`}yujo=xm`4a7|-cg@Q})JTgW=^78&R=aZA{epbitGWF-PGyS))OVxffNM#| zu$%{3FMt#3@9+nsn4FAA(&c-q&!AJ>nkb1-DVzf`7ku!fJIG{{td*nBJXqWuhm=HD zCk-0K@P_5`0mW#>{q>wl3MfWw-dwECfluLkjTE9pO3fG3m`OIM0#Mr7fEUWdBK`m^ zeQOuNiHmr-WR)-{a4}*3<#Hwx z7iJ?vvaCu?FofvA>5s%HPoX#$PYU5uD-GTU)&WY{oTyUi1 zWyeP1okU5N`JtO=vS2Vl3R+?)u86Zh0pd-h0YFKtH*#C@3ty8E(KQf|q%7RPyw;N60Fb-#198fTx8nMIs=pT?}PQd3C@uCbR zxXq6&$i!?*;I9g>O)nmKj_b{#HometZ-{>>aA)vS{=ck%>S;k|*VfKcpUN+`yx6T< zp*Rt)2jC(@8m<#AR9@*dSMV`c_Z>fK$HIzB2>$HigO{20?AYgUQcOuAqgguP#YvL% z2T77;uk&JG`faEQw-}Mx2s2B-b)rBMm{}<-52yMn48CwU2d*AZDW$Vxjd)0eAU-@g zF9sDEL-60vN?kr^f6`XI^iW~jPWsAcn_p+r9Vzh2=Q++NZWmyC`z>rD;%eGj*JV4E zRyAdZ=eJf+*OA`R&#vHq-H7cB)JyjMM}M2<&5y)^6qMm&?3o|fwwl>@RjKfDsVaJ4 zd8(GZQckzQY)w$qev*#lTv$Wp$-oQg90gN=6}{|w(c^0 zZ>?et#j)Q4)@_G#{#zZ?V1FT6wScNc`kuK>H2Bi0$cVR_=aV2rXp3;? z)HT^dA{uE3q-v?qK9M+6QBZLov?mmd0GP~kD9fS>8a@mAb!N+Ir$qBk+2N6_XJnw7 zvVQ}!!$Xh%N;>TD4A2(f@NnPffEoOyat^Aonoxg*KmR{Oyw!YRCE`;xDcSY=%Ab4m z?{2^jIo#a-T{6}b9GRN>e(Kz=+V}4liKuXFGxNOsexb+IGBW=&sC0Rf63Q4(VZr*> zQsiR~qwBwN*lwKrk7~=Yfwuc>Pwfi~XWD|!`5RXLtvSoC7O8{EENUQw@wH`#@p0Up z+i&z-!mpTUB$_`hG4WRu4*t$`*HqgIXk97Tw)!>wuhLVgpv#1uosTa?zTSUUiG!@{ zlCHQ=!(S`AnM`x%jb1BM47y?-NMVX5L9iMF{+6jBwYxU?{}se5ywlTEa{8}Rru!D} zvaIjTmx(nT#GP4U!HlcF={p|Q3B+{-YdGeL>wA~s_-lh7KDkdWG?*QZ6VdxklYG=) z9ON2*L)4@yLF`X+^f0%{ylo1SDd$h5-pQ|#hEG%&o!Z*x{+&=3e#>CbZ7R$>S01*~ zcJ=uSnxUzDGO&6vTq-ZCUd?bvDi2}=3>M~9$inJh!{c=3U&-aAhTEWHiBXIWUsF8m z@}!>7!+OO(-*>(gQ;;Jt_?bOz&0stN<~3Iqu)=$10bzE%=UiynWibG=*8i@K`>coL z>~)c5ZJ?C5=4P~_u|c4K%S%=9j`g4150bler;DxSYjpZ(r@sH<_AKEpB!8zgt^20l z+3Aj#Cv!l~yFr0ny3okrJ@_!5R@dY0J9YRMsF2$KZF1!_s<|WD9KIMyI{&)1UYh&;l{%yD zz&*v%g;s70!DMc82ahlz9C1U&7bys{RbsCFF1l0zZ4s~4%pCxJdH3;FTfx1!dfZPV z?BSbtM%o6VPxfJ9QY%{IeKRS)<|VUf^-I%W>f&EoA%*Ipy&E4NnGNJ7>g?E2LL@F? z@5LqG6ivMUvfj`opF5Z7Qy{FFdG{xUqr0A$XG(|O&GU;DbfF|Ud2}6Q9o&Z<+MkWE zgC7foI_N2K#JJyP=?qqq>%zZO4xkDDN&7knRu2%>mOmGH|7Erg5@qkiEFOk8!V55W z58d(5PBGY19V*b-d&xwy%&PD~TQ*fdi&MK_I;^40nh&q9swVQbV?0koTtoj_F+TYb zZDRLBV`t+q?E&t(cw(=bKF7|R`pV}b@4YOz_Bw%-$&n|yL+Pvf*65Gxbh_f}r_vgM zBjE;nR{ayWk7YgG|2*AZ?FQ&^*_WO<=sZbTCtBJL16dz1&*F0HYIpb(f8K40s|(#; zB<$1cc=$YvBBsW2%CGgLL;Ty-yXm;d2ctPzOa9r^|BrN}a(`*-%2~bgxY;xF5SQ?) zeO7gno{>2WVLNr1rT$7kA364go55^~zyf#6MfTE3MY zGT&e@y}bX@o%5`;{hChwnh>_eVwgm*@7ued`JKx?sXEaTawkNvCN?X;G_f4&>5w*! zVZ7AoQ~Z&IybEi%m0((Cli%MEZws3;r8h-Ox8BV$G{Y3RqaAwU3be?3qH$wM*SbI4H`7*|DHwe9g(|P=3L7?dylmRqlLP4^!azwATgkbt*)#b4rx6 z4uWRNn_h!Z-qu-Q<~%X_!R?ykQ`e%^4eR!XF8!#&-!0~Vy>U-RinLXBp?KDZs?Knz zAtc`5uOiz5q3TB$3XJ%5R~hr!mL>g^zLd$BiU;S|Ez9YbOe$KPM)G_Lf4BK^Ad*sF z>I=ob!EMLA`C6rvJ#V!m&v#szSYm8k&&=46ERJ`1Wn4(osv~0*XPYwvX=-;1$}j+`% z2;6GSY804>PFi&4V9@)cA66>`PQzii?uDNI1`E+zeV0U|4yc{T4!la2OeyTkX12hB zp~BozmB^5X0=;vVYNt&b8`d;4&>FdIO3OW>(OmI{XxNmno(te? zwvg=}xg`Rwzk?E}K0(7P7{%dSu?d&a>ij2AGOYGeOz%2vWmgZubZi*i+QRH5=sz~` z_GnM>k;RCAJSSA{|D!F73L`jB`dj-A{p7THraSQ5v$H&}VfOuau8&von;%bZ+sRoj zBWfGUx`HL!1mN*clyh7>l5i`J>E<=wP(=(kN@j>rl218aNoVm=QU#U^bn`{JC}2%w z%lLul`j;ZEu*m1p%;*RIQuF`bT1{Jvwsq%W<|?1l?)&{FgWZGC_`aO}A_RMKPW`(D zzf!0W$Bex>H

EZvu)7>Zc*O)NTr>qE_z!s3%8Bn_dNdi1`@h& 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 literal 19133 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 z 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::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(beneficiary, METHOD_SEND, None, rt.get_balance(), None, ExitCode::OK); - rt.expect_delete_actor(BURNT_FUNDS_ACTOR_ADDR); + rt.expect_send(beneficiary, METHOD_SEND, None, TokenAmount::zero(), None, ExitCode::OK); + assert!(util::invoke_contract(&mut rt, &selfdestruct_params).is_empty()); + rt.verify(); - assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); + // 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() { +fn test_selfdestruct_missing_beneficiary() { let bytecode = hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap(); let contract = Address::new_id(100); @@ -46,8 +116,10 @@ fn test_selfdestruct_missing() { None, ExitCode::SYS_INVALID_RECEIVER, ); - rt.expect_delete_actor(BURNT_FUNDS_ACTOR_ADDR); + // It still works even if the beneficiary doesn't exist. assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); + let state: State = rt.get_state(); + assert_eq!(state.tombstone, Some(Tombstone { origin: 100, nonce: 0 })); rt.verify(); } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index de60219aa..7205e4c8a 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -102,6 +102,10 @@ impl MessageInfo for FvmMessage { fn gas_premium(&self) -> TokenAmount { fvm::message::gas_premium() } + + fn nonce(&self) -> u64 { + fvm::message::nonce() + } } impl Runtime for FvmRuntime diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index e0b01184a..7d874ffb7 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -277,6 +277,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Message information available to the actor about executing message. pub trait MessageInfo { + /// The nonce of the currently executing message. + fn nonce(&self) -> u64; + /// The address of the immediate calling actor. Always an ID-address. fn caller(&self) -> Address; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 8ee4f8528..eb18df3de 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -872,6 +872,10 @@ impl MessageInfo for MockRuntime { fn gas_premium(&self) -> TokenAmount { self.gas_premium.clone() } + + fn nonce(&self) -> u64 { + 0 + } } impl Runtime for MockRuntime { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index b50dfbdcd..c914f3eb1 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -583,6 +583,10 @@ impl MessageInfo for InvocationCtx<'_, '_> { fn gas_premium(&self) -> TokenAmount { TokenAmount::zero() } + + fn nonce(&self) -> u64 { + self.top.originator_call_seq + } } pub const TEST_VM_RAND_ARRAY: [u8; 32] = [ diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 2fc3d993a..01d08c283 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -63,7 +63,7 @@ fn test_evm_lifecycle() { let call_result = v .apply_message( account, - create_return.robust_address, + create_return.robust_address.unwrap(), TokenAmount::zero(), fil_actor_evm::Method::InvokeContract as u64, Some(ContractParams(contract_params.to_vec())), @@ -158,7 +158,7 @@ fn test_evm_staticcall() { // A -> staticcall -> B (read) OK { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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; @@ -186,7 +186,7 @@ fn test_evm_staticcall() { // A -> staticcall -> B (write) FAIL { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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; @@ -207,7 +207,7 @@ fn test_evm_staticcall() { // A -> staticcall -> B -> call -> C (read) OK { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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]; @@ -237,7 +237,7 @@ fn test_evm_staticcall() { // A -> staticcall -> B -> call -> C (write) FAIL { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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]; @@ -315,7 +315,7 @@ fn test_evm_delegatecall() { // A -> delegatecall -> B (read) OK { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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; @@ -343,7 +343,7 @@ fn test_evm_delegatecall() { // A -> delegatecall -> B (write) -> return (read) OK { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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; @@ -426,7 +426,7 @@ fn test_evm_staticcall_delegatecall() { // A -> staticcall -> B -> delegatecall -> C (read) OK { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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]; @@ -457,7 +457,7 @@ fn test_evm_staticcall_delegatecall() { // A -> staticcall -> B -> delegatecall -> C (write) FAIL { let A_act = accounts[0]; - let A_robust_addr = created[0].robust_address; + 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]; From 9191eb34be1665b4473eb9369baffe3866a5353e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Jan 2023 16:14:44 -0800 Subject: [PATCH 236/339] EVM: return just the bytecode hash (#1015) * EVM: return just the bytecode hash Previously, we returned a multihash. However, this got serialized as a map. Now, we just return the raw digest encoded as 32 bytes. * chore: state size changes We've shaved quite a few bytes off by correctly serializing this. * test_vm: fix hash method We need to return the digest, not the encoded multihash. This only affects the tests. --- .../evm/src/interpreter/instructions/ext.rs | 28 +- actors/evm/src/interpreter/system.rs | 15 +- actors/evm/src/interpreter/uints.rs | 9 + actors/evm/src/lib.rs | 11 +- actors/evm/src/state.rs | 101 ++++- actors/evm/tests/ext_opcodes.rs | 34 +- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 17977 -> 17917 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 27932 -> 27808 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 13830 -> 13832 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 14779 -> 14738 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 14587 -> 14604 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 14204 -> 14346 bytes .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 19949 -> 20173 bytes .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 22537 -> 22556 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22838 -> 22805 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 17373 -> 17361 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 19457 -> 19436 bytes test_vm/src/lib.rs | 2 +- 29 files changed, 1761 insertions(+), 1679 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index d86a759fe..5646e5fc6 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -1,29 +1,19 @@ use crate::interpreter::instructions::memory::copy_to_memory; use crate::interpreter::{address::EthAddress, precompiles::Precompiles}; -use crate::U256; +use crate::{BytecodeHash, U256}; use cid::Cid; use fil_actors_runtime::deserialize_block; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::ActorError; use fvm_ipld_blockstore::Blockstore; -use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::sys::SendFlags; use fvm_shared::{address::Address, econ::TokenAmount}; -use multihash::Multihash; use num_traits::Zero; use { crate::interpreter::{ExecutionState, StatusCode, System}, fil_actors_runtime::runtime::Runtime, }; -/// Keccak256 hash of `[0xfe]`, "native bytecode" -pub const NATIVE_BYTECODE_HASH: [u8; 32] = - hex_literal::hex!("bcc90f2d6dada5b18e155c17a1c0a55920aae94f39857d39d0d8ed07ae8f228b"); - -/// Keccak256 hash of `[]`, empty bytecode -pub const EMPTY_EVM_HASH: [u8; 32] = - hex_literal::hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - pub fn extcodesize( _state: &mut ExecutionState, system: &mut System, @@ -52,20 +42,20 @@ pub fn extcodehash( let addr = match get_contract_type(system.rt, addr) { 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(NATIVE_BYTECODE_HASH.into()), + 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(EMPTY_EVM_HASH.into()), + 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(EMPTY_EVM_HASH.into()), + ContractType::Account => return Ok(BytecodeHash::EMPTY.into()), // Not found ContractType::NotFound => return Ok(U256::zero()), }; // multihash { keccak256(bytecode) } - let bytecode_hash: Multihash = deserialize_block(system.send( + let bytecode_hash: BytecodeHash = deserialize_block(system.send( &addr, crate::Method::GetBytecodeHash as u64, Default::default(), @@ -73,13 +63,7 @@ pub fn extcodehash( None, SendFlags::READ_ONLY, )?)?; - - let digest = bytecode_hash.digest(); - debug_assert_eq!(SupportedHashes::Keccak256 as u64, bytecode_hash.code()); - - // Take the first 32 bytes of the Multihash - let digest_len = digest.len().min(32); - Ok(digest[..digest_len].into()) + Ok(bytecode_hash.into()) } pub fn extcodecopy( diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 9f50311bf..1e62f44d7 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -15,10 +15,11 @@ use fvm_shared::{ sys::SendFlags, MethodNum, IPLD_RAW, }; -use multihash::{Code, Multihash}; +use multihash::Code; use once_cell::unsync::OnceCell; use crate::state::{State, Tombstone}; +use crate::BytecodeHash; use super::{address::EthAddress, Bytecode}; @@ -84,11 +85,11 @@ pub struct EvmBytecode { /// CID of the contract pub cid: Cid, /// Keccak256 hash of the contract - pub evm_hash: Multihash, + pub evm_hash: BytecodeHash, } impl EvmBytecode { - fn new(cid: Cid, evm_hash: Multihash) -> Self { + fn new(cid: Cid, evm_hash: BytecodeHash) -> Self { Self { cid, evm_hash } } } @@ -318,11 +319,9 @@ impl<'r, RT: Runtime> System<'r, RT> { )); } - let code_hash = multihash::Multihash::wrap( - SupportedHashes::Keccak256 as u64, - &self.rt.hash(SupportedHashes::Keccak256, bytecode), - ) - .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to hash bytecode with keccak")?; + 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 diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index f87aba173..4f89e6178 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize}; use substrate_bn::arith; +use crate::BytecodeHash; + use { fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, std::cmp::Ordering, std::fmt, uint::construct_uint, @@ -109,6 +111,13 @@ impl From<&U256> for TokenAmount { } } +impl From for U256 { + fn from(bytecode: BytecodeHash) -> Self { + let bytes: [u8; 32] = bytecode.into(); + Self::from(bytes) + } +} + impl Serialize for U256 { fn serialize(&self, serializer: S) -> Result where diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 4e43b8ba2..8603b2f35 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -2,11 +2,8 @@ use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ADDR, INIT_ACTOR_A use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; use fvm_shared::address::Address; -use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::error::ExitCode; -use interpreter::instructions::ext::EMPTY_EVM_HASH; use interpreter::{address::EthAddress, system::load_bytecode}; -use multihash::Multihash; use crate::interpreter::output::Outcome; @@ -240,14 +237,14 @@ impl EvmContractActor { } } - pub fn bytecode_hash(rt: &mut impl Runtime) -> Result { + 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(Multihash::wrap(SupportedHashes::Keccak256 as u64, &EMPTY_EVM_HASH).unwrap()) + Ok(BytecodeHash::EMPTY) } else { Ok(state.bytecode_hash) } @@ -340,8 +337,8 @@ impl ActorCode for EvmContractActor { Ok(IpldBlock::serialize_cbor(&ret)?) } Some(Method::GetBytecodeHash) => { - let multihash = Self::bytecode_hash(rt)?; - Ok(IpldBlock::serialize_cbor(&multihash)?) + let hash = Self::bytecode_hash(rt)?; + Ok(IpldBlock::serialize_cbor(&hash)?) } Some(Method::GetStorageAt) => { let value = Self::storage_at( diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 6624d2ffa..1f678c692 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -1,8 +1,12 @@ +use std::array::TryFromSliceError; + 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}, }; @@ -15,6 +19,71 @@ pub struct Tombstone { 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 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)] @@ -24,7 +93,7 @@ pub struct State { pub bytecode: Cid, /// The EVM contract bytecode hash keccak256(bytecode) - pub bytecode_hash: multihash::Multihash, + pub bytecode_hash: BytecodeHash, /// The EVM contract state dictionary. /// All eth contract state is a map of U256 -> U256 values. @@ -54,3 +123,33 @@ pub struct State { /// 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/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index e618cfd23..1c5a7a4f1 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -1,8 +1,8 @@ mod asm; use cid::Cid; -use evm::interpreter::instructions::ext::EMPTY_EVM_HASH; use evm::interpreter::U256; +use evm::BytecodeHash; use fil_actor_evm as evm; use fil_actors_runtime::runtime::{Primitives, Runtime, EMPTY_ARR_CID}; use fil_actors_runtime::test_utils::*; @@ -23,7 +23,7 @@ use util::{CONTRACT_ID, DUMMY_ACTOR_CODE_ID}; fn test_extcodesize() { let bytecode = { let init = ""; - let body = r#" + let body = r#" %dispatch_begin() %dispatch(0x00, evm_size) %dispatch(0x01, native_size) @@ -46,7 +46,7 @@ native_size: extcodesize %return_stack_word() -evm_account: +evm_account: jumpdest # evm account push20 0xff00000000000000000000000000000000000101 @@ -151,7 +151,7 @@ fn test_extcodehash() { %dispatch(0x02, non_exist) %dispatch(0x03, account) %dispatch_end() - + evm_contract: jumpdest # get code hash of address 0x88 @@ -203,11 +203,8 @@ account: // a random hash value let bytecode = b"foo bar boxy"; - let bytecode_hash = Multihash::wrap( - SupportedHashes::Keccak256 as u64, - rt.hash(SupportedHashes::Keccak256, bytecode).as_slice(), - ) - .unwrap(); + let bytecode_hash = + BytecodeHash::try_from(rt.hash(SupportedHashes::Keccak256, bytecode).as_slice()).unwrap(); rt.expect_send_generalized( evm_target, @@ -223,7 +220,7 @@ account: // 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.digest())); + assert_eq!(U256::from_big_endian(&result), U256::from(bytecode_hash)); rt.reset(); // Native code is keccak256([0xfe]) @@ -259,13 +256,13 @@ fn test_getbytecodehash_method() { let mut rt = util::construct_and_verify(Vec::new()); rt.expect_validate_caller_any(); - let res: Multihash = rt + let res: BytecodeHash = rt .call::(evm::Method::GetBytecodeHash as u64, None) .unwrap() .unwrap() .deserialize() .unwrap(); - assert_eq!(res.digest(), empty_bytecode_hash(&mut rt)) + assert_eq!(<[u8; 32]>::from(res), empty_bytecode_hash(&mut rt)) } /// Keccak256 hash of &[] @@ -393,13 +390,13 @@ fn test_ext_in_initcode() { let bytecode = { let init = " -# code hash of self +# code hash of self push20 0xFEEDFACECAFEBEEF000000000000000000000000 extcodehash push1 0x00 # key sstore # store for later - -# code size of self + +# code size of self push20 0xFEEDFACECAFEBEEF000000000000000000000000 extcodesize push1 0x01 # key @@ -437,10 +434,7 @@ init_extsize: TokenAmount::zero(), None, SendFlags::READ_ONLY, - IpldBlock::serialize_cbor( - &Multihash::wrap(SupportedHashes::Keccak256 as u64, &EMPTY_EVM_HASH).unwrap(), - ) - .unwrap(), + IpldBlock::serialize_cbor(&BytecodeHash::EMPTY).unwrap(), ExitCode::OK, ); @@ -460,7 +454,7 @@ init_extsize: // codehash let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); rt.verify(); - assert_eq!(EMPTY_EVM_HASH.as_slice(), result); + assert_eq!(BytecodeHash::EMPTY.as_slice(), result); // codesize let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(1)); diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 28e422b8f..adb993afb 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":2479,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2270,"get_count":3,"put_bytes":266,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2274,"get_count":3,"put_bytes":270,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2278,"get_count":3,"put_bytes":274,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2282,"get_count":3,"put_bytes":278,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2286,"get_count":3,"put_bytes":282,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2290,"get_count":3,"put_bytes":287,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2295,"get_count":3,"put_bytes":291,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2299,"get_count":3,"put_bytes":465,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2473,"get_count":4,"put_bytes":469,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":473,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2481,"get_count":4,"put_bytes":477,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2485,"get_count":4,"put_bytes":481,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2489,"get_count":4,"put_bytes":485,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":490,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2498,"get_count":4,"put_bytes":494,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2502,"get_count":4,"put_bytes":460,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2468,"get_count":4,"put_bytes":464,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":485,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2493,"get_count":4,"put_bytes":489,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":598,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2606,"get_count":5,"put_bytes":602,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2610,"get_count":5,"put_bytes":606,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2614,"get_count":5,"put_bytes":610,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2618,"get_count":5,"put_bytes":614,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2622,"get_count":5,"put_bytes":618,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2626,"get_count":5,"put_bytes":623,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2631,"get_count":5,"put_bytes":627,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2491,"get_count":4,"put_bytes":524,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2532,"get_count":4,"put_bytes":528,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2536,"get_count":4,"put_bytes":532,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2540,"get_count":4,"put_bytes":536,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2544,"get_count":4,"put_bytes":540,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2548,"get_count":4,"put_bytes":544,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2552,"get_count":4,"put_bytes":549,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2557,"get_count":4,"put_bytes":553,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2561,"get_count":4,"put_bytes":662,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2670,"get_count":5,"put_bytes":666,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2674,"get_count":5,"put_bytes":670,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2678,"get_count":5,"put_bytes":674,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2682,"get_count":5,"put_bytes":678,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2686,"get_count":5,"put_bytes":682,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2690,"get_count":5,"put_bytes":687,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2695,"get_count":5,"put_bytes":691,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2555,"get_count":4,"put_bytes":588,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2596,"get_count":4,"put_bytes":592,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2600,"get_count":4,"put_bytes":596,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2604,"get_count":4,"put_bytes":600,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2608,"get_count":4,"put_bytes":604,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2612,"get_count":4,"put_bytes":608,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2616,"get_count":4,"put_bytes":613,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2621,"get_count":4,"put_bytes":617,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2625,"get_count":4,"put_bytes":726,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2734,"get_count":5,"put_bytes":730,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2738,"get_count":5,"put_bytes":734,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2742,"get_count":5,"put_bytes":738,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2746,"get_count":5,"put_bytes":742,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2750,"get_count":5,"put_bytes":746,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2754,"get_count":5,"put_bytes":751,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2759,"get_count":5,"put_bytes":755,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2619,"get_count":4,"put_bytes":652,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2660,"get_count":4,"put_bytes":656,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2664,"get_count":4,"put_bytes":660,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2668,"get_count":4,"put_bytes":664,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2672,"get_count":4,"put_bytes":668,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2676,"get_count":4,"put_bytes":672,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2680,"get_count":4,"put_bytes":677,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2685,"get_count":4,"put_bytes":681,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2689,"get_count":4,"put_bytes":790,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2798,"get_count":5,"put_bytes":794,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2802,"get_count":5,"put_bytes":798,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2806,"get_count":5,"put_bytes":802,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2810,"get_count":5,"put_bytes":806,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2814,"get_count":5,"put_bytes":810,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2818,"get_count":5,"put_bytes":815,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2823,"get_count":5,"put_bytes":819,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2683,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":461,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":465,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2473,"get_count":4,"put_bytes":469,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2477,"get_count":4,"put_bytes":473,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2481,"get_count":4,"put_bytes":477,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2485,"get_count":4,"put_bytes":482,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":486,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2494,"get_count":4,"put_bytes":596,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2604,"get_count":5,"put_bytes":600,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2608,"get_count":5,"put_bytes":604,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2612,"get_count":5,"put_bytes":608,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":621,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2629,"get_count":5,"put_bytes":625,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2633,"get_count":5,"put_bytes":589,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":597,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2354,"get_count":3,"put_bytes":497,"put_count":3}} -{"i":1,"series":2,"stats":{"get_bytes":2505,"get_count":4,"put_bytes":501,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":2509,"get_count":4,"put_bytes":505,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":2513,"get_count":4,"put_bytes":509,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":2517,"get_count":4,"put_bytes":513,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":2521,"get_count":4,"put_bytes":517,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":2525,"get_count":4,"put_bytes":522,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":2530,"get_count":4,"put_bytes":526,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":2534,"get_count":4,"put_bytes":698,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2706,"get_count":5,"put_bytes":702,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":2710,"get_count":5,"put_bytes":706,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":2714,"get_count":5,"put_bytes":710,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":2718,"get_count":5,"put_bytes":714,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":2722,"get_count":5,"put_bytes":718,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":2726,"get_count":5,"put_bytes":723,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":2731,"get_count":5,"put_bytes":727,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":2735,"get_count":5,"put_bytes":691,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":2699,"get_count":5,"put_bytes":695,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":699,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":2707,"get_count":5,"put_bytes":703,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":2711,"get_count":5,"put_bytes":707,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":711,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":2719,"get_count":5,"put_bytes":716,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":2724,"get_count":5,"put_bytes":720,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":2728,"get_count":5,"put_bytes":829,"put_count":5}} -{"i":25,"series":2,"stats":{"get_bytes":2837,"get_count":6,"put_bytes":833,"put_count":5}} -{"i":26,"series":2,"stats":{"get_bytes":2841,"get_count":6,"put_bytes":837,"put_count":5}} -{"i":27,"series":2,"stats":{"get_bytes":2845,"get_count":6,"put_bytes":841,"put_count":5}} -{"i":28,"series":2,"stats":{"get_bytes":2849,"get_count":6,"put_bytes":845,"put_count":5}} -{"i":29,"series":2,"stats":{"get_bytes":2853,"get_count":6,"put_bytes":849,"put_count":5}} -{"i":30,"series":2,"stats":{"get_bytes":2857,"get_count":6,"put_bytes":854,"put_count":5}} -{"i":31,"series":2,"stats":{"get_bytes":2862,"get_count":6,"put_bytes":858,"put_count":5}} -{"i":32,"series":2,"stats":{"get_bytes":2722,"get_count":5,"put_bytes":755,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":2763,"get_count":5,"put_bytes":759,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":2767,"get_count":5,"put_bytes":763,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":2771,"get_count":5,"put_bytes":767,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":2775,"get_count":5,"put_bytes":771,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":2779,"get_count":5,"put_bytes":775,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":2783,"get_count":5,"put_bytes":780,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":2788,"get_count":5,"put_bytes":784,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":2792,"get_count":5,"put_bytes":893,"put_count":5}} -{"i":41,"series":2,"stats":{"get_bytes":2901,"get_count":6,"put_bytes":897,"put_count":5}} -{"i":42,"series":2,"stats":{"get_bytes":2905,"get_count":6,"put_bytes":901,"put_count":5}} -{"i":43,"series":2,"stats":{"get_bytes":2909,"get_count":6,"put_bytes":905,"put_count":5}} -{"i":44,"series":2,"stats":{"get_bytes":2913,"get_count":6,"put_bytes":909,"put_count":5}} -{"i":45,"series":2,"stats":{"get_bytes":2917,"get_count":6,"put_bytes":913,"put_count":5}} -{"i":46,"series":2,"stats":{"get_bytes":2921,"get_count":6,"put_bytes":918,"put_count":5}} -{"i":47,"series":2,"stats":{"get_bytes":2926,"get_count":6,"put_bytes":922,"put_count":5}} -{"i":48,"series":2,"stats":{"get_bytes":2786,"get_count":5,"put_bytes":819,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":2827,"get_count":5,"put_bytes":823,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":2831,"get_count":5,"put_bytes":827,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":2835,"get_count":5,"put_bytes":831,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":2839,"get_count":5,"put_bytes":835,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":2843,"get_count":5,"put_bytes":839,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":2847,"get_count":5,"put_bytes":844,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":2852,"get_count":5,"put_bytes":848,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":2856,"get_count":5,"put_bytes":957,"put_count":5}} -{"i":57,"series":2,"stats":{"get_bytes":2965,"get_count":6,"put_bytes":961,"put_count":5}} -{"i":58,"series":2,"stats":{"get_bytes":2969,"get_count":6,"put_bytes":965,"put_count":5}} -{"i":59,"series":2,"stats":{"get_bytes":2973,"get_count":6,"put_bytes":969,"put_count":5}} -{"i":60,"series":2,"stats":{"get_bytes":2977,"get_count":6,"put_bytes":973,"put_count":5}} -{"i":61,"series":2,"stats":{"get_bytes":2981,"get_count":6,"put_bytes":977,"put_count":5}} -{"i":62,"series":2,"stats":{"get_bytes":2985,"get_count":6,"put_bytes":982,"put_count":5}} -{"i":63,"series":2,"stats":{"get_bytes":2990,"get_count":6,"put_bytes":986,"put_count":5}} -{"i":64,"series":2,"stats":{"get_bytes":2850,"get_count":5,"put_bytes":883,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":2891,"get_count":5,"put_bytes":887,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":2895,"get_count":5,"put_bytes":891,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":2899,"get_count":5,"put_bytes":895,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":2903,"get_count":5,"put_bytes":899,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":2907,"get_count":5,"put_bytes":903,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":2911,"get_count":5,"put_bytes":908,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":2916,"get_count":5,"put_bytes":912,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":2920,"get_count":5,"put_bytes":1021,"put_count":5}} -{"i":73,"series":2,"stats":{"get_bytes":3029,"get_count":6,"put_bytes":1025,"put_count":5}} -{"i":74,"series":2,"stats":{"get_bytes":3033,"get_count":6,"put_bytes":1029,"put_count":5}} -{"i":75,"series":2,"stats":{"get_bytes":3037,"get_count":6,"put_bytes":1033,"put_count":5}} -{"i":76,"series":2,"stats":{"get_bytes":3041,"get_count":6,"put_bytes":1037,"put_count":5}} -{"i":77,"series":2,"stats":{"get_bytes":3045,"get_count":6,"put_bytes":1041,"put_count":5}} -{"i":78,"series":2,"stats":{"get_bytes":3049,"get_count":6,"put_bytes":1046,"put_count":5}} -{"i":79,"series":2,"stats":{"get_bytes":3054,"get_count":6,"put_bytes":1050,"put_count":5}} -{"i":80,"series":2,"stats":{"get_bytes":2914,"get_count":5,"put_bytes":947,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":2955,"get_count":5,"put_bytes":951,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":2959,"get_count":5,"put_bytes":955,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":2963,"get_count":5,"put_bytes":959,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":2967,"get_count":5,"put_bytes":963,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":2971,"get_count":5,"put_bytes":967,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":2975,"get_count":5,"put_bytes":972,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":2980,"get_count":5,"put_bytes":976,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":2984,"get_count":5,"put_bytes":1085,"put_count":5}} -{"i":89,"series":2,"stats":{"get_bytes":3093,"get_count":6,"put_bytes":1089,"put_count":5}} -{"i":90,"series":2,"stats":{"get_bytes":3097,"get_count":6,"put_bytes":1093,"put_count":5}} -{"i":91,"series":2,"stats":{"get_bytes":3101,"get_count":6,"put_bytes":1097,"put_count":5}} -{"i":92,"series":2,"stats":{"get_bytes":3105,"get_count":6,"put_bytes":1101,"put_count":5}} -{"i":93,"series":2,"stats":{"get_bytes":3109,"get_count":6,"put_bytes":1105,"put_count":5}} -{"i":94,"series":2,"stats":{"get_bytes":3113,"get_count":6,"put_bytes":1110,"put_count":5}} -{"i":95,"series":2,"stats":{"get_bytes":3118,"get_count":6,"put_bytes":1114,"put_count":5}} -{"i":96,"series":2,"stats":{"get_bytes":2978,"get_count":5,"put_bytes":1011,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":3019,"get_count":5,"put_bytes":1015,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":3023,"get_count":5,"put_bytes":1019,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":3027,"get_count":5,"put_bytes":1023,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2313,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2187,"get_count":3,"put_bytes":183,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2191,"get_count":3,"put_bytes":187,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2195,"get_count":3,"put_bytes":191,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2199,"get_count":3,"put_bytes":195,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2203,"get_count":3,"put_bytes":199,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2207,"get_count":3,"put_bytes":204,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2212,"get_count":3,"put_bytes":208,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":382,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":390,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2398,"get_count":4,"put_bytes":394,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2402,"get_count":4,"put_bytes":398,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2406,"get_count":4,"put_bytes":402,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2410,"get_count":4,"put_bytes":407,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2415,"get_count":4,"put_bytes":411,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2419,"get_count":4,"put_bytes":377,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2385,"get_count":4,"put_bytes":381,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2389,"get_count":4,"put_bytes":385,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2393,"get_count":4,"put_bytes":389,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2397,"get_count":4,"put_bytes":393,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2401,"get_count":4,"put_bytes":397,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2405,"get_count":4,"put_bytes":402,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2410,"get_count":4,"put_bytes":406,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2414,"get_count":4,"put_bytes":515,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2523,"get_count":5,"put_bytes":519,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2527,"get_count":5,"put_bytes":523,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2531,"get_count":5,"put_bytes":527,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2535,"get_count":5,"put_bytes":531,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2539,"get_count":5,"put_bytes":535,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2543,"get_count":5,"put_bytes":540,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2548,"get_count":5,"put_bytes":544,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2408,"get_count":4,"put_bytes":441,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2449,"get_count":4,"put_bytes":445,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2453,"get_count":4,"put_bytes":449,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2457,"get_count":4,"put_bytes":453,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2461,"get_count":4,"put_bytes":457,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":461,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":466,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2474,"get_count":4,"put_bytes":470,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":579,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2587,"get_count":5,"put_bytes":583,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2591,"get_count":5,"put_bytes":587,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2595,"get_count":5,"put_bytes":591,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2599,"get_count":5,"put_bytes":595,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":604,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2612,"get_count":5,"put_bytes":608,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":505,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2513,"get_count":4,"put_bytes":509,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2517,"get_count":4,"put_bytes":513,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2521,"get_count":4,"put_bytes":517,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2525,"get_count":4,"put_bytes":521,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2529,"get_count":4,"put_bytes":525,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2533,"get_count":4,"put_bytes":530,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2538,"get_count":4,"put_bytes":534,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2542,"get_count":4,"put_bytes":643,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2651,"get_count":5,"put_bytes":647,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2655,"get_count":5,"put_bytes":651,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2659,"get_count":5,"put_bytes":655,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2663,"get_count":5,"put_bytes":659,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2667,"get_count":5,"put_bytes":663,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2671,"get_count":5,"put_bytes":668,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2676,"get_count":5,"put_bytes":672,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2536,"get_count":4,"put_bytes":569,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2577,"get_count":4,"put_bytes":573,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2581,"get_count":4,"put_bytes":577,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2585,"get_count":4,"put_bytes":581,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2589,"get_count":4,"put_bytes":585,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2593,"get_count":4,"put_bytes":589,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2597,"get_count":4,"put_bytes":594,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2602,"get_count":4,"put_bytes":598,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2606,"get_count":4,"put_bytes":707,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":711,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2719,"get_count":5,"put_bytes":715,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2723,"get_count":5,"put_bytes":719,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2727,"get_count":5,"put_bytes":723,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2731,"get_count":5,"put_bytes":727,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2735,"get_count":5,"put_bytes":732,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2740,"get_count":5,"put_bytes":736,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2600,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":378,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2386,"get_count":4,"put_bytes":382,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":390,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2398,"get_count":4,"put_bytes":394,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2402,"get_count":4,"put_bytes":399,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2407,"get_count":4,"put_bytes":403,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2411,"get_count":4,"put_bytes":513,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2521,"get_count":5,"put_bytes":517,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2525,"get_count":5,"put_bytes":521,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2529,"get_count":5,"put_bytes":525,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2533,"get_count":5,"put_bytes":529,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2537,"get_count":5,"put_bytes":533,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2541,"get_count":5,"put_bytes":538,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2546,"get_count":5,"put_bytes":542,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2550,"get_count":5,"put_bytes":506,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2514,"get_count":5,"put_bytes":510,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2518,"get_count":5,"put_bytes":514,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":518,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2271,"get_count":3,"put_bytes":414,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2422,"get_count":4,"put_bytes":418,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2426,"get_count":4,"put_bytes":422,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":426,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2434,"get_count":4,"put_bytes":430,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2438,"get_count":4,"put_bytes":434,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2442,"get_count":4,"put_bytes":439,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2447,"get_count":4,"put_bytes":443,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2451,"get_count":4,"put_bytes":615,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2623,"get_count":5,"put_bytes":619,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2627,"get_count":5,"put_bytes":623,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2631,"get_count":5,"put_bytes":627,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2635,"get_count":5,"put_bytes":631,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2639,"get_count":5,"put_bytes":635,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2643,"get_count":5,"put_bytes":640,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2648,"get_count":5,"put_bytes":644,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2652,"get_count":5,"put_bytes":608,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":620,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":628,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2636,"get_count":5,"put_bytes":633,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2641,"get_count":5,"put_bytes":637,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2645,"get_count":5,"put_bytes":746,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2754,"get_count":6,"put_bytes":750,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2758,"get_count":6,"put_bytes":754,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2762,"get_count":6,"put_bytes":758,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2766,"get_count":6,"put_bytes":762,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2770,"get_count":6,"put_bytes":766,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2774,"get_count":6,"put_bytes":771,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2779,"get_count":6,"put_bytes":775,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2639,"get_count":5,"put_bytes":672,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2680,"get_count":5,"put_bytes":676,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2684,"get_count":5,"put_bytes":680,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2688,"get_count":5,"put_bytes":684,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2692,"get_count":5,"put_bytes":688,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2696,"get_count":5,"put_bytes":692,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2700,"get_count":5,"put_bytes":697,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":810,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2818,"get_count":6,"put_bytes":814,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2822,"get_count":6,"put_bytes":818,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2826,"get_count":6,"put_bytes":822,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2830,"get_count":6,"put_bytes":826,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2834,"get_count":6,"put_bytes":830,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2838,"get_count":6,"put_bytes":835,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2843,"get_count":6,"put_bytes":839,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":736,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2744,"get_count":5,"put_bytes":740,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2748,"get_count":5,"put_bytes":744,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2752,"get_count":5,"put_bytes":748,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2756,"get_count":5,"put_bytes":752,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2760,"get_count":5,"put_bytes":756,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2764,"get_count":5,"put_bytes":761,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2769,"get_count":5,"put_bytes":765,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2773,"get_count":5,"put_bytes":874,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2882,"get_count":6,"put_bytes":878,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2886,"get_count":6,"put_bytes":882,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2890,"get_count":6,"put_bytes":886,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2894,"get_count":6,"put_bytes":890,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2898,"get_count":6,"put_bytes":894,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2902,"get_count":6,"put_bytes":899,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2907,"get_count":6,"put_bytes":903,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2767,"get_count":5,"put_bytes":800,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2808,"get_count":5,"put_bytes":804,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2812,"get_count":5,"put_bytes":808,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2816,"get_count":5,"put_bytes":812,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2820,"get_count":5,"put_bytes":816,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2824,"get_count":5,"put_bytes":820,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2828,"get_count":5,"put_bytes":825,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2833,"get_count":5,"put_bytes":829,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2837,"get_count":5,"put_bytes":938,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":2946,"get_count":6,"put_bytes":942,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":2950,"get_count":6,"put_bytes":946,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":2954,"get_count":6,"put_bytes":950,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":2958,"get_count":6,"put_bytes":954,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":2962,"get_count":6,"put_bytes":958,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":2966,"get_count":6,"put_bytes":963,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":2971,"get_count":6,"put_bytes":967,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2831,"get_count":5,"put_bytes":864,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2872,"get_count":5,"put_bytes":868,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2876,"get_count":5,"put_bytes":872,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2880,"get_count":5,"put_bytes":876,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2884,"get_count":5,"put_bytes":880,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2888,"get_count":5,"put_bytes":884,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2892,"get_count":5,"put_bytes":889,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2897,"get_count":5,"put_bytes":893,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2901,"get_count":5,"put_bytes":1002,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":3010,"get_count":6,"put_bytes":1006,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":3014,"get_count":6,"put_bytes":1010,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":3018,"get_count":6,"put_bytes":1014,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":3022,"get_count":6,"put_bytes":1018,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":3026,"get_count":6,"put_bytes":1022,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":3030,"get_count":6,"put_bytes":1027,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":3035,"get_count":6,"put_bytes":1031,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2895,"get_count":5,"put_bytes":928,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":2936,"get_count":5,"put_bytes":932,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":2940,"get_count":5,"put_bytes":936,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":2944,"get_count":5,"put_bytes":940,"put_count":4}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png index 1cba716bdab8634aa86498af6dca639b96d902d7..58d8b6e8799cfea7dcdc15fed82e43b48f0a2ffa 100644 GIT binary patch literal 17917 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 Ji!~mH{XaMvj^6+P literal 17977 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

XJ6dibRVXMZY?Q+s$?7fN81yGh{X^Z;w z*u12}235Y{oY>DX;+iYd(|5$!4e};#>F+Kcq(I>7k}$1*5R3jpTS)6Z?d1kI79cBh zwPv9FCQrr6U>*eG)oL=(3UMEUFRqw>s13wcY48 zJ9G7aa1oN8r6^-pA}aRWF5wlVvX;1gS}YGAI*}Na5kpC*NMiS7&ta#pV0^dGKu`LC6L%HL@h1wC7#{Bzpi{EFYN#ZW5G5y`~b*O*@aYPi2mNsZX8eO zuK1`{+xaH*5ubGw{+lnF$^8eK?*4*3lAECv+v!O)sKoJfpc1k8HT;8-WwjGUuy2-wGj#tolpLQ@-o2ro`(d)2VNpGoahi?Zb z+F4_VFbe+KQ1Kl~Z*eZya6iST@^8(>Cr+$F>%7=yPH&X?H?)7d6epYJe}(b?C~6sN zL!6BtP2_-Q9^+NAq^QXVr%2R$+PwRx?*J?hh|Z7e(PxGB8^+vyjRw8u)P^NH!B3(~+bN>apzbG{ zW)AcI1H;^2+(S_h0RjNdxBaoo(~Vo0;_v_g0dDGHO#E_l$)q0?PEv;@Ycl>TcNeh`NU;2 zA9H1OfLkszWp&uI_$SX>b{!*-38#+n9WPzD$ObBRsvnF@UIVhzC?^^CNU>L9mi8I; zcF(>Wm=3B=G0vpHX0A^Mhy{gkj@)f#u^Dul_8MYc%?N7^TzDQBv8b$H3>72nqrLzP zmT)H^Ur7mLoJfGtU?=JcyVr*$CjWhRw!f~$$6y%&U^Tyah)V3$t4oWILYE^h2{9pN z=m^SPHLj)# z<43W29G%-Kaut*fblXJ8eG3m|I z`l!g^x;HlgHWl6=Y$E7dQ=c?7Bt4^pMV37}qdu0Y>2V7`Ij~K8^fJ&FfXi^;W^q-F z@9aPAG$eE%3;{d>Qlj;^n}Ob&G%=L;f8M`s=QW+_Gb9qEASxl#NXPN?q2Bbs`U3z9 zhV{O%4Z&~(k0LEDw#)9e?nYDY-r!S-w{gxtfyS}a)YhfO9iYtk;nFo~^1VMO8A940 zcUT@H)2>x$2{@p6iDfZgzvd~yDsC#kZZYAKD3n?W2<6zD2u5*V$HXlvV?wf0h|=>9hB=&={Iw~F;8)6UID&^eRNIba}4zIY+GUEa=+zXtPlD! zB8WNGXJO-z3VUKS*T9pZD!8T8h3cMw@*dRm98+`5adT5Ce7`J3@o-ir_$Z&m)su~{EO|g zC-QLJ?6`&E<#~k1XF8{IC<|U_5mve zb`&Qv4~tv@NqFv_L`0$-?ABWa90E9b$fRB%$HeiW*|q={<8CNb5VUUT2|;rJG>1F0 zZyH{rFQZa>yNnQrHkgjbUFc_T0iI*y!?HKhAGHAwdrm;cE%Or zqdSA#;zGR>$dYgEGIDA>)Y(bLYXwJ@Vv8DN4M9eAuh1zUjkwk?#N9WAr6(z{N|u;O zP!g)}=Ac?;%9{$Hz%XLEnUatX1qGF0Y`xrwYUeL9%1Vx0nWis_I_yHR%0w`eA*Rw#LWg5+`MVE8{ zCN@PHNvw0@Y@)<#7znXVc{UAC326 z%2m^?)n*v?<5gOJPpd(BGlZwS-C{F&L)LffnJO56u)1_^2Taf3eGNVtHypTUGZb~5 zV6gV&l=ds7Bf&difQ3x)h=GY<+B@xT!)s)^6H|MD3*NTy`7r~lcPKqP&oaH1R{Ddc zd-KOr*-D-eMrv{_8{+J^yekqdP&(hVxlASf@V*9&U+;v4m0V#ueImA{^{PD|BU+sP znC6w{FE$1oxO((`+JnVVw;hXc+>3)Q16aI-%f}-8uMdedxi~?UG;0M?)?H|U=?gWO z8=%!Rvpm7?0E*%eg$X&(05jHAX+#T*{KsQDS^%QMy42HyRHvhB#`S3;?TIRol@EKv z{{~14JX9jr$|^JWUrhe8*fZ>@P774o(LzFE189@KD2b}@JJAAe_fFHdb`tmdjyD>7 zh2Mrg`|DOtG^ej%U}x?TH*@3og#7Wr*`JgDYw*ji4Lc^u{&PG1f!eV?GZ6PYqyjow z`8O#e7x?-E4jJ|)`Y-GZV!(Dl062_{#t3+>Qw4_tqA*^Rm_y|?7cRh1Dlr(HKNvI^ zi3CnZ;B9sa4%eTr))2f2PD{Xl&7g>j{Wc9FYm7heD;PqtzZbw7L|mx;KUMty$;&Fz zYG?z?y_uYpzBmBL{tE#oi|5Y;1{p~6WhiDe#D zu5;Ql_42t4O$MKBCfmTpkbj8{8lRrg@UYOK@5Gbu?&Co@r4OltsDtaf%s?@f!r(y&BF=}DVhpKl6v)P# zW*lj;fb=^?i-;XbS8+fwR0q0zW4?Etyf90n1~&uWwsm)y)&3>P{uc}hCaY92=1&Pk zHax-H(G7oQ?X7;W+ixt{%M*pTF(A%b^Y(IT+WkxZ!G8gvMT3T1WT8^i%e*NrWW(&K zMu01#$-BloyAp1brd>DjOLfR&vd*~Ya&s;zWnl1MAhdqUApbu>E~k&5xl0W%%W4` zmKpGGfx2ZPf3UH_V6GT4BL;i)We_yH2i3d=0b@NP4c9Uv~ zYFjnz^0bM+D~{e1WI>RM8$jmv4p?<>oqhI}cXhLU)UxAez<gM12eT4w>3Z=3i{pF6G-Mg~ zVTn~HxHXQ}{~d+CP5a+b=tf26y;R!&T@dKmg#D!_DMw5a4fPWVw zPkP^0SIVljEVHk-iEp}8KD_<-52@*PjuJ{uJj+a= znV;`;_2`I&`|9`j*MxJ)6N;H-h){2`$oX{$3CCdN57Q~GGStRF{f$Bu(MoI>*`dj{ z5zmpFufNGEa?gIWCZby>ZF2#qC-7k)ndWa5(aBjBy7Y*%OXTzUl`^7oPMcqbHsfZ{ zfV-7_gBebiy!GOamF=%7PV%Hx+#gEznk6k&vHCA`3}&2QD#Z!^7wqmoAGa`9IIcs2 X`(IPWf#10T0;%7Bq*|u@B;@}BZYPL@ diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 90dec0a02..2248ccab3 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":4009,"put_count":14}} -{"i":1,"series":1,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":1866,"put_count":10}} -{"i":2,"series":1,"stats":{"get_bytes":3010,"get_count":5,"put_bytes":2373,"put_count":11}} -{"i":3,"series":1,"stats":{"get_bytes":3517,"get_count":6,"put_bytes":2775,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":3775,"get_count":5,"put_bytes":3070,"put_count":10}} -{"i":5,"series":1,"stats":{"get_bytes":4214,"get_count":5,"put_bytes":3471,"put_count":10}} -{"i":6,"series":1,"stats":{"get_bytes":2559,"get_count":4,"put_bytes":1988,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":3132,"get_count":6,"put_bytes":2390,"put_count":11}} -{"i":8,"series":1,"stats":{"get_bytes":3390,"get_count":5,"put_bytes":2684,"put_count":10}} -{"i":9,"series":1,"stats":{"get_bytes":3828,"get_count":5,"put_bytes":3087,"put_count":10}} -{"i":10,"series":1,"stats":{"get_bytes":4231,"get_count":5,"put_bytes":3592,"put_count":11}} -{"i":11,"series":1,"stats":{"get_bytes":4736,"get_count":6,"put_bytes":4061,"put_count":12}} -{"i":12,"series":1,"stats":{"get_bytes":3005,"get_count":5,"put_bytes":2299,"put_count":10}} -{"i":13,"series":1,"stats":{"get_bytes":3443,"get_count":5,"put_bytes":2701,"put_count":10}} -{"i":14,"series":1,"stats":{"get_bytes":3845,"get_count":5,"put_bytes":3208,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":4352,"get_count":6,"put_bytes":3609,"put_count":11}} -{"i":16,"series":1,"stats":{"get_bytes":4609,"get_count":5,"put_bytes":3970,"put_count":11}} -{"i":17,"series":1,"stats":{"get_bytes":3058,"get_count":5,"put_bytes":2316,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":3460,"get_count":5,"put_bytes":2822,"put_count":11}} -{"i":19,"series":1,"stats":{"get_bytes":3966,"get_count":6,"put_bytes":3224,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":4224,"get_count":5,"put_bytes":3518,"put_count":10}} -{"i":21,"series":1,"stats":{"get_bytes":4662,"get_count":5,"put_bytes":3988,"put_count":11}} -{"i":22,"series":1,"stats":{"get_bytes":3076,"get_count":5,"put_bytes":2438,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":3582,"get_count":6,"put_bytes":2840,"put_count":11}} -{"i":24,"series":1,"stats":{"get_bytes":3840,"get_count":5,"put_bytes":3134,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":4278,"get_count":5,"put_bytes":3536,"put_count":10}} -{"i":26,"series":1,"stats":{"get_bytes":4680,"get_count":5,"put_bytes":4109,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":3197,"get_count":6,"put_bytes":2455,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":3455,"get_count":5,"put_bytes":2749,"put_count":10}} -{"i":29,"series":1,"stats":{"get_bytes":3893,"get_count":5,"put_bytes":3150,"put_count":10}} -{"i":30,"series":1,"stats":{"get_bytes":4294,"get_count":5,"put_bytes":3657,"put_count":11}} -{"i":31,"series":1,"stats":{"get_bytes":4801,"get_count":6,"put_bytes":4126,"put_count":12}} -{"i":32,"series":1,"stats":{"get_bytes":3070,"get_count":5,"put_bytes":2364,"put_count":10}} -{"i":33,"series":1,"stats":{"get_bytes":3508,"get_count":5,"put_bytes":2765,"put_count":10}} -{"i":34,"series":1,"stats":{"get_bytes":3909,"get_count":5,"put_bytes":3271,"put_count":11}} -{"i":35,"series":1,"stats":{"get_bytes":4415,"get_count":6,"put_bytes":3674,"put_count":11}} -{"i":36,"series":1,"stats":{"get_bytes":4674,"get_count":5,"put_bytes":4035,"put_count":11}} -{"i":37,"series":1,"stats":{"get_bytes":3123,"get_count":5,"put_bytes":2381,"put_count":10}} -{"i":38,"series":1,"stats":{"get_bytes":3525,"get_count":5,"put_bytes":2886,"put_count":11}} -{"i":39,"series":1,"stats":{"get_bytes":4030,"get_count":6,"put_bytes":3288,"put_count":11}} -{"i":40,"series":1,"stats":{"get_bytes":4288,"get_count":5,"put_bytes":3583,"put_count":10}} -{"i":41,"series":1,"stats":{"get_bytes":4727,"get_count":5,"put_bytes":4052,"put_count":11}} -{"i":42,"series":1,"stats":{"get_bytes":3140,"get_count":5,"put_bytes":2501,"put_count":11}} -{"i":43,"series":1,"stats":{"get_bytes":3645,"get_count":6,"put_bytes":2903,"put_count":11}} -{"i":44,"series":1,"stats":{"get_bytes":3903,"get_count":5,"put_bytes":3197,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":4341,"get_count":5,"put_bytes":3600,"put_count":10}} -{"i":46,"series":1,"stats":{"get_bytes":4744,"get_count":5,"put_bytes":4106,"put_count":11}} -{"i":47,"series":1,"stats":{"get_bytes":3194,"get_count":5,"put_bytes":2518,"put_count":11}} -{"i":48,"series":1,"stats":{"get_bytes":3518,"get_count":5,"put_bytes":2812,"put_count":10}} -{"i":49,"series":1,"stats":{"get_bytes":3956,"get_count":5,"put_bytes":3214,"put_count":10}} -{"i":50,"series":1,"stats":{"get_bytes":4358,"get_count":5,"put_bytes":3721,"put_count":11}} -{"i":51,"series":1,"stats":{"get_bytes":4865,"get_count":6,"put_bytes":4122,"put_count":11}} -{"i":52,"series":1,"stats":{"get_bytes":3066,"get_count":4,"put_bytes":2427,"put_count":10}} -{"i":53,"series":1,"stats":{"get_bytes":3571,"get_count":5,"put_bytes":2829,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":3973,"get_count":5,"put_bytes":3335,"put_count":11}} -{"i":55,"series":1,"stats":{"get_bytes":4479,"get_count":6,"put_bytes":3738,"put_count":11}} -{"i":56,"series":1,"stats":{"get_bytes":4738,"get_count":5,"put_bytes":4031,"put_count":10}} -{"i":57,"series":1,"stats":{"get_bytes":5175,"get_count":5,"put_bytes":4500,"put_count":11}} -{"i":58,"series":1,"stats":{"get_bytes":3588,"get_count":5,"put_bytes":2950,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":4094,"get_count":6,"put_bytes":3352,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":4352,"get_count":5,"put_bytes":3647,"put_count":10}} -{"i":61,"series":1,"stats":{"get_bytes":4791,"get_count":5,"put_bytes":4048,"put_count":10}} -{"i":62,"series":1,"stats":{"get_bytes":5192,"get_count":5,"put_bytes":4690,"put_count":13}} -{"i":63,"series":1,"stats":{"get_bytes":2939,"get_count":6,"put_bytes":2197,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":3197,"get_count":5,"put_bytes":2491,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":3635,"get_count":5,"put_bytes":2893,"put_count":10}} -{"i":66,"series":1,"stats":{"get_bytes":4037,"get_count":5,"put_bytes":3399,"put_count":11}} -{"i":67,"series":1,"stats":{"get_bytes":4543,"get_count":6,"put_bytes":3935,"put_count":13}} -{"i":68,"series":1,"stats":{"get_bytes":2879,"get_count":6,"put_bytes":2173,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":3317,"get_count":6,"put_bytes":2575,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":3719,"get_count":6,"put_bytes":3080,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":4224,"get_count":7,"put_bytes":3483,"put_count":12}} -{"i":72,"series":1,"stats":{"get_bytes":4483,"get_count":6,"put_bytes":3844,"put_count":12}} -{"i":73,"series":1,"stats":{"get_bytes":2932,"get_count":6,"put_bytes":2190,"put_count":11}} -{"i":74,"series":1,"stats":{"get_bytes":3334,"get_count":6,"put_bytes":2695,"put_count":12}} -{"i":75,"series":1,"stats":{"get_bytes":3839,"get_count":7,"put_bytes":3097,"put_count":12}} -{"i":76,"series":1,"stats":{"get_bytes":4097,"get_count":6,"put_bytes":3392,"put_count":11}} -{"i":77,"series":1,"stats":{"get_bytes":4536,"get_count":6,"put_bytes":3861,"put_count":12}} -{"i":78,"series":1,"stats":{"get_bytes":2949,"get_count":6,"put_bytes":2311,"put_count":12}} -{"i":79,"series":1,"stats":{"get_bytes":3455,"get_count":7,"put_bytes":2712,"put_count":12}} -{"i":80,"series":1,"stats":{"get_bytes":3712,"get_count":6,"put_bytes":3006,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":4150,"get_count":6,"put_bytes":3409,"put_count":11}} -{"i":82,"series":1,"stats":{"get_bytes":4553,"get_count":6,"put_bytes":3982,"put_count":13}} -{"i":83,"series":1,"stats":{"get_bytes":3070,"get_count":7,"put_bytes":2327,"put_count":12}} -{"i":84,"series":1,"stats":{"get_bytes":3327,"get_count":6,"put_bytes":2621,"put_count":11}} -{"i":85,"series":1,"stats":{"get_bytes":3765,"get_count":6,"put_bytes":3023,"put_count":11}} -{"i":86,"series":1,"stats":{"get_bytes":4167,"get_count":6,"put_bytes":3530,"put_count":12}} -{"i":87,"series":1,"stats":{"get_bytes":4674,"get_count":7,"put_bytes":3932,"put_count":12}} -{"i":88,"series":1,"stats":{"get_bytes":3020,"get_count":6,"put_bytes":2236,"put_count":11}} -{"i":89,"series":1,"stats":{"get_bytes":3380,"get_count":6,"put_bytes":2638,"put_count":11}} -{"i":90,"series":1,"stats":{"get_bytes":3782,"get_count":6,"put_bytes":3144,"put_count":12}} -{"i":91,"series":1,"stats":{"get_bytes":4288,"get_count":7,"put_bytes":3547,"put_count":12}} -{"i":92,"series":1,"stats":{"get_bytes":4547,"get_count":6,"put_bytes":3840,"put_count":11}} -{"i":93,"series":1,"stats":{"get_bytes":2928,"get_count":5,"put_bytes":2253,"put_count":11}} -{"i":94,"series":1,"stats":{"get_bytes":3397,"get_count":6,"put_bytes":2759,"put_count":12}} -{"i":95,"series":1,"stats":{"get_bytes":3903,"get_count":7,"put_bytes":3161,"put_count":12}} -{"i":96,"series":1,"stats":{"get_bytes":4161,"get_count":6,"put_bytes":3456,"put_count":11}} -{"i":97,"series":1,"stats":{"get_bytes":4600,"get_count":6,"put_bytes":3857,"put_count":11}} -{"i":98,"series":1,"stats":{"get_bytes":5001,"get_count":6,"put_bytes":4430,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":3518,"get_count":7,"put_bytes":2776,"put_count":12}} -{"i":0,"series":2,"stats":{"get_bytes":2355,"get_count":3,"put_bytes":1888,"put_count":10}} -{"i":1,"series":2,"stats":{"get_bytes":3032,"get_count":5,"put_bytes":2290,"put_count":10}} -{"i":2,"series":2,"stats":{"get_bytes":3434,"get_count":5,"put_bytes":2797,"put_count":11}} -{"i":3,"series":2,"stats":{"get_bytes":3941,"get_count":6,"put_bytes":3199,"put_count":11}} -{"i":4,"series":2,"stats":{"get_bytes":4199,"get_count":5,"put_bytes":2019,"put_count":11}} -{"i":5,"series":2,"stats":{"get_bytes":3163,"get_count":6,"put_bytes":2421,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":3565,"get_count":6,"put_bytes":2927,"put_count":12}} -{"i":7,"series":2,"stats":{"get_bytes":4071,"get_count":7,"put_bytes":3330,"put_count":12}} -{"i":8,"series":2,"stats":{"get_bytes":4330,"get_count":6,"put_bytes":3623,"put_count":11}} -{"i":9,"series":2,"stats":{"get_bytes":4767,"get_count":6,"put_bytes":4092,"put_count":12}} -{"i":10,"series":2,"stats":{"get_bytes":3180,"get_count":6,"put_bytes":2542,"put_count":12}} -{"i":11,"series":2,"stats":{"get_bytes":3686,"get_count":7,"put_bytes":2944,"put_count":12}} -{"i":12,"series":2,"stats":{"get_bytes":3944,"get_count":6,"put_bytes":3239,"put_count":11}} -{"i":13,"series":2,"stats":{"get_bytes":4383,"get_count":6,"put_bytes":3640,"put_count":11}} -{"i":14,"series":2,"stats":{"get_bytes":4784,"get_count":6,"put_bytes":4213,"put_count":13}} -{"i":15,"series":2,"stats":{"get_bytes":3301,"get_count":7,"put_bytes":2559,"put_count":12}} -{"i":16,"series":2,"stats":{"get_bytes":3559,"get_count":6,"put_bytes":2853,"put_count":11}} -{"i":17,"series":2,"stats":{"get_bytes":3997,"get_count":6,"put_bytes":3255,"put_count":11}} -{"i":18,"series":2,"stats":{"get_bytes":4399,"get_count":6,"put_bytes":3761,"put_count":12}} -{"i":19,"series":2,"stats":{"get_bytes":4905,"get_count":7,"put_bytes":4230,"put_count":13}} -{"i":20,"series":2,"stats":{"get_bytes":3174,"get_count":6,"put_bytes":2468,"put_count":11}} -{"i":21,"series":2,"stats":{"get_bytes":3612,"get_count":6,"put_bytes":2870,"put_count":11}} -{"i":22,"series":2,"stats":{"get_bytes":4014,"get_count":6,"put_bytes":3375,"put_count":12}} -{"i":23,"series":2,"stats":{"get_bytes":4519,"get_count":7,"put_bytes":3778,"put_count":12}} -{"i":24,"series":2,"stats":{"get_bytes":4778,"get_count":6,"put_bytes":4140,"put_count":12}} -{"i":25,"series":2,"stats":{"get_bytes":3228,"get_count":6,"put_bytes":2486,"put_count":11}} -{"i":26,"series":2,"stats":{"get_bytes":3630,"get_count":6,"put_bytes":2991,"put_count":12}} -{"i":27,"series":2,"stats":{"get_bytes":4135,"get_count":7,"put_bytes":3393,"put_count":12}} -{"i":28,"series":2,"stats":{"get_bytes":4393,"get_count":6,"put_bytes":3688,"put_count":11}} -{"i":29,"series":2,"stats":{"get_bytes":4832,"get_count":6,"put_bytes":4157,"put_count":12}} -{"i":30,"series":2,"stats":{"get_bytes":3245,"get_count":6,"put_bytes":2607,"put_count":12}} -{"i":31,"series":2,"stats":{"get_bytes":3751,"get_count":7,"put_bytes":3008,"put_count":12}} -{"i":32,"series":2,"stats":{"get_bytes":4008,"get_count":6,"put_bytes":3302,"put_count":11}} -{"i":33,"series":2,"stats":{"get_bytes":4446,"get_count":6,"put_bytes":3705,"put_count":11}} -{"i":34,"series":2,"stats":{"get_bytes":4849,"get_count":6,"put_bytes":4278,"put_count":13}} -{"i":35,"series":2,"stats":{"get_bytes":3366,"get_count":7,"put_bytes":2623,"put_count":12}} -{"i":36,"series":2,"stats":{"get_bytes":3623,"get_count":6,"put_bytes":2917,"put_count":11}} -{"i":37,"series":2,"stats":{"get_bytes":4061,"get_count":6,"put_bytes":3319,"put_count":11}} -{"i":38,"series":2,"stats":{"get_bytes":4463,"get_count":6,"put_bytes":3826,"put_count":12}} -{"i":39,"series":2,"stats":{"get_bytes":4970,"get_count":7,"put_bytes":4228,"put_count":12}} -{"i":40,"series":2,"stats":{"get_bytes":3316,"get_count":6,"put_bytes":2532,"put_count":11}} -{"i":41,"series":2,"stats":{"get_bytes":3676,"get_count":6,"put_bytes":2934,"put_count":11}} -{"i":42,"series":2,"stats":{"get_bytes":4078,"get_count":6,"put_bytes":3440,"put_count":12}} -{"i":43,"series":2,"stats":{"get_bytes":4584,"get_count":7,"put_bytes":3843,"put_count":12}} -{"i":44,"series":2,"stats":{"get_bytes":4843,"get_count":6,"put_bytes":4136,"put_count":11}} -{"i":45,"series":2,"stats":{"get_bytes":3224,"get_count":5,"put_bytes":2549,"put_count":11}} -{"i":46,"series":2,"stats":{"get_bytes":3693,"get_count":6,"put_bytes":3055,"put_count":12}} -{"i":47,"series":2,"stats":{"get_bytes":4199,"get_count":7,"put_bytes":3457,"put_count":12}} -{"i":48,"series":2,"stats":{"get_bytes":4457,"get_count":6,"put_bytes":3752,"put_count":11}} -{"i":49,"series":2,"stats":{"get_bytes":4896,"get_count":6,"put_bytes":4153,"put_count":11}} -{"i":50,"series":2,"stats":{"get_bytes":5297,"get_count":6,"put_bytes":4726,"put_count":13}} -{"i":51,"series":2,"stats":{"get_bytes":3814,"get_count":7,"put_bytes":3072,"put_count":12}} -{"i":52,"series":2,"stats":{"get_bytes":4072,"get_count":6,"put_bytes":3366,"put_count":11}} -{"i":53,"series":2,"stats":{"get_bytes":4510,"get_count":6,"put_bytes":3769,"put_count":11}} -{"i":54,"series":2,"stats":{"get_bytes":4913,"get_count":6,"put_bytes":4274,"put_count":12}} -{"i":55,"series":2,"stats":{"get_bytes":5418,"get_count":7,"put_bytes":4743,"put_count":13}} -{"i":56,"series":2,"stats":{"get_bytes":3687,"get_count":6,"put_bytes":2981,"put_count":11}} -{"i":57,"series":2,"stats":{"get_bytes":4125,"get_count":6,"put_bytes":3383,"put_count":11}} -{"i":58,"series":2,"stats":{"get_bytes":4527,"get_count":6,"put_bytes":3889,"put_count":12}} -{"i":59,"series":2,"stats":{"get_bytes":5033,"get_count":7,"put_bytes":4291,"put_count":12}} -{"i":60,"series":2,"stats":{"get_bytes":5291,"get_count":6,"put_bytes":4652,"put_count":12}} -{"i":61,"series":2,"stats":{"get_bytes":3740,"get_count":6,"put_bytes":2998,"put_count":11}} -{"i":62,"series":2,"stats":{"get_bytes":4142,"get_count":6,"put_bytes":3504,"put_count":12}} -{"i":63,"series":2,"stats":{"get_bytes":4648,"get_count":7,"put_bytes":3905,"put_count":12}} -{"i":64,"series":2,"stats":{"get_bytes":4905,"get_count":6,"put_bytes":4200,"put_count":11}} -{"i":65,"series":2,"stats":{"get_bytes":5344,"get_count":6,"put_bytes":4670,"put_count":12}} -{"i":66,"series":2,"stats":{"get_bytes":3758,"get_count":6,"put_bytes":3120,"put_count":12}} -{"i":67,"series":2,"stats":{"get_bytes":4264,"get_count":7,"put_bytes":3521,"put_count":12}} -{"i":68,"series":2,"stats":{"get_bytes":4521,"get_count":6,"put_bytes":3815,"put_count":11}} -{"i":69,"series":2,"stats":{"get_bytes":4959,"get_count":6,"put_bytes":4218,"put_count":11}} -{"i":70,"series":2,"stats":{"get_bytes":5362,"get_count":6,"put_bytes":4791,"put_count":13}} -{"i":71,"series":2,"stats":{"get_bytes":3879,"get_count":7,"put_bytes":3137,"put_count":12}} -{"i":72,"series":2,"stats":{"get_bytes":4137,"get_count":6,"put_bytes":3430,"put_count":11}} -{"i":73,"series":2,"stats":{"get_bytes":4574,"get_count":6,"put_bytes":3832,"put_count":11}} -{"i":74,"series":2,"stats":{"get_bytes":4976,"get_count":6,"put_bytes":4339,"put_count":12}} -{"i":75,"series":2,"stats":{"get_bytes":5483,"get_count":7,"put_bytes":4808,"put_count":13}} -{"i":76,"series":2,"stats":{"get_bytes":3752,"get_count":6,"put_bytes":3046,"put_count":11}} -{"i":77,"series":2,"stats":{"get_bytes":4190,"get_count":6,"put_bytes":3447,"put_count":11}} -{"i":78,"series":2,"stats":{"get_bytes":4591,"get_count":6,"put_bytes":3953,"put_count":12}} -{"i":79,"series":2,"stats":{"get_bytes":5097,"get_count":7,"put_bytes":4356,"put_count":12}} -{"i":80,"series":2,"stats":{"get_bytes":5356,"get_count":6,"put_bytes":4717,"put_count":12}} -{"i":81,"series":2,"stats":{"get_bytes":3805,"get_count":6,"put_bytes":3062,"put_count":11}} -{"i":82,"series":2,"stats":{"get_bytes":4206,"get_count":6,"put_bytes":3568,"put_count":12}} -{"i":83,"series":2,"stats":{"get_bytes":4712,"get_count":7,"put_bytes":3970,"put_count":12}} -{"i":84,"series":2,"stats":{"get_bytes":4970,"get_count":6,"put_bytes":4265,"put_count":11}} -{"i":85,"series":2,"stats":{"get_bytes":5409,"get_count":6,"put_bytes":4666,"put_count":11}} -{"i":86,"series":2,"stats":{"get_bytes":3754,"get_count":5,"put_bytes":3183,"put_count":12}} -{"i":87,"series":2,"stats":{"get_bytes":4327,"get_count":7,"put_bytes":3585,"put_count":12}} -{"i":88,"series":2,"stats":{"get_bytes":4585,"get_count":6,"put_bytes":3879,"put_count":11}} -{"i":89,"series":2,"stats":{"get_bytes":5023,"get_count":6,"put_bytes":4282,"put_count":11}} -{"i":90,"series":2,"stats":{"get_bytes":5426,"get_count":6,"put_bytes":4787,"put_count":12}} -{"i":91,"series":2,"stats":{"get_bytes":5931,"get_count":7,"put_bytes":5256,"put_count":13}} -{"i":92,"series":2,"stats":{"get_bytes":4200,"get_count":6,"put_bytes":3494,"put_count":11}} -{"i":93,"series":2,"stats":{"get_bytes":4638,"get_count":6,"put_bytes":3896,"put_count":11}} -{"i":94,"series":2,"stats":{"get_bytes":5040,"get_count":6,"put_bytes":4403,"put_count":12}} -{"i":95,"series":2,"stats":{"get_bytes":5547,"get_count":7,"put_bytes":4804,"put_count":12}} -{"i":96,"series":2,"stats":{"get_bytes":5804,"get_count":6,"put_bytes":5165,"put_count":12}} -{"i":97,"series":2,"stats":{"get_bytes":4253,"get_count":6,"put_bytes":3511,"put_count":11}} -{"i":98,"series":2,"stats":{"get_bytes":4655,"get_count":6,"put_bytes":4017,"put_count":12}} -{"i":99,"series":2,"stats":{"get_bytes":5161,"get_count":7,"put_bytes":4419,"put_count":12}} +{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":3843,"put_count":14}} +{"i":1,"series":1,"stats":{"get_bytes":2526,"get_count":5,"put_bytes":1783,"put_count":10}} +{"i":2,"series":1,"stats":{"get_bytes":2927,"get_count":5,"put_bytes":2290,"put_count":11}} +{"i":3,"series":1,"stats":{"get_bytes":3434,"get_count":6,"put_bytes":2692,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":3692,"get_count":5,"put_bytes":2987,"put_count":10}} +{"i":5,"series":1,"stats":{"get_bytes":4131,"get_count":5,"put_bytes":3388,"put_count":10}} +{"i":6,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":1905,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":3049,"get_count":6,"put_bytes":2307,"put_count":11}} +{"i":8,"series":1,"stats":{"get_bytes":3307,"get_count":5,"put_bytes":2601,"put_count":10}} +{"i":9,"series":1,"stats":{"get_bytes":3745,"get_count":5,"put_bytes":3004,"put_count":10}} +{"i":10,"series":1,"stats":{"get_bytes":4148,"get_count":5,"put_bytes":3509,"put_count":11}} +{"i":11,"series":1,"stats":{"get_bytes":4653,"get_count":6,"put_bytes":3978,"put_count":12}} +{"i":12,"series":1,"stats":{"get_bytes":2922,"get_count":5,"put_bytes":2216,"put_count":10}} +{"i":13,"series":1,"stats":{"get_bytes":3360,"get_count":5,"put_bytes":2618,"put_count":10}} +{"i":14,"series":1,"stats":{"get_bytes":3762,"get_count":5,"put_bytes":3125,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":4269,"get_count":6,"put_bytes":3526,"put_count":11}} +{"i":16,"series":1,"stats":{"get_bytes":4526,"get_count":5,"put_bytes":3887,"put_count":11}} +{"i":17,"series":1,"stats":{"get_bytes":2975,"get_count":5,"put_bytes":2233,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":3377,"get_count":5,"put_bytes":2739,"put_count":11}} +{"i":19,"series":1,"stats":{"get_bytes":3883,"get_count":6,"put_bytes":3141,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":4141,"get_count":5,"put_bytes":3435,"put_count":10}} +{"i":21,"series":1,"stats":{"get_bytes":4579,"get_count":5,"put_bytes":3905,"put_count":11}} +{"i":22,"series":1,"stats":{"get_bytes":2993,"get_count":5,"put_bytes":2355,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":3499,"get_count":6,"put_bytes":2757,"put_count":11}} +{"i":24,"series":1,"stats":{"get_bytes":3757,"get_count":5,"put_bytes":3051,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":4195,"get_count":5,"put_bytes":3453,"put_count":10}} +{"i":26,"series":1,"stats":{"get_bytes":4597,"get_count":5,"put_bytes":4026,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":3114,"get_count":6,"put_bytes":2372,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":3372,"get_count":5,"put_bytes":2666,"put_count":10}} +{"i":29,"series":1,"stats":{"get_bytes":3810,"get_count":5,"put_bytes":3067,"put_count":10}} +{"i":30,"series":1,"stats":{"get_bytes":4211,"get_count":5,"put_bytes":3574,"put_count":11}} +{"i":31,"series":1,"stats":{"get_bytes":4718,"get_count":6,"put_bytes":4043,"put_count":12}} +{"i":32,"series":1,"stats":{"get_bytes":2987,"get_count":5,"put_bytes":2281,"put_count":10}} +{"i":33,"series":1,"stats":{"get_bytes":3425,"get_count":5,"put_bytes":2682,"put_count":10}} +{"i":34,"series":1,"stats":{"get_bytes":3826,"get_count":5,"put_bytes":3188,"put_count":11}} +{"i":35,"series":1,"stats":{"get_bytes":4332,"get_count":6,"put_bytes":3591,"put_count":11}} +{"i":36,"series":1,"stats":{"get_bytes":4591,"get_count":5,"put_bytes":3952,"put_count":11}} +{"i":37,"series":1,"stats":{"get_bytes":3040,"get_count":5,"put_bytes":2298,"put_count":10}} +{"i":38,"series":1,"stats":{"get_bytes":3442,"get_count":5,"put_bytes":2803,"put_count":11}} +{"i":39,"series":1,"stats":{"get_bytes":3947,"get_count":6,"put_bytes":3205,"put_count":11}} +{"i":40,"series":1,"stats":{"get_bytes":4205,"get_count":5,"put_bytes":3500,"put_count":10}} +{"i":41,"series":1,"stats":{"get_bytes":4644,"get_count":5,"put_bytes":3969,"put_count":11}} +{"i":42,"series":1,"stats":{"get_bytes":3057,"get_count":5,"put_bytes":2418,"put_count":11}} +{"i":43,"series":1,"stats":{"get_bytes":3562,"get_count":6,"put_bytes":2820,"put_count":11}} +{"i":44,"series":1,"stats":{"get_bytes":3820,"get_count":5,"put_bytes":3114,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":4258,"get_count":5,"put_bytes":3517,"put_count":10}} +{"i":46,"series":1,"stats":{"get_bytes":4661,"get_count":5,"put_bytes":4023,"put_count":11}} +{"i":47,"series":1,"stats":{"get_bytes":3111,"get_count":5,"put_bytes":2435,"put_count":11}} +{"i":48,"series":1,"stats":{"get_bytes":3435,"get_count":5,"put_bytes":2729,"put_count":10}} +{"i":49,"series":1,"stats":{"get_bytes":3873,"get_count":5,"put_bytes":3131,"put_count":10}} +{"i":50,"series":1,"stats":{"get_bytes":4275,"get_count":5,"put_bytes":3638,"put_count":11}} +{"i":51,"series":1,"stats":{"get_bytes":4782,"get_count":6,"put_bytes":4039,"put_count":11}} +{"i":52,"series":1,"stats":{"get_bytes":2983,"get_count":4,"put_bytes":2344,"put_count":10}} +{"i":53,"series":1,"stats":{"get_bytes":3488,"get_count":5,"put_bytes":2746,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":3890,"get_count":5,"put_bytes":3252,"put_count":11}} +{"i":55,"series":1,"stats":{"get_bytes":4396,"get_count":6,"put_bytes":3655,"put_count":11}} +{"i":56,"series":1,"stats":{"get_bytes":4655,"get_count":5,"put_bytes":3948,"put_count":10}} +{"i":57,"series":1,"stats":{"get_bytes":5092,"get_count":5,"put_bytes":4417,"put_count":11}} +{"i":58,"series":1,"stats":{"get_bytes":3505,"get_count":5,"put_bytes":2867,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":4011,"get_count":6,"put_bytes":3269,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":4269,"get_count":5,"put_bytes":3564,"put_count":10}} +{"i":61,"series":1,"stats":{"get_bytes":4708,"get_count":5,"put_bytes":3965,"put_count":10}} +{"i":62,"series":1,"stats":{"get_bytes":5109,"get_count":5,"put_bytes":4607,"put_count":13}} +{"i":63,"series":1,"stats":{"get_bytes":2856,"get_count":6,"put_bytes":2114,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":3114,"get_count":5,"put_bytes":2408,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":3552,"get_count":5,"put_bytes":2810,"put_count":10}} +{"i":66,"series":1,"stats":{"get_bytes":3954,"get_count":5,"put_bytes":3316,"put_count":11}} +{"i":67,"series":1,"stats":{"get_bytes":4460,"get_count":6,"put_bytes":3852,"put_count":13}} +{"i":68,"series":1,"stats":{"get_bytes":2796,"get_count":6,"put_bytes":2090,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":3234,"get_count":6,"put_bytes":2492,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":3636,"get_count":6,"put_bytes":2997,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":4141,"get_count":7,"put_bytes":3400,"put_count":12}} +{"i":72,"series":1,"stats":{"get_bytes":4400,"get_count":6,"put_bytes":3761,"put_count":12}} +{"i":73,"series":1,"stats":{"get_bytes":2849,"get_count":6,"put_bytes":2107,"put_count":11}} +{"i":74,"series":1,"stats":{"get_bytes":3251,"get_count":6,"put_bytes":2612,"put_count":12}} +{"i":75,"series":1,"stats":{"get_bytes":3756,"get_count":7,"put_bytes":3014,"put_count":12}} +{"i":76,"series":1,"stats":{"get_bytes":4014,"get_count":6,"put_bytes":3309,"put_count":11}} +{"i":77,"series":1,"stats":{"get_bytes":4453,"get_count":6,"put_bytes":3778,"put_count":12}} +{"i":78,"series":1,"stats":{"get_bytes":2866,"get_count":6,"put_bytes":2228,"put_count":12}} +{"i":79,"series":1,"stats":{"get_bytes":3372,"get_count":7,"put_bytes":2629,"put_count":12}} +{"i":80,"series":1,"stats":{"get_bytes":3629,"get_count":6,"put_bytes":2923,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":4067,"get_count":6,"put_bytes":3326,"put_count":11}} +{"i":82,"series":1,"stats":{"get_bytes":4470,"get_count":6,"put_bytes":3899,"put_count":13}} +{"i":83,"series":1,"stats":{"get_bytes":2987,"get_count":7,"put_bytes":2244,"put_count":12}} +{"i":84,"series":1,"stats":{"get_bytes":3244,"get_count":6,"put_bytes":2538,"put_count":11}} +{"i":85,"series":1,"stats":{"get_bytes":3682,"get_count":6,"put_bytes":2940,"put_count":11}} +{"i":86,"series":1,"stats":{"get_bytes":4084,"get_count":6,"put_bytes":3447,"put_count":12}} +{"i":87,"series":1,"stats":{"get_bytes":4591,"get_count":7,"put_bytes":3849,"put_count":12}} +{"i":88,"series":1,"stats":{"get_bytes":2937,"get_count":6,"put_bytes":2153,"put_count":11}} +{"i":89,"series":1,"stats":{"get_bytes":3297,"get_count":6,"put_bytes":2555,"put_count":11}} +{"i":90,"series":1,"stats":{"get_bytes":3699,"get_count":6,"put_bytes":3061,"put_count":12}} +{"i":91,"series":1,"stats":{"get_bytes":4205,"get_count":7,"put_bytes":3464,"put_count":12}} +{"i":92,"series":1,"stats":{"get_bytes":4464,"get_count":6,"put_bytes":3757,"put_count":11}} +{"i":93,"series":1,"stats":{"get_bytes":2845,"get_count":5,"put_bytes":2170,"put_count":11}} +{"i":94,"series":1,"stats":{"get_bytes":3314,"get_count":6,"put_bytes":2676,"put_count":12}} +{"i":95,"series":1,"stats":{"get_bytes":3820,"get_count":7,"put_bytes":3078,"put_count":12}} +{"i":96,"series":1,"stats":{"get_bytes":4078,"get_count":6,"put_bytes":3373,"put_count":11}} +{"i":97,"series":1,"stats":{"get_bytes":4517,"get_count":6,"put_bytes":3774,"put_count":11}} +{"i":98,"series":1,"stats":{"get_bytes":4918,"get_count":6,"put_bytes":4347,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":3435,"get_count":7,"put_bytes":2693,"put_count":12}} +{"i":0,"series":2,"stats":{"get_bytes":2272,"get_count":3,"put_bytes":1805,"put_count":10}} +{"i":1,"series":2,"stats":{"get_bytes":2949,"get_count":5,"put_bytes":2207,"put_count":10}} +{"i":2,"series":2,"stats":{"get_bytes":3351,"get_count":5,"put_bytes":2714,"put_count":11}} +{"i":3,"series":2,"stats":{"get_bytes":3858,"get_count":6,"put_bytes":3116,"put_count":11}} +{"i":4,"series":2,"stats":{"get_bytes":4116,"get_count":5,"put_bytes":1936,"put_count":11}} +{"i":5,"series":2,"stats":{"get_bytes":3080,"get_count":6,"put_bytes":2338,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":3482,"get_count":6,"put_bytes":2844,"put_count":12}} +{"i":7,"series":2,"stats":{"get_bytes":3988,"get_count":7,"put_bytes":3247,"put_count":12}} +{"i":8,"series":2,"stats":{"get_bytes":4247,"get_count":6,"put_bytes":3540,"put_count":11}} +{"i":9,"series":2,"stats":{"get_bytes":4684,"get_count":6,"put_bytes":4009,"put_count":12}} +{"i":10,"series":2,"stats":{"get_bytes":3097,"get_count":6,"put_bytes":2459,"put_count":12}} +{"i":11,"series":2,"stats":{"get_bytes":3603,"get_count":7,"put_bytes":2861,"put_count":12}} +{"i":12,"series":2,"stats":{"get_bytes":3861,"get_count":6,"put_bytes":3156,"put_count":11}} +{"i":13,"series":2,"stats":{"get_bytes":4300,"get_count":6,"put_bytes":3557,"put_count":11}} +{"i":14,"series":2,"stats":{"get_bytes":4701,"get_count":6,"put_bytes":4130,"put_count":13}} +{"i":15,"series":2,"stats":{"get_bytes":3218,"get_count":7,"put_bytes":2476,"put_count":12}} +{"i":16,"series":2,"stats":{"get_bytes":3476,"get_count":6,"put_bytes":2770,"put_count":11}} +{"i":17,"series":2,"stats":{"get_bytes":3914,"get_count":6,"put_bytes":3172,"put_count":11}} +{"i":18,"series":2,"stats":{"get_bytes":4316,"get_count":6,"put_bytes":3678,"put_count":12}} +{"i":19,"series":2,"stats":{"get_bytes":4822,"get_count":7,"put_bytes":4147,"put_count":13}} +{"i":20,"series":2,"stats":{"get_bytes":3091,"get_count":6,"put_bytes":2385,"put_count":11}} +{"i":21,"series":2,"stats":{"get_bytes":3529,"get_count":6,"put_bytes":2787,"put_count":11}} +{"i":22,"series":2,"stats":{"get_bytes":3931,"get_count":6,"put_bytes":3292,"put_count":12}} +{"i":23,"series":2,"stats":{"get_bytes":4436,"get_count":7,"put_bytes":3695,"put_count":12}} +{"i":24,"series":2,"stats":{"get_bytes":4695,"get_count":6,"put_bytes":4057,"put_count":12}} +{"i":25,"series":2,"stats":{"get_bytes":3145,"get_count":6,"put_bytes":2403,"put_count":11}} +{"i":26,"series":2,"stats":{"get_bytes":3547,"get_count":6,"put_bytes":2908,"put_count":12}} +{"i":27,"series":2,"stats":{"get_bytes":4052,"get_count":7,"put_bytes":3310,"put_count":12}} +{"i":28,"series":2,"stats":{"get_bytes":4310,"get_count":6,"put_bytes":3605,"put_count":11}} +{"i":29,"series":2,"stats":{"get_bytes":4749,"get_count":6,"put_bytes":4074,"put_count":12}} +{"i":30,"series":2,"stats":{"get_bytes":3162,"get_count":6,"put_bytes":2524,"put_count":12}} +{"i":31,"series":2,"stats":{"get_bytes":3668,"get_count":7,"put_bytes":2925,"put_count":12}} +{"i":32,"series":2,"stats":{"get_bytes":3925,"get_count":6,"put_bytes":3219,"put_count":11}} +{"i":33,"series":2,"stats":{"get_bytes":4363,"get_count":6,"put_bytes":3622,"put_count":11}} +{"i":34,"series":2,"stats":{"get_bytes":4766,"get_count":6,"put_bytes":4195,"put_count":13}} +{"i":35,"series":2,"stats":{"get_bytes":3283,"get_count":7,"put_bytes":2540,"put_count":12}} +{"i":36,"series":2,"stats":{"get_bytes":3540,"get_count":6,"put_bytes":2834,"put_count":11}} +{"i":37,"series":2,"stats":{"get_bytes":3978,"get_count":6,"put_bytes":3236,"put_count":11}} +{"i":38,"series":2,"stats":{"get_bytes":4380,"get_count":6,"put_bytes":3743,"put_count":12}} +{"i":39,"series":2,"stats":{"get_bytes":4887,"get_count":7,"put_bytes":4145,"put_count":12}} +{"i":40,"series":2,"stats":{"get_bytes":3233,"get_count":6,"put_bytes":2449,"put_count":11}} +{"i":41,"series":2,"stats":{"get_bytes":3593,"get_count":6,"put_bytes":2851,"put_count":11}} +{"i":42,"series":2,"stats":{"get_bytes":3995,"get_count":6,"put_bytes":3357,"put_count":12}} +{"i":43,"series":2,"stats":{"get_bytes":4501,"get_count":7,"put_bytes":3760,"put_count":12}} +{"i":44,"series":2,"stats":{"get_bytes":4760,"get_count":6,"put_bytes":4053,"put_count":11}} +{"i":45,"series":2,"stats":{"get_bytes":3141,"get_count":5,"put_bytes":2466,"put_count":11}} +{"i":46,"series":2,"stats":{"get_bytes":3610,"get_count":6,"put_bytes":2972,"put_count":12}} +{"i":47,"series":2,"stats":{"get_bytes":4116,"get_count":7,"put_bytes":3374,"put_count":12}} +{"i":48,"series":2,"stats":{"get_bytes":4374,"get_count":6,"put_bytes":3669,"put_count":11}} +{"i":49,"series":2,"stats":{"get_bytes":4813,"get_count":6,"put_bytes":4070,"put_count":11}} +{"i":50,"series":2,"stats":{"get_bytes":5214,"get_count":6,"put_bytes":4643,"put_count":13}} +{"i":51,"series":2,"stats":{"get_bytes":3731,"get_count":7,"put_bytes":2989,"put_count":12}} +{"i":52,"series":2,"stats":{"get_bytes":3989,"get_count":6,"put_bytes":3283,"put_count":11}} +{"i":53,"series":2,"stats":{"get_bytes":4427,"get_count":6,"put_bytes":3686,"put_count":11}} +{"i":54,"series":2,"stats":{"get_bytes":4830,"get_count":6,"put_bytes":4191,"put_count":12}} +{"i":55,"series":2,"stats":{"get_bytes":5335,"get_count":7,"put_bytes":4660,"put_count":13}} +{"i":56,"series":2,"stats":{"get_bytes":3604,"get_count":6,"put_bytes":2898,"put_count":11}} +{"i":57,"series":2,"stats":{"get_bytes":4042,"get_count":6,"put_bytes":3300,"put_count":11}} +{"i":58,"series":2,"stats":{"get_bytes":4444,"get_count":6,"put_bytes":3806,"put_count":12}} +{"i":59,"series":2,"stats":{"get_bytes":4950,"get_count":7,"put_bytes":4208,"put_count":12}} +{"i":60,"series":2,"stats":{"get_bytes":5208,"get_count":6,"put_bytes":4569,"put_count":12}} +{"i":61,"series":2,"stats":{"get_bytes":3657,"get_count":6,"put_bytes":2915,"put_count":11}} +{"i":62,"series":2,"stats":{"get_bytes":4059,"get_count":6,"put_bytes":3421,"put_count":12}} +{"i":63,"series":2,"stats":{"get_bytes":4565,"get_count":7,"put_bytes":3822,"put_count":12}} +{"i":64,"series":2,"stats":{"get_bytes":4822,"get_count":6,"put_bytes":4117,"put_count":11}} +{"i":65,"series":2,"stats":{"get_bytes":5261,"get_count":6,"put_bytes":4587,"put_count":12}} +{"i":66,"series":2,"stats":{"get_bytes":3675,"get_count":6,"put_bytes":3037,"put_count":12}} +{"i":67,"series":2,"stats":{"get_bytes":4181,"get_count":7,"put_bytes":3438,"put_count":12}} +{"i":68,"series":2,"stats":{"get_bytes":4438,"get_count":6,"put_bytes":3732,"put_count":11}} +{"i":69,"series":2,"stats":{"get_bytes":4876,"get_count":6,"put_bytes":4135,"put_count":11}} +{"i":70,"series":2,"stats":{"get_bytes":5279,"get_count":6,"put_bytes":4708,"put_count":13}} +{"i":71,"series":2,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":3054,"put_count":12}} +{"i":72,"series":2,"stats":{"get_bytes":4054,"get_count":6,"put_bytes":3347,"put_count":11}} +{"i":73,"series":2,"stats":{"get_bytes":4491,"get_count":6,"put_bytes":3749,"put_count":11}} +{"i":74,"series":2,"stats":{"get_bytes":4893,"get_count":6,"put_bytes":4256,"put_count":12}} +{"i":75,"series":2,"stats":{"get_bytes":5400,"get_count":7,"put_bytes":4725,"put_count":13}} +{"i":76,"series":2,"stats":{"get_bytes":3669,"get_count":6,"put_bytes":2963,"put_count":11}} +{"i":77,"series":2,"stats":{"get_bytes":4107,"get_count":6,"put_bytes":3364,"put_count":11}} +{"i":78,"series":2,"stats":{"get_bytes":4508,"get_count":6,"put_bytes":3870,"put_count":12}} +{"i":79,"series":2,"stats":{"get_bytes":5014,"get_count":7,"put_bytes":4273,"put_count":12}} +{"i":80,"series":2,"stats":{"get_bytes":5273,"get_count":6,"put_bytes":4634,"put_count":12}} +{"i":81,"series":2,"stats":{"get_bytes":3722,"get_count":6,"put_bytes":2979,"put_count":11}} +{"i":82,"series":2,"stats":{"get_bytes":4123,"get_count":6,"put_bytes":3485,"put_count":12}} +{"i":83,"series":2,"stats":{"get_bytes":4629,"get_count":7,"put_bytes":3887,"put_count":12}} +{"i":84,"series":2,"stats":{"get_bytes":4887,"get_count":6,"put_bytes":4182,"put_count":11}} +{"i":85,"series":2,"stats":{"get_bytes":5326,"get_count":6,"put_bytes":4583,"put_count":11}} +{"i":86,"series":2,"stats":{"get_bytes":3671,"get_count":5,"put_bytes":3100,"put_count":12}} +{"i":87,"series":2,"stats":{"get_bytes":4244,"get_count":7,"put_bytes":3502,"put_count":12}} +{"i":88,"series":2,"stats":{"get_bytes":4502,"get_count":6,"put_bytes":3796,"put_count":11}} +{"i":89,"series":2,"stats":{"get_bytes":4940,"get_count":6,"put_bytes":4199,"put_count":11}} +{"i":90,"series":2,"stats":{"get_bytes":5343,"get_count":6,"put_bytes":4704,"put_count":12}} +{"i":91,"series":2,"stats":{"get_bytes":5848,"get_count":7,"put_bytes":5173,"put_count":13}} +{"i":92,"series":2,"stats":{"get_bytes":4117,"get_count":6,"put_bytes":3411,"put_count":11}} +{"i":93,"series":2,"stats":{"get_bytes":4555,"get_count":6,"put_bytes":3813,"put_count":11}} +{"i":94,"series":2,"stats":{"get_bytes":4957,"get_count":6,"put_bytes":4320,"put_count":12}} +{"i":95,"series":2,"stats":{"get_bytes":5464,"get_count":7,"put_bytes":4721,"put_count":12}} +{"i":96,"series":2,"stats":{"get_bytes":5721,"get_count":6,"put_bytes":5082,"put_count":12}} +{"i":97,"series":2,"stats":{"get_bytes":4170,"get_count":6,"put_bytes":3428,"put_count":11}} +{"i":98,"series":2,"stats":{"get_bytes":4572,"get_count":6,"put_bytes":3934,"put_count":12}} +{"i":99,"series":2,"stats":{"get_bytes":5078,"get_count":7,"put_bytes":4336,"put_count":12}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png index a3b21bc0b9464280934a1df4c911175152ed2d63..151ddffa1aa0f4f178bba264a8b42643b9da69ba 100644 GIT binary patch literal 27808 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 literal 27932 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 diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index ddd82a771..ab71d1d60 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":3797,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":5526,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":5443,"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 index f1668762b546fba3cc99f941d745d5e451378efd..b09e5a8de69da3f8502ba896aca3813d3f7ff613 100644 GIT binary patch literal 13832 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 literal 13830 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

EZvu)7>Zc*O)NTr>qE_z!s3%8Bn_dNdi1`@h& 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 diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index c914f3eb1..40c997e07 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1225,7 +1225,7 @@ impl Primitives for VM<'_> { 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).to_bytes() + hasher.digest(data).digest().to_owned() } fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { From 610f399f193f3a395914048012a01d9a87ec6d45 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Jan 2023 19:00:19 -0800 Subject: [PATCH 237/339] EVM: use FRC-0042 for invoke (#1021) This also re-numbers all the other method numbers, because we're removing method 2. This is going to be annoying... but it's better to do this now than to have a dead method. fixes https://github.com/filecoin-project/ref-fvm/issues/1256 --- Cargo.lock | 1 + actors/eam/src/ext.rs | 2 +- actors/evm/Cargo.toml | 1 + actors/evm/src/lib.rs | 37 ++++++++++++++++++------------------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f5cd9da3..cbc3d83a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1888,6 +1888,7 @@ dependencies = [ "etk-asm", "fil_actors_runtime", "fixed-hash 0.7.0", + "frc42_dispatch 3.0.0", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.2", "fvm_ipld_kamt", diff --git a/actors/eam/src/ext.rs b/actors/eam/src/ext.rs index 0d087048f..b15a32456 100644 --- a/actors/eam/src/ext.rs +++ b/actors/eam/src/ext.rs @@ -36,5 +36,5 @@ pub mod evm { pub input_data: RawBytes, } - pub const RESURRECT_METHOD: u64 = 7; + pub const RESURRECT_METHOD: u64 = 2; } diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 3593e6137..156f6e2a0 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -42,6 +42,7 @@ substrate-bn = { version = "0.6.0", default-features = false } near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } lazy_static = "1.4.0" once_cell = { version = "1.16.0", default-features = false} +frc42_dispatch = "3.0.0" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 8603b2f35..6763461b3 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -50,13 +50,12 @@ fn test_method_selector() { #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - // TODO: Do we want to use ExportedNums for all of these, per FRC-42? - InvokeContract = 2, + Resurrect = 2, GetBytecode = 3, - GetStorageAt = 4, - InvokeContractDelegate = 5, - GetBytecodeHash = 6, - Resurrect = 7, + GetBytecodeHash = 4, + GetStorageAt = 5, + InvokeContractDelegate = 6, + InvokeContract = frc42_dispatch::method_hash!("InvokeEVM"), } pub struct EvmContractActor; @@ -297,19 +296,6 @@ impl ActorCode for EvmContractActor { RT: Runtime, RT::Blockstore: Clone, { - // We reserve all methods below EVM_MAX_RESERVED (<= 1023) method. This is a _subset_ of - // those reserved by FRC0042. - if method > EVM_MAX_RESERVED_METHOD { - return Self::handle_filecoin_method(rt, method, args).map(|d| { - if d.is_empty() { - None - } else { - // TODO: Pass the codec through to here? https://github.com/filecoin-project/ref-fvm/issues/1422 - Some(IpldBlock { codec: DAG_CBOR, data: d }) - } - }); - } - match FromPrimitive::from_u64(method) { Some(Method::Constructor) => { Self::constructor( @@ -374,6 +360,19 @@ impl ActorCode for EvmContractActor { )?; 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).map(|d| { + if d.is_empty() { + None + } else { + // TODO: Pass the codec through to here? + // https://github.com/filecoin-project/ref-fvm/issues/1422 + Some(IpldBlock { codec: DAG_CBOR, data: d }) + } + }) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } From 4229c3aa929520da8ccace6a6ae33f7c4c930917 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 12 Jan 2023 22:23:13 +0200 Subject: [PATCH 238/339] EVM: fix embryo method error message (#1025) fix embry method error message --- actors/placeholder/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/placeholder/src/lib.rs b/actors/placeholder/src/lib.rs index 996d7e260..d5e51031c 100644 --- a/actors/placeholder/src/lib.rs +++ b/actors/placeholder/src/lib.rs @@ -3,6 +3,6 @@ pub extern "C" fn invoke(_: u32) -> u32 { fvm_sdk::vm::abort( fvm_shared::error::ExitCode::USR_UNHANDLED_MESSAGE.value(), - Some("placeholder actors may only receive messages on method 0"), + Some("there is no contract deployed at this address; placeholder actors may only receive value transfers with method 0"), ) } From 6eb161f1e3040cd99cfd500e65a795f99ffa5f0c Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 12 Jan 2023 22:32:47 +0200 Subject: [PATCH 239/339] EVM: add integration test for eam/init revert data propagation. (#1026) * add test for revert data propagation through EAM/init * rustfmt --- test_vm/tests/evm_test.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 01d08c283..4318285d8 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -477,3 +477,36 @@ fn test_evm_staticcall_delegatecall() { 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::Create2 as u64, + // init code: + // PUSH1 0x42; PUSH1 0x0; MSTORE; + // PUSH1 0x20; PUSH1 0x0; REVERT + Some(fil_actor_eam::Create2Params { + initcode: vec![0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xfd], + salt: [0u8; 32], + }), + ) + .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); +} From 0f4a75d305fd4a0450331252ef111630ec6ada94 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 12 Jan 2023 14:05:02 -0800 Subject: [PATCH 240/339] EVM: Rename read right pad for clarity (#1029) rename read_right_pad --- actors/evm/src/interpreter/precompiles/evm.rs | 14 +++++++++----- actors/evm/src/interpreter/precompiles/fvm.rs | 10 +++++----- .../evm/src/interpreter/precompiles/parameter.rs | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 013f0aa33..b521dbdb8 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -11,7 +11,7 @@ use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1 use uint::byteorder::{ByteOrder, LE}; use crate::interpreter::{ - precompiles::{parameter::read_right_pad, PrecompileError}, + precompiles::{parameter::right_pad, PrecompileError}, System, U256, }; @@ -112,7 +112,7 @@ pub(super) fn modexp( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let input = read_right_pad(input, 96); + let input = right_pad(input, 96); // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. // Since we don't have any complexity functions or specific gas measurements of modexp in FEVM, @@ -140,7 +140,7 @@ pub(super) fn modexp( return Ok(Vec::new()); } let input = if input.len() > 96 { &input[96..] } else { &[] }; - let input = read_right_pad(input, base_len + exponent_len + mod_len); + let input = right_pad(input, base_len + exponent_len + mod_len); let base = BigUint::from_bytes_be(&input[0..base_len]); let exponent = BigUint::from_bytes_be(&input[base_len..exponent_len + base_len]); @@ -194,7 +194,7 @@ pub(super) fn ec_mul( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let input = read_right_pad(input, 96); + let input = right_pad(input, 96); let mut input_params: PaddedChunks = PaddedChunks::new(&input); let point = input_params.next_param_padded()?; @@ -346,9 +346,13 @@ mod tests { let mut expected = input.clone(); expected.resize(i, 0); - let res = read_right_pad(&input, i); + let res = right_pad(&input, i); assert_eq!(&*res, &expected); + let no_padding = b"foo bar boxy ".to_vec(); + let res = right_pad(&no_padding, 12); + // do nothing + assert_eq!(&res, &no_padding); input.push(0); } } diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index e281db1d0..0248d3cd7 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -6,7 +6,7 @@ use num_traits::FromPrimitive; use crate::interpreter::{ instructions::call::CallKind, precompiles::{ - parameter::{read_right_pad, Parameter}, + parameter::{right_pad, Parameter}, NativeType, }, System, U256, @@ -23,7 +23,7 @@ pub(super) fn get_actor_type( _: PrecompileContext, ) -> PrecompileResult { // should never panic, pad to 32 bytes then read exactly 32 bytes - let id_bytes: [u8; 32] = read_right_pad(input, 32)[..32].as_ref().try_into().unwrap(); + let id_bytes: [u8; 32] = right_pad(input, 32)[..32].as_ref().try_into().unwrap(); let id = match Parameter::::try_from(&id_bytes) { Ok(id) => id.0, Err(_) => return Ok(Vec::new()), @@ -98,7 +98,7 @@ pub(super) fn get_randomness( debug_assert_eq!(input_params.chunks_read(), 4); - let entropy = read_right_pad(input_params.remaining_slice(), entropy_len as usize); + let entropy = right_pad(input_params.remaining_slice(), entropy_len as usize); let randomness = match randomness_type { Some(RandomnessType::Chain) => system @@ -144,7 +144,7 @@ pub(super) fn resolve_address( let mut input_params = U256Reader::new(input); let len = input_params.next_param_padded::()? as usize; - let addr = match Address::from_bytes(&read_right_pad(input_params.remaining_slice(), len)) { + let addr = match Address::from_bytes(&right_pad(input_params.remaining_slice(), len)) { Ok(o) => o, Err(_) => return Ok(Vec::new()), }; @@ -197,7 +197,7 @@ pub(super) fn call_actor( let result = { let start = input_params.remaining_slice(); - let bytes = read_right_pad(start, send_data_size + address_size); + let bytes = right_pad(start, send_data_size + address_size); let input_data = &bytes[..send_data_size]; let address = &bytes[send_data_size..send_data_size + address_size]; diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/interpreter/precompiles/parameter.rs index 5c82b7323..97a51a051 100644 --- a/actors/evm/src/interpreter/precompiles/parameter.rs +++ b/actors/evm/src/interpreter/precompiles/parameter.rs @@ -18,8 +18,8 @@ impl From for PrecompileError { } } -// It is uncomfortable how much Eth pads everything... -pub(super) fn read_right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { +/// Pad out to len as needed, but does not slice output to len +pub(super) fn right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { let mut input: Cow<[u8]> = input.into(); let input_len = input.len(); if len > input_len { From b9333d26add1706878c39df464416faabaecfc4b Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 12 Jan 2023 15:11:28 -0800 Subject: [PATCH 241/339] EVM: Fix resolve address errors (#1031) * use precompile error value since it is now not actually returned and will log a more helpful reason than just empty vec. also add some logging * fmt * i hate clippy sometimes * test was not actually testing for not found like it said it was --- actors/evm/src/interpreter/precompiles/fvm.rs | 10 ++++++++-- actors/evm/tests/precompile.rs | 10 +++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 0248d3cd7..165d1695d 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -146,12 +146,18 @@ pub(super) fn resolve_address( let len = input_params.next_param_padded::()? as usize; let addr = match Address::from_bytes(&right_pad(input_params.remaining_slice(), len)) { Ok(o) => o, - Err(_) => return Ok(Vec::new()), + Err(e) => { + log::debug!(target: "evm", "Address parsing failed: {e}"); + return Err(PrecompileError::InvalidInput); + } }; Ok(system .rt .resolve_address(&addr) - .map(|a| U256::from(a).to_bytes().to_vec()) + .map(|a| { + log::debug!(target: "evm", "{addr} resolved to {a}"); + U256::from(a).to_bytes().to_vec() + }) .unwrap_or_default()) } diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index f10da3999..8f2031855 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -296,7 +296,15 @@ fn test_precompile_failure() { // not found succeeds with empty rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(&mut rt, &U256::from(111).to_bytes()); + let input = { + let addr = FILAddress::new_delegated(111, b"foo").unwrap().to_bytes(); + // first word is len + let mut v = U256::from(addr.len()).to_bytes().to_vec(); + // then addr + v.extend_from_slice(&addr); + v + }; + let result = util::invoke_contract(&mut rt, &input); rt.verify(); assert_eq!(&[1u8], result.as_slice()); rt.reset(); From e8a4798d2579e907ace3ac04d068284f61610f12 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 12 Jan 2023 20:53:20 -0500 Subject: [PATCH 242/339] Bump FRC42_dispatch to 3.0.0 (#1035) --- Cargo.lock | 248 ++++++++++++-------------------------- actors/account/Cargo.toml | 2 +- actors/init/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- 6 files changed, 79 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbc3d83a6..4f0d03aa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,11 +1778,11 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "fil_actors_runtime", - "frc42_dispatch 2.0.0", + "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "num-derive", "num-traits", "serde", @@ -1801,7 +1801,7 @@ dependencies = [ "futures", "fvm_ipld_blockstore", "fvm_ipld_car", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "serde", "serde_ipld_dagcbor", "serde_json", @@ -1813,8 +1813,8 @@ version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "log", "num-derive", "num-traits", @@ -1827,13 +1827,13 @@ version = "10.0.0-alpha.1" dependencies = [ "cid 0.8.6", "fil_actors_runtime", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "frc46_token", "fvm_actor_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "lazy_static", "log", "num-derive", @@ -1850,8 +1850,8 @@ dependencies = [ "fil_actor_evm", "fil_actors_runtime", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "hex-literal", "log", "multihash 0.16.3", @@ -1867,8 +1867,8 @@ name = "fil_actor_ethaccount" version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "hex-literal", "num-derive", "num-traits", @@ -1888,11 +1888,11 @@ dependencies = [ "etk-asm", "fil_actors_runtime", "fixed-hash 0.7.0", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_kamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "hex", "hex-literal", "impl-serde 0.3.2", @@ -1921,11 +1921,11 @@ dependencies = [ "anyhow", "cid 0.8.6", "fil_actors_runtime", - "frc42_dispatch 2.0.0", + "frc42_dispatch", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "log", "num-derive", "num-traits", @@ -1942,14 +1942,14 @@ dependencies = [ "fil_actor_reward", "fil_actor_verifreg", "fil_actors_runtime", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "frc46_token", "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "integer-encoding", "itertools", "libipld-core 0.13.1", @@ -1973,13 +1973,13 @@ dependencies = [ "fil_actor_power", "fil_actor_reward", "fil_actors_runtime", - "frc42_dispatch 2.0.0", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "itertools", "lazy_static", "log", @@ -1998,12 +1998,12 @@ dependencies = [ "anyhow", "cid 0.8.6", "fil_actors_runtime", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "indexmap", "integer-encoding", "lazy_static", @@ -2020,11 +2020,11 @@ dependencies = [ "cid 0.8.6", "derive_builder", "fil_actors_runtime", - "frc42_dispatch 2.0.0", + "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "num-derive", "num-traits", "serde", @@ -2034,8 +2034,8 @@ dependencies = [ name = "fil_actor_placeholder" version = "10.0.0-alpha.1" dependencies = [ - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", + "fvm_sdk", + "fvm_shared", ] [[package]] @@ -2046,11 +2046,11 @@ dependencies = [ "cid 0.8.6", "fil_actor_reward", "fil_actors_runtime", - "frc42_dispatch 2.0.0", + "frc42_dispatch", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "indexmap", "integer-encoding", "lazy_static", @@ -2066,8 +2066,8 @@ version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "lazy_static", "log", "num", @@ -2084,8 +2084,8 @@ dependencies = [ "cid 0.8.6", "fil_actors_runtime", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "num-derive", "num-traits", "serde", @@ -2098,13 +2098,13 @@ dependencies = [ "anyhow", "cid 0.8.6", "fil_actors_runtime", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "frc46_token", "fvm_actor_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "lazy_static", "log", "num-derive", @@ -2125,10 +2125,10 @@ dependencies = [ "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", + "fvm_sdk", + "fvm_shared", "hex", "itertools", "lazy_static", @@ -2197,8 +2197,8 @@ dependencies = [ "fil_actors_runtime", "frc46_token", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "num-derive", "num-traits", "serde", @@ -2253,41 +2253,16 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "frc42_dispatch" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271778fe29028ec60cdc759ae7edd7e49bf928bc27fc4cdcbaa12ffbc4774e0" -dependencies = [ - "frc42_hasher 1.2.0", - "frc42_macros 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fvm_ipld_encoding 0.2.3", - "fvm_sdk 2.1.0", - "fvm_shared 2.0.0", - "thiserror", -] - [[package]] name = "frc42_dispatch" version = "3.0.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" dependencies = [ - "frc42_hasher 1.3.0", - "frc42_macros 1.0.0 (git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2)", - "fvm_ipld_encoding 0.3.2", - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", - "thiserror", -] - -[[package]] -name = "frc42_hasher" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18775b28d34b541f5bf010f43680dd2b9ef7cabbee620abeac49eeacc66bb9e6" -dependencies = [ - "fvm_sdk 2.1.0", - "fvm_shared 2.0.0", + "frc42_hasher", + "frc42_macros", + "fvm_ipld_encoding", + "fvm_sdk", + "fvm_shared", "thiserror", ] @@ -2296,31 +2271,18 @@ name = "frc42_hasher" version = "1.3.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" dependencies = [ - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", + "fvm_sdk", + "fvm_shared", "thiserror", ] -[[package]] -name = "frc42_macros" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c343356c3652593452cc1cfe95535a915261342e2a4c13bb8388ca29b259a400" -dependencies = [ - "blake2b_simd", - "frc42_hasher 1.2.0", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "frc42_macros" version = "1.0.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" dependencies = [ "blake2b_simd", - "frc42_hasher 1.3.0", + "frc42_hasher", "proc-macro2", "quote", "syn", @@ -2333,14 +2295,14 @@ source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=fe dependencies = [ "anyhow", "cid 0.8.6", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_amt", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", + "fvm_sdk", + "fvm_shared", "integer-encoding", "num-traits", "serde", @@ -2482,11 +2444,11 @@ source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=fe dependencies = [ "anyhow", "cid 0.8.6", - "frc42_dispatch 3.0.0", + "frc42_dispatch", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", - "fvm_sdk 3.0.0-alpha.21", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_sdk", + "fvm_shared", "num-traits", "serde", "serde_tuple", @@ -2502,7 +2464,7 @@ dependencies = [ "anyhow", "cid 0.8.6", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "itertools", "once_cell", "serde", @@ -2515,7 +2477,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1950291f40d2d1047eb0a4568f7ef6d5b4973452dcef012dffb1957fe483ff7" dependencies = [ - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "serde", "thiserror", "unsigned-varint", @@ -2541,30 +2503,12 @@ dependencies = [ "cid 0.8.6", "futures", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "integer-encoding", "serde", "thiserror", ] -[[package]] -name = "fvm_ipld_encoding" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1ff5ba581625ab38cf2829fbd04ac232c6277466fdbe0270b42dcb976902d5" -dependencies = [ - "anyhow", - "cid 0.8.6", - "cs_serde_bytes", - "fvm_ipld_blockstore", - "multihash 0.16.3", - "serde", - "serde_ipld_dagcbor", - "serde_repr", - "serde_tuple", - "thiserror", -] - [[package]] name = "fvm_ipld_encoding" version = "0.3.2" @@ -2593,7 +2537,7 @@ dependencies = [ "cid 0.8.6", "forest_hash_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "libipld-core 0.14.0", "multihash 0.16.3", "once_cell", @@ -2613,7 +2557,7 @@ dependencies = [ "cid 0.8.6", "forest_hash_utils", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "libipld-core 0.15.0", "multihash 0.16.3", "once_cell", @@ -2622,21 +2566,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "fvm_sdk" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e211f0571eed80e90537f09dcca43ca6f7172b43c9a40850ea404bcf09a139c1" -dependencies = [ - "cid 0.8.6", - "fvm_ipld_encoding 0.2.3", - "fvm_shared 2.0.0", - "lazy_static", - "log", - "num-traits", - "thiserror", -] - [[package]] name = "fvm_sdk" version = "3.0.0-alpha.21" @@ -2644,43 +2573,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aec61bd25838e3b26805506de57e11a51798498e6dec7b3075ec8ab4db6579f5" dependencies = [ "cid 0.8.6", - "fvm_ipld_encoding 0.3.2", - "fvm_shared 3.0.0-alpha.15", + "fvm_ipld_encoding", + "fvm_shared", "lazy_static", "log", "num-traits", "thiserror", ] -[[package]] -name = "fvm_shared" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7d1de62b7b74909d6e4816de83c4e6b46017bba9a31bb0de82b6b26b11cf74" -dependencies = [ - "anyhow", - "blake2b_simd", - "byteorder", - "cid 0.8.6", - "cs_serde_bytes", - "data-encoding", - "data-encoding-macro", - "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.2.3", - "lazy_static", - "log", - "multihash 0.16.3", - "num-bigint", - "num-derive", - "num-integer", - "num-traits", - "serde", - "serde_repr", - "serde_tuple", - "thiserror", - "unsigned-varint", -] - [[package]] name = "fvm_shared" version = "3.0.0-alpha.15" @@ -2695,7 +2595,7 @@ dependencies = [ "data-encoding", "data-encoding-macro", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "lazy_static", "log", "multihash 0.16.3", @@ -4613,9 +4513,9 @@ dependencies = [ "fvm_actor_utils", "fvm_ipld_bitfield", "fvm_ipld_blockstore", - "fvm_ipld_encoding 0.3.2", + "fvm_ipld_encoding", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.15", + "fvm_shared", "hex", "indexmap", "integer-encoding", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 72d133d9e..ee1d4a46d 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.15", default-features = false } -frc42_dispatch = "2.0.0" +frc42_dispatch = "3.0.0" fvm_actor_utils = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index dd1245d93..e43395525 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "lib"] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.15", default-features = false } fvm_ipld_hamt = "0.6.1" -frc42_dispatch = "2.0.0" +frc42_dispatch = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 79e536642..a99953a55 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -frc42_dispatch = "2.0.0" +frc42_dispatch = "3.0.0" fvm_shared = { version = "3.0.0-alpha.15", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 2662a6a70..08e064465 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.15", default-features = false } -frc42_dispatch = "2.0.0" +frc42_dispatch = "3.0.0" num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index 5b056ab93..ed2dfcc45 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.15", default-features = false } -frc42_dispatch = "2.0.0" +frc42_dispatch = "3.0.0" fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" From 93aab43e1459141d7a911b7f07411a51156a7998 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 12 Jan 2023 18:04:08 -0800 Subject: [PATCH 243/339] EVM: precompile actor_type error (#1034) precompile revert instead of succeeding with nothing if input is invalid. --- actors/evm/src/interpreter/precompiles/fvm.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 165d1695d..512aa403d 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -26,7 +26,10 @@ pub(super) fn get_actor_type( let id_bytes: [u8; 32] = right_pad(input, 32)[..32].as_ref().try_into().unwrap(); let id = match Parameter::::try_from(&id_bytes) { Ok(id) => id.0, - Err(_) => return Ok(Vec::new()), + Err(_) => { + log::debug!(target: "evm", "ID address parsing failed: {}", hex::encode(id_bytes)); + return Err(PrecompileError::InvalidInput); + } }; // resolve type from code CID From 2b1df242c5fb75ce431d2fd6fa663dc916458513 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 12 Jan 2023 23:35:27 -0500 Subject: [PATCH 244/339] Runtime: Don't convert syscall errors (#1024) --- Cargo.lock | 50 +++++++++---------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/datacap/src/lib.rs | 4 +- actors/eam/Cargo.toml | 2 +- actors/ethaccount/Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- actors/evm/src/interpreter/system.rs | 14 +++++- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- actors/placeholder/Cargo.toml | 4 +- actors/power/Cargo.toml | 2 +- actors/power/tests/power_actor_tests.rs | 4 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 6 +-- runtime/src/runtime/fvm.rs | 64 ++----------------------- runtime/src/runtime/mod.rs | 63 +++++++++++++++++++++++- runtime/src/test_utils.rs | 22 +++------ state/Cargo.toml | 2 +- test_vm/Cargo.toml | 2 +- test_vm/src/lib.rs | 17 +++---- 27 files changed, 143 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f0d03aa3..30a1b3160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2256,7 +2256,7 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "3.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "frc42_hasher", "frc42_macros", @@ -2269,7 +2269,7 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "1.3.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "fvm_sdk", "fvm_shared", @@ -2279,7 +2279,7 @@ dependencies = [ [[package]] name = "frc42_macros" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "blake2b_simd", "frc42_hasher", @@ -2291,7 +2291,7 @@ dependencies = [ [[package]] name = "frc46_token" version = "3.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "anyhow", "cid 0.8.6", @@ -2440,7 +2440,7 @@ dependencies = [ [[package]] name = "fvm_actor_utils" version = "3.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#9fb649e70b9ba333b1944a4696f2f6bae9f9793e" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "anyhow", "cid 0.8.6", @@ -2568,9 +2568,9 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.21" +version = "3.0.0-alpha.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec61bd25838e3b26805506de57e11a51798498e6dec7b3075ec8ab4db6579f5" +checksum = "d51908aab9e7564fbcb278d78e31c12402b68c70517661780feb29adbb234aaf" dependencies = [ "cid 0.8.6", "fvm_ipld_encoding", @@ -2583,9 +2583,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.15" +version = "3.0.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dea3259a0e147bcd22c5670441c1cbc2452e70763bf6688fb14e96f36cee2c2" +checksum = "a95666bb2e4b450f1750015a5f6c5da7544e744e295690cb4fcc5ba453283142" dependencies = [ "anyhow", "bitflags", @@ -3968,9 +3968,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -4993,45 +4993,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index ee1d4a46d..25856bab9 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } frc42_dispatch = "3.0.0" fvm_actor_utils = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index fbd5483ec..97ae26504 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index b96036902..03300a51f 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.2" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index eb0cff4c6..1332c469f 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -4,14 +4,14 @@ use frc46_token::token::types::{ TransferFromParams, TransferFromReturn, TransferParams, TransferReturn, }; use frc46_token::token::{Token, TokenError, TOKEN_PRECISION}; -use fvm_actor_utils::messaging::{Messaging, MessagingError, Response}; +use fvm_actor_utils::messaging::{Messaging, MessagingError}; use fvm_actor_utils::receiver::ReceiverHookError; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; use fvm_shared::econ::TokenAmount; use fvm_shared::error::{ErrorNumber, ExitCode}; -use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, Response, METHOD_CONSTRUCTOR, METHOD_SEND}; 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 2d9012a43..0b8ddddbf 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.2" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 941d8a101..04852332e 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "lib"] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } serde = { version = "1.0.136", features = ["derive"] } fvm_ipld_encoding = "0.3.2" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", 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 156f6e2a0..ff002f9ae 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } fvm_ipld_kamt = { version = "0.1.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 1e62f44d7..42e090d68 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -222,11 +222,21 @@ impl<'r, RT: Runtime> System<'r, RT> { send_flags: SendFlags, ) -> Result, ActorError> { self.flush()?; - let result = self.rt.send_generalized(to, method, params, value, gas_limit, send_flags)?; + let result = self + .rt + .send_generalized(to, method, params, value, gas_limit, send_flags) + .map_err(|err| actor_error!(unspecified; "send syscall failed: {}", err))?; if !send_flags.read_only() { self.reload()?; } - Ok(result) + match result.exit_code { + ExitCode::OK => Ok(result.return_data), + e => Err(ActorError::unchecked_with_data( + e, + "send failed".to_string(), + result.return_data, + )), + } } /// Flush the actor state (bytecode, nonce, and slots). diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index e43395525..8579b8e18 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } fvm_ipld_hamt = "0.6.1" frc42_dispatch = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index a3e6d2687..3044d6656 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_bitfield = "0.5.2" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.2" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index a99953a55..e0fad04d1 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.0" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } fvm_ipld_bitfield = "0.5.4" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } fvm_ipld_hamt = "0.6.1" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 778dfc749..77adf2e27 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -23,7 +23,7 @@ fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.2" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", 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 08e064465..662e146ec 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } frc42_dispatch = "3.0.0" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/placeholder/Cargo.toml b/actors/placeholder/Cargo.toml index 6032b4fe9..d2f2b7605 100644 --- a/actors/placeholder/Cargo.toml +++ b/actors/placeholder/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fvm_sdk = { version = "3.0.0-alpha.21", optional = true } -fvm_shared = { version = "3.0.0-alpha.15", optional = true } +fvm_sdk = { version = "3.0.0-alpha.22", optional = true } +fvm_shared = { version = "3.0.0-alpha.16", optional = true } [features] fil-actor = ["fvm_sdk", "fvm_shared"] diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index ed2dfcc45..893ffc8c2 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } frc42_dispatch = "3.0.0" fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 9ab368957..0c7e08bfc 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -119,11 +119,11 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { IpldBlock::serialize_cbor(&message_params).unwrap(), TokenAmount::from_atto(10), None, - ExitCode::SYS_INSUFFICIENT_FUNDS, + ExitCode::USR_INSUFFICIENT_FUNDS, ); expect_abort( - ExitCode::SYS_INSUFFICIENT_FUNDS, + ExitCode::USR_INSUFFICIENT_FUNDS, rt.call::( Method::CreateMiner as u64, IpldBlock::serialize_cbor(&create_miner_params).unwrap(), diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 850f75a1d..e0f45916b 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 5bac8c576..c1960dbf5 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } fvm_ipld_encoding = "0.3.2" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index d04882911..065abaec6 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -24,7 +24,7 @@ fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.2" fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index ba9fb7f75..4ad059d74 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] fvm_ipld_hamt = "0.6.1" fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } num = { version = "0.4", features = ["serde"] } num-traits = "0.2.14" num-derive = "0.3.3" @@ -37,7 +37,7 @@ castaway = "0.2.2" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.21", optional = true } +fvm_sdk = { version = "3.0.0-alpha.22", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } @@ -90,4 +90,4 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static", "pretty_env_logger"] +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static", "pretty_env_logger"] \ No newline at end of file diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 7205e4c8a..d61476ca2 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -24,6 +24,7 @@ use fvm_shared::sector::{ }; use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; +use fvm_shared::Response; use fvm_shared::{ActorID, MethodNum}; use num_traits::FromPrimitive; use serde::de::DeserializeOwned; @@ -318,69 +319,12 @@ where value: TokenAmount, gas_limit: Option, flags: SendFlags, - ) -> Result, ActorError> { + ) -> Result { if self.in_transaction { - return Err(actor_error!(assertion_failed; "send is not allowed during transaction")); + return Err(ErrorNumber::IllegalOperation); } - match fvm::send::send(to, method, params, value, gas_limit, flags) { - Ok(ret) => { - if ret.exit_code.is_success() { - Ok(ret.return_data) - } else { - // The returned code can't be simply propagated as it may be a system exit code. - // TODO: improve propagation once we return a RuntimeError. - // Ref https://github.com/filecoin-project/builtin-actors/issues/144 - let exit_code = match ret.exit_code { - // This means the called actor did something wrong. We can't "make up" a - // reasonable exit code. - ExitCode::SYS_MISSING_RETURN - | ExitCode::SYS_ILLEGAL_INSTRUCTION - | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, - // We don't expect any other system errors. - code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, - // Otherwise, pass it through. - code => code, - }; - Err(ActorError::unchecked_with_data( - exit_code, - format!( - "send to {} method {} aborted with code {}", - to, method, ret.exit_code - ), - ret.return_data, - )) - } - } - Err(err) => Err(match err { - // Some of these errors are from operations in the Runtime or SDK layer - // before or after the underlying VM send syscall. - ErrorNumber::NotFound => { - // This means that the receiving actor doesn't exist. - // TODO: we can't reasonably determine the correct "exit code" here. - actor_error!(unspecified; "receiver not found") - } - ErrorNumber::InsufficientFunds => { - // This means that the send failed because we have insufficient funds. We will - // get a _syscall error_, not an exit code, because the target actor will not - // run (and therefore will not exit). - actor_error!(insufficient_funds; "not enough funds") - } - ErrorNumber::LimitExceeded => { - // This means we've exceeded the recursion limit. - // TODO: Define a better exit code. - actor_error!(assertion_failed; "recursion limit exceeded") - } - ErrorNumber::ReadOnly => ActorError::unchecked( - ExitCode::USR_READ_ONLY, - "attempted to mutate state while in readonly mode".into(), - ), - err => { - // We don't expect any other syscall exit codes. - actor_error!(assertion_failed; "unexpected error: {}", err) - } - }), - } + fvm::send::send(to, method, params, value, gas_limit, flags) } fn new_actor_address(&mut self) -> Result { diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 7d874ffb7..75422bf33 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -49,6 +49,8 @@ pub(crate) mod empty; pub use empty::EMPTY_ARR_CID; use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::error::{ErrorNumber, ExitCode}; +use fvm_shared::Response; /// Runtime is the VM's internal runtime object. /// this is everything that is accessible to actors, beyond parameters. @@ -196,7 +198,64 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { params: Option, value: TokenAmount, ) -> Result, ActorError> { - self.send_generalized(to, method, params, value, None, SendFlags::empty()) + match self.send_generalized(to, method, params, value, None, SendFlags::empty()) { + Ok(ret) => { + if ret.exit_code.is_success() { + Ok(ret.return_data) + } else { + // The returned code can't be simply propagated as it may be a system exit code. + // TODO: improve propagation once we return a RuntimeError. + // Ref https://github.com/filecoin-project/builtin-actors/issues/144 + let exit_code = match ret.exit_code { + // This means the called actor did something wrong. We can't "make up" a + // reasonable exit code. + ExitCode::SYS_MISSING_RETURN + | ExitCode::SYS_ILLEGAL_INSTRUCTION + | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, + // We don't expect any other system errors. + code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, + // Otherwise, pass it through. + code => code, + }; + Err(ActorError::unchecked_with_data( + exit_code, + format!( + "send to {} method {} aborted with code {}", + to, method, ret.exit_code + ), + ret.return_data, + )) + } + } + Err(err) => Err(match err { + // Some of these errors are from operations in the Runtime or SDK layer + // before or after the underlying VM send syscall. + ErrorNumber::NotFound => { + // This means that the receiving actor doesn't exist. + // TODO: we can't reasonably determine the correct "exit code" here. + actor_error!(unspecified; "receiver not found") + } + ErrorNumber::InsufficientFunds => { + // This means that the send failed because we have insufficient funds. We will + // get a _syscall error_, not an exit code, because the target actor will not + // run (and therefore will not exit). + actor_error!(insufficient_funds; "not enough funds") + } + ErrorNumber::LimitExceeded => { + // This means we've exceeded the recursion limit. + // TODO: Define a better exit code. + actor_error!(assertion_failed; "recursion limit exceeded") + } + ErrorNumber::ReadOnly => ActorError::unchecked( + ExitCode::USR_READ_ONLY, + "attempted to mutate state while in readonly mode".into(), + ), + err => { + // We don't expect any other syscall exit codes. + actor_error!(assertion_failed; "unexpected error: {}", err) + } + }), + } } /// Generalizes [`Runtime::send`] and [`Runtime::send_read_only`] to allow the caller to @@ -209,7 +268,7 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { value: TokenAmount, gas_limit: Option, flags: SendFlags, - ) -> Result, ActorError>; + ) -> Result; /// Computes an address for a new actor. The returned address is intended to uniquely refer to /// the actor even in the event of a chain re-org (whereas an ID-address might refer to a diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index eb18df3de..14320c1b4 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -22,7 +22,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::error::ExitCode; +use fvm_shared::error::{ErrorNumber, ExitCode}; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::RANDOMNESS_LENGTH; use fvm_shared::sector::{ @@ -31,7 +31,7 @@ use fvm_shared::sector::{ }; use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; -use fvm_shared::{ActorID, MethodNum}; +use fvm_shared::{ActorID, MethodNum, Response}; use multihash::derive::Multihash; use multihash::MultihashDigest; @@ -1130,11 +1130,11 @@ impl Runtime for MockRuntime { value: TokenAmount, gas_limit: Option, send_flags: SendFlags, - ) -> Result, ActorError> { + ) -> Result { // TODO gas_limit is currently ignored, what should we do about it? self.require_in_call(); if self.in_transaction { - return Err(actor_error!(assertion_failed; "side-effect within transaction")); + return Ok(Response { exit_code: ExitCode::USR_ASSERTION_FAILED, return_data: None }); } assert!( @@ -1176,22 +1176,12 @@ impl Runtime for MockRuntime { { let mut balance = self.balance.borrow_mut(); if value > *balance { - return Err(ActorError::unchecked( - ExitCode::SYS_SENDER_STATE_INVALID, - format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance), - )); + return Err(ErrorNumber::InsufficientFunds); } *balance -= value; } - match expected_msg.exit_code { - ExitCode::OK => Ok(expected_msg.send_return), - x => Err(ActorError::unchecked_with_data( - x, - "Expected message Fail".to_string(), - expected_msg.send_return, - )), - } + Ok(Response { exit_code: expected_msg.exit_code, return_data: expected_msg.send_return }) } fn new_actor_address(&mut self) -> Result { diff --git a/state/Cargo.toml b/state/Cargo.toml index 364c05eea..46862107b 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,7 +27,7 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } fvm_ipld_encoding = "0.3.2" frc46_token = "3.0.0" fvm_ipld_blockstore = "0.1.1" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index aa24cf2b2..c14b99eff 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -39,7 +39,7 @@ fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.2", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.15", default-features = false } +fvm_shared = { version = "3.0.0-alpha.16", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 40c997e07..5df749675 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -49,7 +49,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::error::ExitCode; +use fvm_shared::error::{ErrorNumber, ExitCode}; use fvm_shared::event::ActorEvent; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::Randomness; @@ -61,7 +61,7 @@ use fvm_shared::sector::{ use fvm_shared::smooth::FilterEstimate; use fvm_shared::sys::SendFlags; use fvm_shared::version::NetworkVersion; -use fvm_shared::{ActorID, MethodNum, IPLD_RAW, METHOD_CONSTRUCTOR, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, Response, IPLD_RAW, METHOD_CONSTRUCTOR, METHOD_SEND}; use regex::Regex; use serde::de::DeserializeOwned; use serde::{ser, Serialize}; @@ -998,7 +998,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { value: TokenAmount, _gas_limit: Option, mut send_flags: SendFlags, - ) -> Result, ActorError> { + ) -> Result { // replicate FVM by silently propagating read only flag to subcalls if self.read_only() { send_flags.set(SendFlags::READ_ONLY, true) @@ -1006,10 +1006,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { // TODO gas_limit is current ignored, what should we do about it? if !self.allow_side_effects { - return Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, - "Calling send is not allowed during side-effect lock".to_string(), - )); + return Ok(Response { exit_code: ExitCode::SYS_ASSERTION_FAILED, return_data: None }); } let new_actor_msg = InternalMessage { from: self.to(), to: *to, value, method, params }; @@ -1030,7 +1027,11 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { subinvocs.push(invoc); subinvocs }); - res + + Ok(Response { + exit_code: res.as_ref().err().map(|e| e.exit_code()).unwrap_or(ExitCode::OK), + return_data: res.unwrap_or_else(|mut e| e.take_data()), + }) } fn get_randomness_from_tickets( From 5bc990be1ba8165718a685e8a075ed3ea224a39e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 12 Jan 2023 21:37:00 -0800 Subject: [PATCH 245/339] feat: refactor precompile parameter reading (#1036) This patch: 1. Refactors the parameter reader to be less generic and easier to understand. 2. Refactors parameter reading to always use the reader (letting it take care of things like padding). 3. Adds a bunch of parameter reading tests. 4. Simplifies the ec_recover precompile. --- actors/evm/src/interpreter/precompiles/evm.rs | 184 ++++------ actors/evm/src/interpreter/precompiles/fvm.rs | 70 ++-- .../src/interpreter/precompiles/parameter.rs | 313 +++++++++--------- actors/evm/tests/call.rs | 14 +- 4 files changed, 253 insertions(+), 328 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index b521dbdb8..a547e5f73 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -1,76 +1,47 @@ +use std::ops::RangeInclusive; + use fil_actors_runtime::runtime::Runtime; -use fvm_shared::{ - bigint::BigUint, - crypto::{ - hash::SupportedHashes, - signature::{SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, - }, +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 uint::byteorder::{ByteOrder, LE}; -use crate::interpreter::{ - precompiles::{parameter::right_pad, PrecompileError}, - System, U256, -}; +use crate::interpreter::{precompiles::PrecompileError, System, U256}; -use super::{ - parameter::{PaddedChunks, U256Reader}, - PrecompileContext, PrecompileResult, -}; +use super::{parameter::ParameterReader, PrecompileContext, PrecompileResult}; -lazy_static::lazy_static! { - pub(crate) static ref SECP256K1: BigUint = BigUint::from_bytes_be(&hex_literal::hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); -} +const SECP256K1_RANGE: RangeInclusive = U256::from_u64(2) + ..=U256::from_u128_words( + 0xfffffffffffffffffffffffffffffffe, + 0xbaaedce6af48a03bbfd25e8cd0364141, + ); -/// recover a secp256k1 pubkey from a hash, recovery byte, and a signature -pub(super) fn ec_recover( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut input_params = U256Reader::new(input); - - let hash: [u8; SECP_SIG_MESSAGE_HASH_SIZE] = input_params.next_padded(); - let recovery_byte = input_params.next_param_padded::(); - let r = input_params.next_padded(); - let s = input_params.next_padded(); - let big_r = BigUint::from_bytes_be(&r); - let big_s = BigUint::from_bytes_be(&s); - - // recovery byte is a single byte value but is represented with 32 bytes, sad - let v = match recovery_byte { - Ok(re) => { - if matches!(re, 27 | 28) { - re - 27 - } else { - return Ok(Vec::new()); - } - } - _ => return Ok(Vec::new()), - }; +fn ec_recover_internal(system: &mut System, input: &[u8]) -> PrecompileResult { + let mut input_params = ParameterReader::new(input); + let hash: [u8; SECP_SIG_MESSAGE_HASH_SIZE] = input_params.read_fixed(); + let recovery_byte: u8 = input_params.read_param()?; + let r: U256 = input_params.read_param()?; + let s: U256 = input_params.read_param()?; - let valid = if big_r <= BigUint::one() || big_s <= BigUint::one() { - false - } else { - big_r <= *SECP256K1 && big_s <= *SECP256K1 && (v == 0 || v == 1) - }; + // Must be either 27 or 28 + let v = recovery_byte.checked_sub(27).ok_or(PrecompileError::InvalidInput)?; - if !valid { - return Ok(Vec::new()); + if v > 1 || !SECP256K1_RANGE.contains(&r) || !SECP256K1_RANGE.contains(&s) { + return Err(PrecompileError::InvalidInput); } let mut sig: [u8; SECP_SIG_LEN] = [0u8; 65]; - sig[..32].copy_from_slice(&r); - sig[32..64].copy_from_slice(&s); + r.to_big_endian(&mut sig[..32]); + s.to_big_endian(&mut sig[32..64]); sig[64] = v; - let pubkey = if let Ok(key) = system.rt.recover_secp_public_key(&hash, &sig) { - key - } else { - return Ok(Vec::new()); - }; + 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]); @@ -78,6 +49,16 @@ pub(super) fn ec_recover( 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, @@ -112,40 +93,21 @@ pub(super) fn modexp( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let input = right_pad(input, 96); - - // Follows go-ethereum by truncating bits to u64, ignoring other all other values in the first 24 bytes. - // Since we don't have any complexity functions or specific gas measurements of modexp in FEVM, - // we let values be whatever and have FEVM gas accounting be the one responsible for keeping things within reasonable limits. - // We _also_ will default with 0 (though this is already done with right padding above) since that is expected to be fine. - // Eth really relies heavily on gas checking being correct and safe for client nodes... - fn read_bigint_len(input: &[u8], start: usize) -> Result { - let digits = BigUint::from_bytes_be(&input[start..start + 32]); - let mut digits = digits.iter_u64_digits(); - // truncate to 64 bits - digits - .next() - .or(Some(0)) - // wont ever error here, just a type conversion - .ok_or(PrecompileError::OutOfGas) - .and_then(|d| u32::try_from(d).map_err(|_| PrecompileError::OutOfGas)) - .map(|d| d as usize) - } + let mut reader = ParameterReader::new(input); - let base_len = read_bigint_len(&input, 0)?; - let exponent_len = read_bigint_len(&input, 32)?; - let mod_len = read_bigint_len(&input, 64)?; + // 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_param::()? as usize; + let exponent_len = reader.read_param::()? as usize; + let mod_len = reader.read_param::()? as usize; if base_len == 0 && mod_len == 0 { return Ok(Vec::new()); } - let input = if input.len() > 96 { &input[96..] } else { &[] }; - let input = right_pad(input, base_len + exponent_len + mod_len); - let base = BigUint::from_bytes_be(&input[0..base_len]); - let exponent = BigUint::from_bytes_be(&input[base_len..exponent_len + base_len]); - let modulus = - BigUint::from_bytes_be(&input[base_len + exponent_len..mod_len + base_len + exponent_len]); + 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 @@ -181,9 +143,9 @@ pub(super) fn ec_add( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params: PaddedChunks = PaddedChunks::new(input); - let point1 = input_params.next_param_padded()?; - let point2 = input_params.next_param_padded()?; + let mut input_params = ParameterReader::new(input); + let point1: G1 = input_params.read_param()?; + let point2: G1 = input_params.read_param()?; curve_to_vec(point1 + point2) } @@ -194,14 +156,9 @@ pub(super) fn ec_mul( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let input = right_pad(input, 96); - let mut input_params: PaddedChunks = PaddedChunks::new(&input); - let point = input_params.next_param_padded()?; - - let scalar = { - let data = U256::from_big_endian(&input_params.remaining_slice()[..32]); - Fr::new_mul_factor(data.into()) - }; + let mut input_params = ParameterReader::new(input); + let point: G1 = input_params.read_param()?; + let scalar: Fr = input_params.read_param()?; curve_to_vec(point * scalar) } @@ -213,19 +170,19 @@ pub(super) fn ec_pairing( _: PrecompileContext, ) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { - let mut i_in = U256Reader::new(input); + let mut reader = ParameterReader::new(input); - let x = Fq::from_u256(i_in.next_into_param::()?.into())?; - let y = Fq::from_u256(i_in.next_into_param::()?.into())?; + let x: Fq = reader.read_param()?; + let y: Fq = reader.read_param()?; let twisted_x = { - let b = Fq::from_u256(i_in.next_into_param::()?.into())?; - let a = Fq::from_u256(i_in.next_into_param::()?.into())?; + let b: Fq = reader.read_param()?; + let a: Fq = reader.read_param()?; Fq2::new(a, b) }; let twisted_y = { - let b = Fq::from_u256(i_in.next_into_param::()?.into())?; - let a = Fq::from_u256(i_in.next_into_param::()?.into())?; + let b: Fq = reader.read_param()?; + let a: Fq = reader.read_param()?; Fq2::new(a, b) }; @@ -250,6 +207,8 @@ pub(super) fn ec_pairing( 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); } @@ -338,25 +297,6 @@ mod tests { } } - #[test] - fn padding() { - let input = b"foo bar boxy"; - let mut input = Vec::from(*input); - for i in 12..64 { - let mut expected = input.clone(); - expected.resize(i, 0); - - let res = right_pad(&input, i); - assert_eq!(&*res, &expected); - - let no_padding = b"foo bar boxy ".to_vec(); - let res = right_pad(&no_padding, 12); - // do nothing - assert_eq!(&res, &no_padding); - input.push(0); - } - } - #[test] fn bn_recover() { let mut rt = MockRuntime::default(); diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 512aa403d..b5a31ea8f 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -3,16 +3,9 @@ use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags}; use num_traits::FromPrimitive; -use crate::interpreter::{ - instructions::call::CallKind, - precompiles::{ - parameter::{right_pad, Parameter}, - NativeType, - }, - System, U256, -}; +use crate::interpreter::{instructions::call::CallKind, precompiles::NativeType, System, U256}; -use super::{parameter::U256Reader, PrecompileContext, PrecompileError, PrecompileResult}; +use super::{parameter::ParameterReader, PrecompileContext, PrecompileError, PrecompileResult}; /// Read right padded BE encoded low u64 ID address from a u256 word. /// Returns variant of [`BuiltinType`] encoded as a u256 word. @@ -22,15 +15,8 @@ pub(super) fn get_actor_type( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - // should never panic, pad to 32 bytes then read exactly 32 bytes - let id_bytes: [u8; 32] = right_pad(input, 32)[..32].as_ref().try_into().unwrap(); - let id = match Parameter::::try_from(&id_bytes) { - Ok(id) => id.0, - Err(_) => { - log::debug!(target: "evm", "ID address parsing failed: {}", hex::encode(id_bytes)); - return Err(PrecompileError::InvalidInput); - } - }; + let mut reader = ParameterReader::new(input); + let id: u64 = reader.read_param()?; // resolve type from code CID let builtin_type = system @@ -85,7 +71,7 @@ pub(super) fn get_randomness( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = U256Reader::new(input); + let mut input_params = ParameterReader::new(input); #[derive(num_derive::FromPrimitive)] #[repr(i32)] @@ -94,14 +80,12 @@ pub(super) fn get_randomness( Beacon = 1, } - let randomness_type = RandomnessType::from_i32(input_params.next_param_padded::()?); - let personalization = input_params.next_param_padded::()?; - let rand_epoch = input_params.next_param_padded::()?; - let entropy_len = input_params.next_param_padded::()?; + let randomness_type = RandomnessType::from_i32(input_params.read_param::()?); + let personalization = input_params.read_param::()?; + let rand_epoch = input_params.read_param::()?; + let entropy_len = input_params.read_param::()? as usize; - debug_assert_eq!(input_params.chunks_read(), 4); - - let entropy = right_pad(input_params.remaining_slice(), entropy_len as usize); + let entropy = input_params.read_padded(entropy_len); let randomness = match randomness_type { Some(RandomnessType::Chain) => system @@ -125,8 +109,8 @@ pub(super) fn lookup_delegated_address( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut id_bytes = U256Reader::new(input); - let id = id_bytes.next_param_padded::()?; + let mut id_bytes = ParameterReader::new(input); + let id = id_bytes.read_param::()?; let address = system.rt.lookup_delegated_address(id); let ab = match address { @@ -144,10 +128,11 @@ pub(super) fn resolve_address( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = U256Reader::new(input); + let mut input_params = ParameterReader::new(input); - let len = input_params.next_param_padded::()? as usize; - let addr = match Address::from_bytes(&right_pad(input_params.remaining_slice(), len)) { + let len = input_params.read_param::()? as usize; + let addr_bytes = input_params.read_padded(len); + let addr = match Address::from_bytes(&addr_bytes) { Ok(o) => o, Err(e) => { log::debug!(target: "evm", "Address parsing failed: {e}"); @@ -188,29 +173,26 @@ pub(super) fn call_actor( return Err(PrecompileError::CallForbidden); } - let mut input_params = U256Reader::new(input); + let mut input_params = ParameterReader::new(input); - let method: u64 = input_params.next_param_padded()?; + let method: u64 = input_params.read_param()?; - let value: U256 = input_params.next_padded().into(); + let value: U256 = input_params.read_param()?; - let flags: u64 = input_params.next_param_padded()?; + let flags: u64 = input_params.read_param()?; let flags = SendFlags::from_bits(flags).ok_or(PrecompileError::InvalidInput)?; - let codec: u64 = input_params.next_param_padded()?; + let codec: u64 = input_params.read_param()?; - let send_data_size = input_params.next_param_padded::()? as usize; - let address_size = input_params.next_param_padded::()? as usize; + let send_data_size = input_params.read_param::()? as usize; + let address_size = input_params.read_param::()? as usize; // ------ Begin Call ------- let result = { - let start = input_params.remaining_slice(); - let bytes = right_pad(start, send_data_size + address_size); - - let input_data = &bytes[..send_data_size]; - let address = &bytes[send_data_size..send_data_size + address_size]; - let address = Address::from_bytes(address).map_err(|_| PrecompileError::InvalidInput)?; + let input_data = input_params.read_padded(send_data_size); + let address = input_params.read_padded(address_size); + let address = Address::from_bytes(&address).map_err(|_| PrecompileError::InvalidInput)?; // TODO only CBOR or "nothing" for now let params = match codec { diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/interpreter/precompiles/parameter.rs index 97a51a051..1e60eb713 100644 --- a/actors/evm/src/interpreter/precompiles/parameter.rs +++ b/actors/evm/src/interpreter/precompiles/parameter.rs @@ -1,6 +1,7 @@ -use std::{borrow::Cow, slice::ChunksExact}; +use std::borrow::Cow; -use substrate_bn::{AffineG1, FieldError, Fq, Group, GroupError, G1}; +use fvm_shared::bigint::BigUint; +use substrate_bn::{AffineG1, FieldError, Fq, Fr, Group, GroupError, G1}; use crate::interpreter::U256; @@ -18,212 +19,210 @@ impl From for PrecompileError { } } -/// Pad out to len as needed, but does not slice output to len -pub(super) fn right_pad<'a>(input: impl Into>, len: usize) -> Cow<'a, [u8]> { - let mut input: Cow<[u8]> = input.into(); - let input_len = input.len(); - if len > input_len { - input.to_mut().resize(len, 0); - } - input -} - -/// ensures top bits are zeroed -pub fn assert_zero_bytes(src: &[u8]) -> Result<(), PrecompileError> { - if src[..S] != [0u8; S] { - Err(PrecompileError::InvalidInput) - } else { - Ok(()) - } +pub(super) trait Parameter: Sized { + fn read(reader: &mut ParameterReader) -> Result; } -pub(super) struct Parameter(pub T); - -impl<'a> TryFrom<&'a [u8; 64]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 64]) -> Result { - let x = Fq::from_u256(U256::from_big_endian(&value[0..32]).into())?; - let y = Fq::from_u256(U256::from_big_endian(&value[32..64]).into())?; +impl Parameter for G1 { + fn read(reader: &mut ParameterReader) -> Result { + let x: Fq = reader.read_param()?; + let y: Fq = reader.read_param()?; - Ok(if x.is_zero() && y.is_zero() { - Parameter(G1::zero()) - } else { - Parameter(AffineG1::new(x, y)?.into()) - }) - } -} - -impl<'a> From<&'a [u8; 32]> for Parameter<[u8; 32]> { - fn from(value: &'a [u8; 32]) -> Self { - Self(*value) + Ok(if x.is_zero() && y.is_zero() { G1::zero() } else { AffineG1::new(x, y)?.into() }) } } -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<28>(value)?; - // Type ensures our remaining len == 4 - Ok(Self(u32::from_be_bytes(value[28..].try_into().unwrap()))) +impl Parameter for Fq { + fn read(reader: &mut ParameterReader) -> Result { + Ok(Fq::from_slice(&reader.read_fixed::<32>())?) } } -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<28>(value)?; - // Type ensures our remaining len == 4 - Ok(Self(i32::from_be_bytes(value[28..].try_into().unwrap()))) +impl Parameter for Fr { + fn read(reader: &mut ParameterReader) -> Result { + Ok(Fr::from_slice(&reader.read_fixed::<32>())?) } } -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<31>(value)?; - Ok(Self(value[31])) +impl Parameter for U256 { + fn read(reader: &mut ParameterReader) -> Result { + Ok(U256::from(reader.read_fixed::<32>())) } } -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<24>(value)?; - // Type ensures our remaining len == 8 - Ok(Self(u64::from_be_bytes(value[24..].try_into().unwrap()))) +impl Parameter for [u8; 32] { + fn read(reader: &mut ParameterReader) -> Result { + Ok(reader.read_fixed()) } } -impl<'a> TryFrom<&'a [u8; 32]> for Parameter { - type Error = PrecompileError; - - fn try_from(value: &'a [u8; 32]) -> Result { - assert_zero_bytes::<24>(value)?; - // Type ensures our remaining len == 8 - Ok(Self(i64::from_be_bytes(value[24..].try_into().unwrap()))) +impl Parameter for u8 { + fn read(reader: &mut ParameterReader) -> Result { + reader.drop_zeros::<31>()?; + Ok(reader.read_byte()) } } -impl<'a> From<&'a [u8; 32]> for Parameter { - fn from(value: &'a [u8; 32]) -> Self { - Self(U256::from_big_endian(value)) - } +macro_rules! impl_param_int { + ($($t:ty)*) => { + $( + impl Parameter for $t { + fn read(reader: &mut ParameterReader) -> 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())) + } + } + )* + }; } -pub(super) type U256Reader<'a> = PaddedChunks<'a, u8, 32>; +impl_param_int!(u16 i16 u32 i32 u64 i64); -// will be nicer with https://github.com/rust-lang/rust/issues/74985 -/// Wrapper around `ChunksExact` that pads instead of overflowing. -/// Also provides a nice API interface for reading Parameters from input -pub(super) struct PaddedChunks<'a, T: Sized + Copy, const CHUNK_SIZE: usize> { - slice: &'a [T], - chunks: ChunksExact<'a, T>, - exhausted: bool, +/// Provides a nice API interface for reading Parameters from input. This API treats the input as if +/// it is followed by infinite zeros. +pub(super) struct ParameterReader<'a> { + slice: &'a [u8], } -impl<'a, T: Sized + Copy, const CHUNK_SIZE: usize> PaddedChunks<'a, T, CHUNK_SIZE> { - pub(super) fn new(slice: &'a [T]) -> Self { - Self { slice, chunks: slice.chunks_exact(CHUNK_SIZE), exhausted: false } - } - - pub fn next(&mut self) -> Option<&[T; CHUNK_SIZE]> { - self.chunks.next().map(|s| s.try_into().unwrap()) +impl<'a> ParameterReader<'a> { + pub(super) fn new(slice: &'a [u8]) -> Self { + ParameterReader { slice } } - pub fn next_padded(&mut self) -> [T; CHUNK_SIZE] - where - T: Default, - { - if self.chunks.len() > 0 { - self.next().copied().unwrap_or([T::default(); CHUNK_SIZE]) - } else if self.exhausted() { - [T::default(); CHUNK_SIZE] + /// Drop a fixed number of bytes, and return an error if said bytes are not zeros. + pub fn drop_zeros(&mut self) -> Result<(), PrecompileError> { + 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 { - self.exhausted = true; - let mut buf = [T::default(); CHUNK_SIZE]; - let remainder = self.chunks.remainder(); - buf[..remainder.len()].copy_from_slice(remainder); - buf + Err(PrecompileError::InvalidInput) } } - pub fn exhausted(&self) -> bool { - self.exhausted - } - - pub fn remaining_len(&self) -> usize { - if self.exhausted { - 0 + /// 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 { - self.chunks.len() * CHUNK_SIZE + self.chunks.remainder().len() + 0 } } - pub fn chunks_read(&self) -> usize { - let total_chunks = self.slice.len() / CHUNK_SIZE; - let unread_chunks = self.chunks.len(); - total_chunks - unread_chunks + /// 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) + } } - // remaining unpadded slice of unread items - pub fn remaining_slice(&self) -> &[T] { - let start = self.slice.len() - self.remaining_len(); - &self.slice[start..] + /// 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 } - // // tries to read an unpadded and exact (aligned) parameter - #[allow(unused)] - pub fn next_param(&mut self) -> Result + /// Read a single parameter from the input. The parameter's type decides how much input it needs + /// to read. + /// + /// Most parameters will read in 32 byte chunks, but that's up to the parameter's + /// implementation. + pub fn read_param(&mut self) -> Result where - Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, + V: Parameter, { - Parameter::::try_from(self.next().ok_or(PrecompileError::IncorrectInputSize)?) - .map(|a| a.0) + Parameter::read(self) } +} - // tries to read a parameter with padding - pub fn next_param_padded(&mut self) -> Result - where - T: Default, - Parameter: for<'from> TryFrom<&'from [T; CHUNK_SIZE], Error = PrecompileError>, - { - Parameter::::try_from(&self.next_padded()).map(|a| a.0) +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_read_fixed() { + let mut reader = ParameterReader::new(&[1, 2, 3]); + assert_eq!(reader.read_fixed::<2>(), [1, 2]); + assert_eq!(reader.read_fixed::<0>(), []); + assert_eq!(reader.read_fixed::<5>(), [3u8, 0, 0, 0, 0]); + assert_eq!(reader.read_fixed::<3>(), [0, 0, 0]); } - #[allow(unused)] - pub fn next_into_param_padded(&mut self) -> V - where - T: Default, - Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, - { - Parameter::::from(&self.next_padded()).0 + #[test] + fn test_right_pad() { + let mut reader = ParameterReader::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][..]); } - // read a parameter with padding - pub fn next_into_param(&mut self) -> Result - where - T: Default, - Parameter: for<'from> From<&'from [T; CHUNK_SIZE]>, - { - self.next().map(|p| Parameter::::from(p).0).ok_or(PrecompileError::IncorrectInputSize) + #[test] + fn test_int() { + let mut data = vec![0u8; 37]; + data[31] = 1; + let mut reader = ParameterReader::new(&data); + assert_eq!(reader.read_param::().unwrap(), 1); + assert_eq!(reader.read_param::().unwrap(), 0); + + // Expect this to overflow now. + data[0] = 1; + let mut reader = ParameterReader::new(&data); + assert!(matches!(reader.read_param::().unwrap_err(), PrecompileError::InvalidInput)); } -} -#[cfg(test)] -mod test { - use super::*; + #[test] + fn test_big_int() { + let mut reader = ParameterReader::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_assert_zero_bytes() { - let mut bytes = [0u8; 32]; - assert_zero_bytes::<32>(&bytes).unwrap(); - bytes[31] = 1; - assert_zero_bytes::<31>(&bytes).unwrap(); - assert_zero_bytes::<32>(&bytes).expect_err("expected error from nonzero byte"); + fn test_byte() { + let mut reader = ParameterReader::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/tests/call.rs b/actors/evm/tests/call.rs index a9884f89b..4b69ffce2 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -487,6 +487,11 @@ fn test_callactor_inner(exit_code: ExitCode) { exit_code, ); + /// ensures top bits are zeroed + pub fn assert_zero_bytes(src: &[u8]) { + assert_eq!(src[..S], [0u8; S]); + } + // invoke let result = util::invoke_contract(&mut rt, &contract_params); @@ -504,30 +509,29 @@ fn test_callactor_inner(exit_code: ExitCode) { impl CallActorReturn { pub fn read(src: &[u8]) -> Self { - use fil_actor_evm::interpreter::precompiles::parameter::assert_zero_bytes; assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values, got {:?}", src); let bytes = &src[..32]; let exit_code = { - assert_zero_bytes::<4>(bytes).unwrap(); + assert_zero_bytes::<4>(bytes); ExitCode::new(u32::from_be_bytes(bytes[28..32].try_into().unwrap())) }; let bytes = &src[32..64]; let codec = { - assert_zero_bytes::<8>(bytes).unwrap(); + assert_zero_bytes::<8>(bytes); u64::from_be_bytes(bytes[24..32].try_into().unwrap()) }; let bytes = &src[64..96]; let offset = { - assert_zero_bytes::<4>(bytes).unwrap(); + assert_zero_bytes::<4>(bytes); u32::from_be_bytes(bytes[28..32].try_into().unwrap()) }; let bytes = &src[96..128]; let size = { - assert_zero_bytes::<4>(bytes).unwrap(); + assert_zero_bytes::<4>(bytes); u32::from_be_bytes(bytes[28..32].try_into().unwrap()) }; let data = Vec::from(&src[offset as usize..(offset + size) as usize]); From 9ad083d37caa53fcdbfe5e7994342fd5591a3ea0 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 12 Jan 2023 22:18:59 -0800 Subject: [PATCH 246/339] EVM: Test resolve_address precompile (#1017) * remaining slice wip test * add tests for resolve_delegate * rename test * add tests for bls, secp, and smaller input but expect padding to make valid * undo rename in unrelated test * misspell in previous commit * document address parsing failure case * rebase with next * small fixes, test for invalid second param (address parsing) --- actors/evm/src/interpreter/precompiles/fvm.rs | 5 +- actors/evm/tests/precompile.rs | 119 +++++++++++++++++- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index b5a31ea8f..b816e6b3f 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -120,9 +120,10 @@ pub(super) fn lookup_delegated_address( Ok(ab) } -/// Reads a FIL (i.e. f0xxx, f4x1xxx) encoded address +/// 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. +/// 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], diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 8f2031855..2457603b8 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -3,8 +3,8 @@ mod asm; use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ - MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, - MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, + new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, + MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, }; use fvm_shared::address::Address as FILAddress; @@ -282,6 +282,121 @@ return rt.reset(); } +#[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.add_delegated_address(evm_target, 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.add_delegated_address(unknown_target, 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 = { + let addr = addr.to_bytes(); + let mut v = U256::from(addr.len()).to_bytes().to_vec(); + v.extend_from_slice(&addr); + v + }; + 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![]); + + // valid with extra padding + rt.expect_gas_available(10_000_000_000u64); + let input = { + let addr = evm_del.to_bytes(); + // address length to read + let mut v = U256::from(addr.len()).to_bytes().to_vec(); + // address itself + v.extend_from_slice(&addr); + // extra padding + v.extend_from_slice(&[0; 10]); + v + }; + let result = util::invoke_contract(&mut rt, &input); + rt.verify(); + assert_eq!(id_to_vec(&evm_target), &result[1..]); + assert_eq!(1, result[0]); + rt.reset(); + + // valid but needs padding + rt.expect_gas_available(10_000_000_000u64); + let input = { + // EVM f4 but subaddress len is 12 bytes + // FEEDFACECAFEBEEF00000000 + let addr = FILAddress::new_delegated(10, &util::CONTRACT_ADDRESS[..12]).unwrap(); + let addr = addr.to_bytes(); + + let read_len = addr.len() + 8; + let mut v = U256::from(read_len).to_bytes().to_vec(); + // address itself + v.extend_from_slice(&addr); + v + }; + let result = util::invoke_contract(&mut rt, &input); + rt.verify(); + assert_eq!(id_to_vec(&evm_target), &result[1..]); + assert_eq!(1, result[0]); + rt.reset(); + + // 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_failure() { let bytecode = resolve_address_contract(); From 9fd5ea5a5d931881a103c99e9c6e7f9657653a36 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 12 Jan 2023 22:34:52 -0800 Subject: [PATCH 247/339] fix: correctly check the secp range (#1038) Co-authored-by: mriise --- actors/evm/src/interpreter/precompiles/evm.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index a547e5f73..a1e962156 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -1,4 +1,4 @@ -use std::ops::RangeInclusive; +use std::ops::Range; use fil_actors_runtime::runtime::Runtime; use fvm_shared::crypto::{ @@ -13,11 +13,17 @@ use crate::interpreter::{precompiles::PrecompileError, System, U256}; use super::{parameter::ParameterReader, PrecompileContext, PrecompileResult}; -const SECP256K1_RANGE: RangeInclusive = U256::from_u64(2) - ..=U256::from_u128_words( - 0xfffffffffffffffffffffffffffffffe, - 0xbaaedce6af48a03bbfd25e8cd0364141, - ); +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 = ParameterReader::new(input); @@ -29,6 +35,7 @@ fn ec_recover_internal(system: &mut System, input: &[u8]) -> Pr // 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); } From 54417acd690e5542dcad7d50565294f20a746755 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 07:33:13 -0800 Subject: [PATCH 248/339] fix: remove CallCode (#1041) It was never implemented and is deprecated. fixes https://github.com/filecoin-project/ref-fvm/issues/870 --- actors/evm/src/interpreter/execution.rs | 1 - .../evm/src/interpreter/instructions/call.rs | 27 +------------------ .../evm/src/interpreter/instructions/mod.rs | 1 - 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index bfc689b97..05dfa0a14 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -225,7 +225,6 @@ pub mod opcodes { // 0xEF Reserved for EIP-3541 0xf0: CREATE, 0xf1: CALL, - 0xf2: CALLCODE, 0xf3: RETURN, 0xf4: DELEGATECALL, 0xf5: CREATE2, diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index f9f22f4fd..6e77fba34 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -17,7 +17,7 @@ use { crate::interpreter::ExecutionState, crate::interpreter::System, crate::interpreter::U256, - crate::{DelegateCallParams, Method, EVM_CONTRACT_EXECUTION_ERROR}, + crate::{DelegateCallParams, Method}, fil_actors_runtime::runtime::builtins::Type, fil_actors_runtime::runtime::Runtime, fil_actors_runtime::ActorError, @@ -30,7 +30,6 @@ pub enum CallKind { Call, DelegateCall, StaticCall, - CallCode, } pub fn calldataload( @@ -112,26 +111,6 @@ pub fn call_call( ) } -#[inline] -pub fn call_callcode( - 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::CallCode, - (gas, dst, value, input_offset, input_size, output_offset, output_size), - ) -} - #[inline] pub fn call_delegatecall( state: &mut ExecutionState, @@ -311,10 +290,6 @@ pub fn call_generic( .to_string(), )), }, - CallKind::CallCode => Err(ActorError::unchecked( - EVM_CONTRACT_EXECUTION_ERROR, - "unsupported opcode".to_string(), - )), }; let (code, data) = match call_result { Ok(result) => (1, result), diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 693bf8205..36ef2a27b 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -344,7 +344,6 @@ 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! { CALLCODE(gas, dst, value, ioff, isz, ooff, osz) => call::call_callcode } 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 } From b9343f9605f85566663bf5fed2586597b45c9efa Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 13 Jan 2023 08:52:26 -0800 Subject: [PATCH 249/339] EVM: Pad ripemd160 precompile to 32 bytes (#1040) pad ripemd160 out to 32 bytes --- actors/evm/src/interpreter/precompiles/evm.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index a1e962156..4ed061b81 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -81,7 +81,11 @@ pub(super) fn ripemd160( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - Ok(system.rt.hash(SupportedHashes::Ripemd160, input)) + let mut out = vec![0; 12]; + let hash = system.rt.hash(SupportedHashes::Ripemd160, input); + out.extend_from_slice(&hash); + debug_assert_eq!(out.len(), 32); + Ok(out) } /// data copy @@ -365,7 +369,7 @@ mod tests { let mut rt = MockRuntime::default(); let mut system = System::create(&mut rt).unwrap(); - let expected = hex!("4cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); + let expected = hex!("0000000000000000000000004cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); let res = hash(&mut system, input, PrecompileContext::default()).unwrap(); assert_eq!(&res, &expected); } From a50a77c5ca4abde9afa413b2506581e9a07618ef Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 13 Jan 2023 11:53:41 -0500 Subject: [PATCH 250/339] EVM: Improve the call tests (#1043) * Don't use the same methodNum as expected return data to avoid confusion * Include the success/failure of the call in the return data * Take methodNum and expected success as parameters for more extensible tests --- actors/evm/tests/call.rs | 180 ++++++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 79 deletions(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 4b69ffce2..096ce83c0 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -6,13 +6,13 @@ use evm::EVM_CONTRACT_REVERTED; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::*; use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_ipld_encoding::{BytesSer, DAG_CBOR, IPLD_RAW}; +use fvm_ipld_encoding::{BytesSer, IPLD_RAW}; use fvm_shared::address::Address as FILAddress; use fvm_shared::bigint::Zero; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sys::SendFlags; -use fvm_shared::METHOD_SEND; +use fvm_shared::{MethodNum, METHOD_SEND}; mod util; @@ -403,14 +403,18 @@ push4 0xffffffff # call_actor must be from delegatecall delegatecall -# copy result to mem 0x00 (overwrites input data) +# write exit code memory +push1 0x00 # offset +mstore8 + returndatasize -push1 0x00 -push1 0x00 +push1 0x00 # offset +push1 0x01 # dest offset (we have already written the exit code of the call) returndatacopy -# return -returndatasize +returndatasize +push1 0x01 # (add 1 to the returndatasize to accommodate for the exitcode) +add push1 0x00 return "#; @@ -421,16 +425,16 @@ return #[test] fn test_callactor_success() { // Should work if the called actor succeeds. - test_callactor_inner(ExitCode::OK) + test_callactor_inner(2048, ExitCode::OK, true) } #[test] fn test_callactor_revert() { - // Should propegate the return value if the called actor fails. - test_callactor_inner(EVM_CONTRACT_REVERTED) + // Should propagate the return value if the called actor fails. + test_callactor_inner(2048, EVM_CONTRACT_REVERTED, true) } -fn test_callactor_inner(exit_code: ExitCode) { +fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_input: bool) { let contract = callactor_proxy_contract(); const CALLACTOR_NUM_PARAMS: usize = 6; @@ -442,10 +446,10 @@ fn test_callactor_inner(exit_code: ExitCode) { let target = FILAddress::new_id(target_id); rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); - // dest + method 0x42 with no data + // dest + method with no data let mut contract_params = Vec::new(); - let method = U256::from(0x42); + let method = U256::from(method_num); let value = U256::from(0); let send_flags = SendFlags::default(); let codec = U256::from(0); @@ -472,85 +476,103 @@ fn test_callactor_inner(exit_code: ExitCode) { ); // expected return data - let mut return_data = vec![0u8; 32]; - return_data[31] = 0x42; + // Test with a codec _other_ than DAG_CBOR, to make sure we are actually passing the returned codec + let some_codec = 0x42; + let send_return = IpldBlock { codec: some_codec, data: vec![0xde, 0xad, 0xbe, 0xef] }; rt.expect_gas_available(10_000_000_000u64); - rt.expect_send_generalized( - target, - 0x42, - make_raw_params(proxy_call_input_data), - TokenAmount::zero(), - Some(0xffffffff), - send_flags, - Some(IpldBlock { codec: DAG_CBOR, data: return_data }), - exit_code, - ); + if valid_call_input { + // We only get to the send_generalized if the call params were valid + rt.expect_send_generalized( + target, + method_num, + make_raw_params(proxy_call_input_data), + TokenAmount::zero(), + Some(0xffffffff), + send_flags, + Some(send_return.clone()), + exit_code, + ); + } /// ensures top bits are zeroed - pub fn assert_zero_bytes(src: &[u8]) { + fn assert_zero_bytes(src: &[u8]) { assert_eq!(src[..S], [0u8; S]); } // invoke - let result = util::invoke_contract(&mut rt, &contract_params); - - // assert return + let mut result = util::invoke_contract(&mut rt, &contract_params); + rt.verify(); + rt.reset(); - #[derive(Debug, PartialEq, Eq)] - struct CallActorReturn { - exit_code: ExitCode, - codec: u64, - data_offset: u32, - data_size: u32, - data: Vec, - } + let call_result = result.remove(0); + if !valid_call_input { + // 0 is failure + assert_eq!(call_result, 0); + } else { + // 1 is success + assert_eq!(call_result, 1); + + // if call succeeded, we should have a return that we assert over + + #[derive(Debug, PartialEq, Eq)] + struct CallActorReturn { + exit_code: ExitCode, + codec: u64, + data_offset: u32, + data_size: u32, + data: Vec, + } - impl CallActorReturn { - pub fn read(src: &[u8]) -> Self { - assert!(src.len() >= 4 * 32, "expected to read at least 4 U256 values, got {:?}", src); - - let bytes = &src[..32]; - let exit_code = { - assert_zero_bytes::<4>(bytes); - ExitCode::new(u32::from_be_bytes(bytes[28..32].try_into().unwrap())) - }; - - let bytes = &src[32..64]; - let codec = { - assert_zero_bytes::<8>(bytes); - u64::from_be_bytes(bytes[24..32].try_into().unwrap()) - }; - - let bytes = &src[64..96]; - let offset = { - assert_zero_bytes::<4>(bytes); - u32::from_be_bytes(bytes[28..32].try_into().unwrap()) - }; - - let bytes = &src[96..128]; - let size = { - assert_zero_bytes::<4>(bytes); - u32::from_be_bytes(bytes[28..32].try_into().unwrap()) - }; - let data = Vec::from(&src[offset as usize..(offset + size) as usize]); - - Self { exit_code, codec, data_offset: offset, data_size: size, data } + impl CallActorReturn { + pub fn read(src: &[u8]) -> Self { + assert!( + src.len() >= 4 * 32, + "expected to read at least 4 U256 values, got {:?}", + src + ); + + let bytes = &src[..32]; + let exit_code = { + assert_zero_bytes::<4>(bytes); + ExitCode::new(u32::from_be_bytes(bytes[28..32].try_into().unwrap())) + }; + + let bytes = &src[32..64]; + let codec = { + assert_zero_bytes::<8>(bytes); + u64::from_be_bytes(bytes[24..32].try_into().unwrap()) + }; + + let bytes = &src[64..96]; + let offset = { + assert_zero_bytes::<4>(bytes); + u32::from_be_bytes(bytes[28..32].try_into().unwrap()) + }; + + let bytes = &src[96..128]; + let size = { + assert_zero_bytes::<4>(bytes); + u32::from_be_bytes(bytes[28..32].try_into().unwrap()) + }; + let data = Vec::from(&src[offset as usize..(offset + size) as usize]); + + Self { exit_code, codec, data_offset: offset, data_size: size, data } + } } - } - let result = CallActorReturn::read(&result); - let expected = CallActorReturn { - exit_code, - codec: DAG_CBOR, - data_offset: 128, - data_size: 32, - data: U256::from(0x42).to_bytes().to_vec(), - }; - rt.verify(); - assert_eq!(result, expected); - rt.reset(); + let result = CallActorReturn::read(&result); + let expected = CallActorReturn { + exit_code, + codec: send_return.clone().codec, + data_offset: 128, + data_size: send_return.clone().data.len() as u32, + data: send_return.data, + }; + + assert_eq!(result, expected); + } } fn make_raw_params(bytes: Vec) -> Option { From 7fd18071aeac376253bdc1b7beae3fe2b136e92e Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 13 Jan 2023 12:34:30 -0500 Subject: [PATCH 251/339] Restrict CALLACTOR to methodnum >= 1024 (#1037) --- actors/evm/src/interpreter/precompiles/fvm.rs | 5 +++++ actors/evm/tests/call.rs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index b816e6b3f..0965fda32 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,3 +1,4 @@ +use crate::EVM_MAX_RESERVED_METHOD; use fil_actors_runtime::runtime::{builtins::Type, Runtime}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags}; @@ -188,6 +189,10 @@ pub(super) fn call_actor( let send_data_size = input_params.read_param::()? as usize; let address_size = input_params.read_param::()? as usize; + if method <= EVM_MAX_RESERVED_METHOD { + return Err(PrecompileError::InvalidInput); + } + // ------ Begin Call ------- let result = { diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 096ce83c0..521cf017d 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -434,6 +434,12 @@ fn test_callactor_revert() { test_callactor_inner(2048, EVM_CONTRACT_REVERTED, true) } +#[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 = callactor_proxy_contract(); From cf497fa40f39f85beaeb1f976f65dd6d63fd696c Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 13 Jan 2023 20:09:23 +0200 Subject: [PATCH 252/339] refactor EAM interface (#1032) That way, the encoding is identical to the invoke params. This will make signature validation simpler. Co-authored-by: Steven Allen --- Cargo.lock | 1 + actors/eam/src/ext.rs | 4 ++ actors/eam/src/lib.rs | 122 +++++++++++++++++++++++++++-------- actors/eam/tests/create.rs | 126 ++++++++++++++++--------------------- runtime/src/test_utils.rs | 6 ++ test_vm/Cargo.toml | 1 + test_vm/src/lib.rs | 6 +- test_vm/tests/evm_test.rs | 116 ++++++++++++++++++++++++---------- 8 files changed, 248 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30a1b3160..da21badd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4517,6 +4517,7 @@ dependencies = [ "fvm_ipld_hamt", "fvm_shared", "hex", + "hex-literal", "indexmap", "integer-encoding", "lazy_static", diff --git a/actors/eam/src/ext.rs b/actors/eam/src/ext.rs index b15a32456..6c0c58b0f 100644 --- a/actors/eam/src/ext.rs +++ b/actors/eam/src/ext.rs @@ -38,3 +38,7 @@ pub mod evm { pub const RESURRECT_METHOD: u64 = 2; } + +pub mod account { + pub const PUBKEY_ADDRESS_METHOD: u64 = 2; +} diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 1d32d68d5..fe17666de 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -1,12 +1,17 @@ use std::iter; +use num_traits::Zero; + use ext::{ + account::PUBKEY_ADDRESS_METHOD, evm::RESURRECT_METHOD, init::{Exec4Params, Exec4Return}, }; use fil_actors_runtime::{actor_dispatch_unrestricted, deserialize_block, AsActorError}; + use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_shared::error::ExitCode; +use fvm_shared::{error::ExitCode, sys::SendFlags}; +use serde::{Deserialize, Serialize}; pub mod ext; @@ -37,6 +42,7 @@ pub enum Method { // TODO: Do we want to use ExportedNums for all of these, per FRC-42? Create = 2, Create2 = 3, + CreateExternal = 4, } /// Compute the a new actor address using the EVM's CREATE rules. @@ -57,6 +63,10 @@ pub fn compute_address_create2( EthAddress(hash_20(rt, &[&[0xff], &from.0[..], salt, &inithash].concat())) } +pub fn compute_address_create_external(rt: &impl Runtime, from: &EthAddress) -> EthAddress { + compute_address_create(rt, from, rt.message().nonce()) +} + #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); @@ -104,6 +114,10 @@ pub struct Create2Params { pub salt: [u8; 32], } +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +pub struct CreateExternalParams(#[serde(with = "strict_bytes")] pub Vec); + #[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq)] pub struct Return { pub actor_id: ActorID, @@ -113,6 +127,7 @@ pub struct Return { pub type CreateReturn = Return; pub type Create2Return = Return; +pub type CreateExternalReturn = Return; impl Return { fn from_exec4(exec4: Exec4Return, eth_address: EthAddress) -> Self { @@ -177,21 +192,60 @@ fn create_actor( Ok(Return::from_exec4(ret, new_addr)) } -fn resolve_caller(rt: &mut impl Runtime) -> Result { - let caller_id = rt.message().caller().id().unwrap(); - Ok(match rt.lookup_delegated_address(caller_id).map(|a| *a.payload()) { - Some(Payload::Delegated(addr)) if addr.namespace() == EAM_ACTOR_ID => EthAddress( +fn resolve_eth_address(rt: &mut impl Runtime, actor_id: ActorID) -> Result { + match rt.lookup_delegated_address(actor_id).map(|a| *a.payload()) { + Some(Payload::Delegated(addr)) if addr.namespace() == EAM_ACTOR_ID => Ok(EthAddress( addr.subaddress() .try_into() .context_code(ExitCode::USR_FORBIDDEN, "caller's eth address isn't valid")?, - ), - _ => { - let mut bytes = [0u8; 20]; - bytes[0] = 0xff; - bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); - EthAddress(bytes) + )), + _ => Err(actor_error!(forbidden; "caller doesn't have an eth address")), + } +} + +fn resolve_caller_external(rt: &mut impl Runtime) -> Result<(EthAddress, EthAddress), ActorError> { + let caller = rt.message().caller(); + let caller_id = caller.id().unwrap(); + let caller_code_cid = rt.get_actor_code_cid(&caller_id).expect("failed to lookup caller code"); + match rt.resolve_builtin_actor_type(&caller_code_cid) { + Some(Type::Account) => { + let result = rt + .send_generalized( + &caller, + PUBKEY_ADDRESS_METHOD, + None, + Zero::zero(), + None, + SendFlags::READ_ONLY, + ) + .context_code( + ExitCode::USR_ASSERTION_FAILED, + "account failed to return its key address", + )?; + + if !result.exit_code.is_success() { + // TODO: rebase on https://github.com/filecoin-project/builtin-actors/pull/1039 + return Err(ActorError::unchecked( + result.exit_code, + "failed to retrieve account robust address".to_string(), + )); + } + let robust_addr: Address = deserialize_block(result.return_data)?; + let robust_eth_bytes = hash_20(rt, &robust_addr.to_bytes()); + + let mut id_bytes = [0u8; 20]; + id_bytes[0] = 0xff; + id_bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); + + Ok((EthAddress(id_bytes), EthAddress(robust_eth_bytes))) } - }) + Some(Type::EthAccount) => { + let addr = resolve_eth_address(rt, caller_id)?; + Ok((addr, addr)) + } + Some(t) => Err(ActorError::forbidden(format!("disallowed caller type {}", t.name()))), + None => Err(ActorError::forbidden(format!("disallowed caller code {caller_code_cid}"))), + } } pub struct EamActor; @@ -209,17 +263,11 @@ impl EamActor { /// Create a new contract per the EVM's CREATE rules. /// - /// Permissions: May be called by any actor. + /// Permissions: May be called by the EVM. pub fn create(rt: &mut impl Runtime, params: CreateParams) -> Result { - // TODO: this accepts a nonce from the user, so we _may_ want to limit it to specific - // actors. However, we won't deploy over another actor anyways (those constraints are - // enforced by the init actor and the FVM itself), so it shouldn't really be an issue in - // practice. - // - // This allows _any_ actor to behave like an Ethereum account, so we'd prefer to keep it - // open. - rt.validate_immediate_caller_accept_any()?; - let caller_addr = resolve_caller(rt)?; + // We only allow EVM actors to call this. + rt.validate_immediate_caller_type(&[Type::EVM])?; + let caller_addr = resolve_eth_address(rt, rt.message().caller().id().unwrap())?; // CREATE logic let eth_addr = compute_address_create(rt, &caller_addr, params.nonce); @@ -230,15 +278,14 @@ impl EamActor { /// Create a new contract per the EVM's CREATE2 rules. /// - /// Permissions: May be called by any actor. + /// Permissions: May be called by the EVM. pub fn create2( rt: &mut impl Runtime, params: Create2Params, ) -> Result { - rt.validate_immediate_caller_accept_any()?; - - // Try to lookup the caller's EVM address, but otherwise derive one from the ID address. - let caller_addr = resolve_caller(rt)?; + // We only allow EVM actors to call this. + rt.validate_immediate_caller_type(&[Type::EVM])?; + let caller_addr = resolve_eth_address(rt, rt.message().caller().id().unwrap())?; // Compute the CREATE2 address let eth_addr = compute_address_create2(rt, &caller_addr, ¶ms.salt, ¶ms.initcode); @@ -246,6 +293,26 @@ impl EamActor { // send to init actor create_actor(rt, caller_addr, eth_addr, params.initcode) } + + /// Create a new contract from off-chain. + /// + /// When called by an EthAccount, this method will compute the new actor's address according to + /// the `CREATE` rules. When called by a "native" Account, this method will derive the address + /// from the _hash_ of the caller's key address. + /// + /// Permissions: May be called by builtin or eth accounts. + pub fn create_external( + rt: &mut impl Runtime, + params: CreateExternalParams, + ) -> Result { + // We only accept calls by top-level accounts. + // `resolve_caller_external` will check the actual types. + rt.validate_immediate_caller_is(&[rt.message().origin()])?; + + let (owner_addr, stable_addr) = resolve_caller_external(rt)?; + let eth_addr = compute_address_create_external(rt, &stable_addr); + create_actor(rt, owner_addr, eth_addr, params.0) + } } impl ActorCode for EamActor { @@ -254,6 +321,7 @@ impl ActorCode for EamActor { Constructor => constructor, Create => create, Create2 => create2, + CreateExternal => create_external, } } diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 8166e1bc8..7c1300162 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -4,9 +4,10 @@ use eam::{ compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, }; use fil_actor_eam as eam; +use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ - expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, + expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -17,82 +18,63 @@ use fvm_shared::error::ExitCode; #[test] fn call_create() { - fn test_create(rt: &mut MockRuntime, eth_addr: eam::EthAddress) { - rt.expect_validate_caller_any(); + let mut rt = construct_and_verify(); + + let id_addr = Address::new_id(110); + let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); + rt.add_delegated_address(id_addr, f4_eth_addr); - let initcode = vec![0xff]; + rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); + + rt.expect_validate_caller_type(vec![Type::EVM]); - let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; + let initcode = vec![0xff]; - let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; + let create_params = CreateParams { initcode: initcode.clone(), nonce: 0 }; - let new_eth_addr = compute_address_create(rt, ð_addr, 0); - let params = Exec4Params { - code_cid: *EVM_ACTOR_CODE_ID, - constructor_params: RawBytes::serialize(evm_params).unwrap(), - subaddress: new_eth_addr.0[..].to_owned().into(), - }; + let evm_params = EvmConstructorParams { creator: eth_addr, initcode: initcode.into() }; - let send_return = IpldBlock::serialize_cbor(&Exec4Return { - id_address: Address::new_id(111), - robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks - }) + let new_eth_addr = compute_address_create(&rt, ð_addr, 0); + let params = Exec4Params { + code_cid: *EVM_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(evm_params).unwrap(), + subaddress: new_eth_addr.0[..].to_owned().into(), + }; + + let send_return = IpldBlock::serialize_cbor(&Exec4Return { + id_address: Address::new_id(111), + robust_address: Address::new_id(0), // not a robust address but im hacking here and nobody checks + }) + .unwrap(); + + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC4_METHOD, + IpldBlock::serialize_cbor(¶ms).unwrap(), + TokenAmount::from_atto(0), + send_return, + ExitCode::OK, + ); + + let result = rt + .call::( + eam::Method::Create as u64, + IpldBlock::serialize_cbor(&create_params).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize::() .unwrap(); - rt.expect_send( - INIT_ACTOR_ADDR, - EXEC4_METHOD, - IpldBlock::serialize_cbor(¶ms).unwrap(), - TokenAmount::from_atto(0), - send_return, - ExitCode::OK, - ); - - let result = rt - .call::( - eam::Method::Create as u64, - IpldBlock::serialize_cbor(&create_params).unwrap(), - ) - .unwrap() - .unwrap() - .deserialize::() - .unwrap(); - - let expected_return = Return { - actor_id: 111, - robust_address: Some(Address::new_id(0)), - eth_address: new_eth_addr, - }; - - assert_eq!(result, expected_return); - rt.verify(); - } - - // From an EVM actor - { - let mut rt = construct_and_verify(); - - let id_addr = Address::new_id(110); - let eth_addr = - eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); - let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); - rt.add_delegated_address(id_addr, f4_eth_addr); - - rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); - test_create(&mut rt, eth_addr); - } - - // From a non-evm actor. - { - let mut rt = construct_and_verify(); - - let id_addr = Address::new_id(110); - let eth_addr = - eam::EthAddress(hex_literal::hex!("FF0000000000000000000000000000000000006E")); - - rt.set_caller(*MULTISIG_ACTOR_CODE_ID, id_addr); - test_create(&mut rt, eth_addr); - } + let expected_return = Return { + actor_id: 111, + robust_address: Some(Address::new_id(0)), + eth_address: new_eth_addr, + }; + + assert_eq!(result, expected_return); + rt.verify(); } #[test] @@ -112,7 +94,7 @@ fn call_resurrect() { let target_f4_eth_addr = Address::new_delegated(10, &target_eth_addr.0).unwrap(); rt.add_delegated_address(target_id_addr, target_f4_eth_addr); - rt.expect_validate_caller_any(); + rt.expect_validate_caller_type(vec![Type::EVM]); let initcode = vec![0xff]; @@ -156,7 +138,7 @@ fn call_create2() { rt.add_delegated_address(id_addr, f4_eth_addr); rt.set_caller(*EVM_ACTOR_CODE_ID, id_addr); - rt.expect_validate_caller_any(); + rt.expect_validate_caller_type(vec![Type::EVM]); let initcode = vec![0xff]; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 14320c1b4..cf9f94280 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -66,6 +66,7 @@ lazy_static::lazy_static! { 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); @@ -83,6 +84,7 @@ lazy_static::lazy_static! { 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 }; pub static ref ACTOR_CODES: BTreeMap = [ @@ -101,9 +103,12 @@ lazy_static::lazy_static! { (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, ()); @@ -112,6 +117,7 @@ lazy_static::lazy_static! { map.insert(*MINER_ACTOR_CODE_ID, ()); map.insert(*PLACEHOLDER_ACTOR_CODE_ID, ()); map.insert(*EVM_ACTOR_CODE_ID, ()); + map.insert(*ETHACCOUNT_ACTOR_CODE_ID, ()); map }; } diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index c14b99eff..2f3110d97 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -58,6 +58,7 @@ 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 5df749675..f06da32c4 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -628,9 +628,9 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { // Validate that there's an actor at the target ID (we don't care what is there, // just that something is there). if self.v.get_actor(Address::new_id(da.namespace())).is_some() => - { - false - } + { + false + } _ => { return Err(ActorError::unchecked( ExitCode::SYS_INVALID_RECEIVER, diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 4318285d8..bdf3c64ca 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -3,14 +3,20 @@ use std::sync::Arc; use ethers::core::types::Address as EthAddress; use ethers::prelude::abigen; use ethers::providers::Provider; -use fil_actors_runtime::{test_utils::EVM_ACTOR_CODE_ID, EAM_ACTOR_ADDR}; +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}; -use fvm_shared::ActorID; +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::create_accounts, VM}; +use test_vm::{ + util::{apply_ok, create_accounts}, + TEST_FAUCET_ADDR, VM, +}; // Generate a statically typed interface for the contract. abigen!(Recursive, "../actors/evm/tests/contracts/Recursive.abi"); @@ -45,8 +51,8 @@ fn test_evm_lifecycle() { account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, - Some(fil_actor_eam::Create2Params { initcode: bytecode, salt: [0u8; 32] }), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode)), ) .unwrap(); @@ -56,7 +62,7 @@ fn test_evm_lifecycle() { create_result.message ); - let create_return: fil_actor_eam::Create2Return = + 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"); @@ -78,6 +84,62 @@ fn test_evm_lifecycle() { assert_eq!(0, evm_ret, "expected contract to return 0 on success"); } +#[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(); @@ -89,8 +151,8 @@ fn test_evm_empty_initcode() { account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, - Some(fil_actor_eam::Create2Params { initcode: vec![], salt: [0u8; 32] }), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(vec![])), ) .unwrap(); @@ -128,11 +190,8 @@ fn test_evm_staticcall() { *account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, - Some(fil_actor_eam::Create2Params { - initcode: bytecode.clone(), - salt: [0u8; 32], - }), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), ) .unwrap(); @@ -142,7 +201,7 @@ fn test_evm_staticcall() { create_result.message ); - let create_return: fil_actor_eam::Create2Return = + let create_return: fil_actor_eam::CreateExternalReturn = create_result.ret.unwrap().deserialize().expect("failed to decode results"); // Make sure we deployed an EVM actor. @@ -285,11 +344,8 @@ fn test_evm_delegatecall() { *account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, - Some(fil_actor_eam::Create2Params { - initcode: bytecode.clone(), - salt: [0u8; 32], - }), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), ) .unwrap(); @@ -299,7 +355,7 @@ fn test_evm_delegatecall() { create_result.message ); - let create_return: fil_actor_eam::Create2Return = + let create_return: fil_actor_eam::CreateExternalReturn = create_result.ret.unwrap().deserialize().expect("failed to decode results"); // Make sure we deployed an EVM actor. @@ -396,11 +452,8 @@ fn test_evm_staticcall_delegatecall() { *account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, - Some(fil_actor_eam::Create2Params { - initcode: bytecode.clone(), - salt: [0u8; 32], - }), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), ) .unwrap(); @@ -410,7 +463,7 @@ fn test_evm_staticcall_delegatecall() { create_result.message ); - let create_return: fil_actor_eam::Create2Return = + let create_return: fil_actor_eam::CreateExternalReturn = create_result.ret.unwrap().deserialize().expect("failed to decode results"); // Make sure we deployed an EVM actor. @@ -489,14 +542,13 @@ fn test_evm_init_revert_data() { account, EAM_ACTOR_ADDR, TokenAmount::zero(), - fil_actor_eam::Method::Create2 as u64, + fil_actor_eam::Method::CreateExternal as u64, // init code: // PUSH1 0x42; PUSH1 0x0; MSTORE; // PUSH1 0x20; PUSH1 0x0; REVERT - Some(fil_actor_eam::Create2Params { - initcode: vec![0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xfd], - salt: [0u8; 32], - }), + Some(fil_actor_eam::CreateExternalParams(vec![ + 0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xfd, + ])), ) .unwrap(); From e41d332956ff12bf69d404626e40f23f5cf46597 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 13 Jan 2023 10:19:43 -0800 Subject: [PATCH 253/339] EVM: dont panic on invalid precompile address (#1042) * use full U256 for precompile check methods * return nothing on invalid precompile address * add comment about precompile addresses in get_contract_type * use Option to catch non-existing precompile, use EthAddress instead of whatever insanity i did last night --- actors/evm/src/interpreter/address.rs | 2 +- .../evm/src/interpreter/instructions/call.rs | 41 +++++++----- .../evm/src/interpreter/instructions/ext.rs | 3 +- actors/evm/src/interpreter/precompiles/mod.rs | 64 +++++++++---------- 4 files changed, 60 insertions(+), 50 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index 34886d99a..d6365bb9c 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -40,7 +40,7 @@ impl TryFrom for Address { impl TryFrom<&EthAddress> for Address { type Error = StatusCode; fn try_from(addr: &EthAddress) -> Result { - if is_reserved_precompile_address(addr.0) { + if is_reserved_precompile_address(addr) { return Err(StatusCode::BadAddress(format!( "Cannot convert a precompile address: {:?} to an f4 address", addr diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 6e77fba34..bb59af061 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -4,7 +4,7 @@ use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::BytesDe; use fvm_shared::{address::Address, sys::SendFlags, IPLD_RAW, METHOD_SEND}; -use crate::interpreter::precompiles::PrecompileContext; +use crate::interpreter::precompiles::{is_reserved_precompile_address, PrecompileContext}; use super::ext::{get_contract_type, get_evm_bytecode_cid, ContractType}; @@ -175,21 +175,29 @@ pub fn call_generic( &[] }; - if precompiles::Precompiles::::is_precompile(&dst) { + if is_reserved_precompile_address(&dst.into()) { let context = PrecompileContext { call_type: kind, gas_limit: effective_gas_limit(system, gas) }; - 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(512); - log::info!(target: "evm", "Calling Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", EthAddress::try_from(dst).unwrap_or(EthAddress([0xff; 20])), context, input_hex); - } - match precompiles::Precompiles::call_precompile(system, dst, input_data, context) { - Ok(return_data) => (1, return_data), - Err(err) => { - log::error!(target: "evm", "Precompile failed: error {:?}", err); - // precompile failed, exit with reverted and no output + Some(res) => { + 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(512); + log::info!(target: "evm", "Call Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", EthAddress::try_from(dst).unwrap_or(EthAddress([0xff; 20])), context, input_hex); + } + + match res { + Ok(return_data) => (1, return_data), + Err(err) => { + log::error!(target: "evm", "Precompile failed: error {:?}", err); + // precompile failed, exit with reverted and no output + (0, vec![]) + } + } + } + None => { + log::warn!(target: "evm", "Non-existing precompile address: {:?}", EthAddress::from(dst)); (0, vec![]) } } @@ -197,7 +205,10 @@ pub fn call_generic( let call_result = match kind { CallKind::Call | CallKind::StaticCall => { let dst_addr: EthAddress = dst.into(); - let dst_addr: Address = dst_addr.try_into().expect("address is a precompile"); + let dst_addr: Address = dst_addr.try_into().map_err(|_| ActorError::assertion_failed( + "Reached a precompile address when a precompile should've been caught earlier in the system" + .to_string(), + ))?; // Special casing for account/placeholder/non-existent actors: we just do a SEND (method 0) // which allows us to transfer funds (and create placeholders) @@ -286,7 +297,7 @@ pub fn call_generic( Err(ActorError::forbidden("cannot delegate-call to native actors".into())) } ContractType::Precompile => Err(ActorError::assertion_failed( - "Reached a precompile address when a precompile should've been caught earlier in the system" + "Reached a precompile address in DelegateCall when a precompile should've been caught earlier in the system" .to_string(), )), }, diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 5646e5fc6..106cfc2b7 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -99,7 +99,8 @@ pub enum ContractType { pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { let addr: EthAddress = addr.into(); // precompiles cant be resolved by the FVM - if Precompiles::::is_precompile(&addr.as_evm_word()) { + // 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.as_evm_word()) { return ContractType::Precompile; } diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 28fc6ffcd..62324bed1 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use fil_actors_runtime::runtime::Runtime; use substrate_bn::{CurveError, GroupError}; -use super::{instructions::call::CallKind, System, U256}; +use super::{address::EthAddress, instructions::call::CallKind, System, U256}; mod evm; mod fvm; @@ -61,8 +61,8 @@ const fn gen_native_precompiles() -> [PrecompileFn; 4] { } } -pub fn is_reserved_precompile_address(addr: [u8; 20]) -> bool { - let [prefix, middle @ .., index] = addr; +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 @@ -74,11 +74,10 @@ impl Precompiles { const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); const NATIVE_PRECOMPILES: [PrecompileFn; 4] = gen_native_precompiles(); - fn lookup_precompile(addr: &[u8; 32]) -> Option> { - // unrwap will never panic, 32 - 12 = 20 - let addr: [u8; 20] = addr[12..].try_into().unwrap(); - let [prefix, _m @ .., index] = addr; - if is_reserved_precompile_address(addr) { + fn lookup_precompile(addr: U256) -> Option> { + let addr: EthAddress = addr.into(); + 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), @@ -92,22 +91,21 @@ impl Precompiles { } /// Precompile Context will be flattened to None if not calling the call_actor precompile. - /// Panics if address is not a precompile. + /// Returns `None` if precompile does not exist at the address provided. pub fn call_precompile( system: &mut System, precompile_addr: U256, input: &[u8], context: PrecompileContext, - ) -> PrecompileResult { - unsafe { - Self::lookup_precompile(&precompile_addr.to_bytes()).unwrap()(system, input, context) - } + ) -> Option { + Self::lookup_precompile(precompile_addr) + .map(|precompile_fn| unsafe { precompile_fn(system, input, context) }) } - /// Checks if word represents + /// Checks if word is an existing precompile #[inline] - pub fn is_precompile(addr: &U256) -> bool { - !addr.is_zero() && Self::lookup_precompile(&addr.to_bytes()).is_some() + pub fn is_precompile(addr: U256) -> bool { + !addr.is_zero() && Self::lookup_precompile(addr).is_some() } } @@ -123,7 +121,7 @@ pub enum PrecompileError { CallForbidden, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct PrecompileContext { pub call_type: CallKind, pub gas_limit: u64, @@ -160,49 +158,49 @@ mod test { #[test] fn is_native_precompile() { let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); - assert!(Precompiles::::is_precompile(&addr.as_evm_word())); - assert!(is_reserved_precompile_address(addr.0)); + assert!(Precompiles::::is_precompile(addr.as_evm_word())); + assert!(is_reserved_precompile_address(&addr)); } #[test] fn is_evm_precompile() { let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); - assert!(Precompiles::::is_precompile(&addr.as_evm_word())); - assert!(is_reserved_precompile_address(addr.0)); + assert!(Precompiles::::is_precompile(addr.as_evm_word())); + assert!(is_reserved_precompile_address(&addr)); } #[test] fn is_over_precompile() { let addr = EthAddress(hex_literal::hex!("ff00000000000000000000000000000000000001")); - assert!(!Precompiles::::is_precompile(&addr.as_evm_word())); - assert!(!is_reserved_precompile_address(addr.0)); + assert!(!Precompiles::::is_precompile(addr.as_evm_word())); + 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.as_evm_word())); - assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); - assert!(!is_reserved_precompile_address(eth_addr.0)); - assert!(!is_reserved_precompile_address(native_addr.0)); + assert!(!Precompiles::::is_precompile(eth_addr.as_evm_word())); + assert!(!Precompiles::::is_precompile(native_addr.as_evm_word())); + 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.as_evm_word())); - assert!(!is_reserved_precompile_address(addr.0)); + assert!(!Precompiles::::is_precompile(addr.as_evm_word())); + 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.as_evm_word())); - assert!(!Precompiles::::is_precompile(&native_addr.as_evm_word())); + assert!(!Precompiles::::is_precompile(eth_addr.as_evm_word())); + assert!(!Precompiles::::is_precompile(native_addr.as_evm_word())); // reserved doesn't check index is within range - assert!(is_reserved_precompile_address(eth_addr.0)); - assert!(is_reserved_precompile_address(native_addr.0)); + assert!(is_reserved_precompile_address(ð_addr)); + assert!(is_reserved_precompile_address(&native_addr)); } } From f1fcba08216e530a490cb1e42ac7779e6cb6e51c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 11:39:03 -0800 Subject: [PATCH 254/339] fix: check actor error codes (#1039) This commit: 1. Extracts the actor code checking to methods on the ActorError itself. 2. Uses them in the EVM. Co-authored-by: vyzo --- actors/evm/src/interpreter/system.rs | 20 +++++++++------- runtime/src/actor_error.rs | 34 ++++++++++++++++++++++++++++ runtime/src/runtime/mod.rs | 18 ++------------- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 42e090d68..e23488e5c 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -226,17 +226,21 @@ impl<'r, RT: Runtime> System<'r, RT> { .rt .send_generalized(to, method, params, value, gas_limit, send_flags) .map_err(|err| actor_error!(unspecified; "send syscall failed: {}", err))?; + + // Don't bother reloading on abort, just return the error. + if !result.exit_code.is_success() { + return Err(ActorError::checked_with_data( + result.exit_code, + format!("failed to call {to} on method {method}"), + result.return_data, + )); + } + if !send_flags.read_only() { self.reload()?; } - match result.exit_code { - ExitCode::OK => Ok(result.return_data), - e => Err(ActorError::unchecked_with_data( - e, - "send failed".to_string(), - result.return_data, - )), - } + + Ok(result.return_data) } /// Flush the actor state (bytecode, nonce, and slots). diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index b4082d60a..f9f0145b7 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -29,6 +29,40 @@ impl ActorError { Self { exit_code: code, msg, data } } + /// Creates a new ActorError. This method checks if the exit code is within the allowed range, + /// and automatically converts it into a user code. + pub fn checked(code: ExitCode, msg: String) -> Self { + let exit_code = match code { + // This means the called actor did something wrong. We can't "make up" a + // reasonable exit code. + ExitCode::SYS_MISSING_RETURN + | ExitCode::SYS_ILLEGAL_INSTRUCTION + | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, + // We don't expect any other system errors. + code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, + // Otherwise, pass it through. + code => code, + }; + Self { exit_code, msg, data: None } + } + + /// Creates a new ActorError. This method checks if the exit code is within the allowed range, + /// and automatically converts it into a user code. + pub fn checked_with_data(code: ExitCode, msg: String, data: Option) -> Self { + let exit_code = match code { + // This means the called actor did something wrong. We can't "make up" a + // reasonable exit code. + ExitCode::SYS_MISSING_RETURN + | ExitCode::SYS_ILLEGAL_INSTRUCTION + | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, + // We don't expect any other system errors. + code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, + // Otherwise, pass it through. + code => code, + }; + Self { exit_code, msg, data } + } + pub fn illegal_argument(msg: String) -> Self { Self { exit_code: ExitCode::USR_ILLEGAL_ARGUMENT, msg, data: None } } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 75422bf33..8d6fc8d76 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -203,22 +203,8 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { if ret.exit_code.is_success() { Ok(ret.return_data) } else { - // The returned code can't be simply propagated as it may be a system exit code. - // TODO: improve propagation once we return a RuntimeError. - // Ref https://github.com/filecoin-project/builtin-actors/issues/144 - let exit_code = match ret.exit_code { - // This means the called actor did something wrong. We can't "make up" a - // reasonable exit code. - ExitCode::SYS_MISSING_RETURN - | ExitCode::SYS_ILLEGAL_INSTRUCTION - | ExitCode::SYS_ILLEGAL_EXIT_CODE => ExitCode::USR_UNSPECIFIED, - // We don't expect any other system errors. - code if code.is_system_error() => ExitCode::USR_ASSERTION_FAILED, - // Otherwise, pass it through. - code => code, - }; - Err(ActorError::unchecked_with_data( - exit_code, + Err(ActorError::checked_with_data( + ret.exit_code, format!( "send to {} method {} aborted with code {}", to, method, ret.exit_code From d7fb8424e6d0380793d51ccf687eaaff1bac4057 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 12:56:49 -0800 Subject: [PATCH 255/339] fix: use EthAddress in more places (#1044) This mostly just reduces conversions, but also makes some types a bit more obvious. --- actors/evm/src/interpreter/address.rs | 5 ++++ .../evm/src/interpreter/instructions/call.rs | 14 +++++----- .../evm/src/interpreter/instructions/ext.rs | 11 ++++---- actors/evm/src/interpreter/precompiles/mod.rs | 27 +++++++++---------- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index d6365bb9c..acd6bc290 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -85,6 +85,11 @@ impl EthAddress { pub fn as_evm_word(&self) -> U256 { U256::from_big_endian(&self.0) } + + /// Returns true if this is the null/zero EthAddress. + pub fn is_null(&self) -> bool { + self.0 == [0; 20] + } } impl AsRef<[u8]> for EthAddress { diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index bb59af061..8ebf97723 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -175,16 +175,17 @@ pub fn call_generic( &[] }; - if is_reserved_precompile_address(&dst.into()) { + let dst: EthAddress = dst.into(); + if is_reserved_precompile_address(&dst) { let context = PrecompileContext { call_type: kind, gas_limit: effective_gas_limit(system, gas) }; - match precompiles::Precompiles::call_precompile(system, dst, input_data, context) { + match precompiles::Precompiles::call_precompile(system, &dst, input_data, context) { Some(res) => { 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(512); - log::info!(target: "evm", "Call Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", EthAddress::try_from(dst).unwrap_or(EthAddress([0xff; 20])), context, input_hex); + log::info!(target: "evm", "Call Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", dst, context, input_hex); } match res { @@ -197,15 +198,14 @@ pub fn call_generic( } } None => { - log::warn!(target: "evm", "Non-existing precompile address: {:?}", EthAddress::from(dst)); + log::warn!(target: "evm", "Non-existing precompile address: {:?}", dst); (0, vec![]) } } } else { let call_result = match kind { CallKind::Call | CallKind::StaticCall => { - let dst_addr: EthAddress = dst.into(); - let dst_addr: Address = dst_addr.try_into().map_err(|_| ActorError::assertion_failed( + let dst_addr: Address = dst.try_into().map_err(|_| ActorError::assertion_failed( "Reached a precompile address when a precompile should've been caught earlier in the system" .to_string(), ))?; @@ -266,7 +266,7 @@ pub fn call_generic( system.send(&dst_addr, method, params, value, gas_limit, send_flags) } } - CallKind::DelegateCall => match get_contract_type(system.rt, dst) { + 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)? { diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 106cfc2b7..97b7b7c51 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -22,7 +22,7 @@ pub fn extcodesize( // 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) { + let len = match get_contract_type(system.rt, &addr.into()) { ContractType::EVM(addr) => { get_evm_bytecode(system, &addr).map(|bytecode| bytecode.len())? } @@ -39,7 +39,7 @@ pub fn extcodehash( system: &mut System, addr: U256, ) -> Result { - let addr = match get_contract_type(system.rt, addr) { + 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()), @@ -74,7 +74,7 @@ pub fn extcodecopy( data_offset: U256, size: U256, ) -> Result<(), StatusCode> { - let bytecode = match get_contract_type(system.rt, addr) { + 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 @@ -96,11 +96,10 @@ pub enum ContractType { } /// Resolves an address to the address type -pub fn get_contract_type(rt: &RT, addr: U256) -> ContractType { - let addr: EthAddress = addr.into(); +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.as_evm_word()) { + if Precompiles::::is_precompile(addr) { return ContractType::Precompile; } diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 62324bed1..4036a3d06 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -74,10 +74,9 @@ impl Precompiles { const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); const NATIVE_PRECOMPILES: [PrecompileFn; 4] = gen_native_precompiles(); - fn lookup_precompile(addr: U256) -> Option> { - let addr: EthAddress = addr.into(); + fn lookup_precompile(addr: &EthAddress) -> Option> { let [prefix, _m @ .., index] = addr.0; - if is_reserved_precompile_address(&addr) { + if is_reserved_precompile_address(addr) { let index = index as usize - 1; match prefix { NATIVE_PRECOMPILE_ADDRESS_PREFIX => Self::NATIVE_PRECOMPILES.get(index), @@ -94,7 +93,7 @@ impl Precompiles { /// Returns `None` if precompile does not exist at the address provided. pub fn call_precompile( system: &mut System, - precompile_addr: U256, + precompile_addr: &EthAddress, input: &[u8], context: PrecompileContext, ) -> Option { @@ -104,8 +103,8 @@ impl Precompiles { /// Checks if word is an existing precompile #[inline] - pub fn is_precompile(addr: U256) -> bool { - !addr.is_zero() && Self::lookup_precompile(addr).is_some() + pub fn is_precompile(addr: &EthAddress) -> bool { + !addr.is_null() && Self::lookup_precompile(addr).is_some() } } @@ -158,21 +157,21 @@ mod test { #[test] fn is_native_precompile() { let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); - assert!(Precompiles::::is_precompile(addr.as_evm_word())); + 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.as_evm_word())); + 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.as_evm_word())); + assert!(!Precompiles::::is_precompile(&addr)); assert!(!is_reserved_precompile_address(&addr)); } @@ -180,8 +179,8 @@ mod 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(eth_addr.as_evm_word())); - assert!(!Precompiles::::is_precompile(native_addr.as_evm_word())); + assert!(!Precompiles::::is_precompile(ð_addr)); + assert!(!Precompiles::::is_precompile(&native_addr)); assert!(!is_reserved_precompile_address(ð_addr)); assert!(!is_reserved_precompile_address(&native_addr)); } @@ -189,7 +188,7 @@ mod test { #[test] fn between_precompile() { let addr = EthAddress(hex_literal::hex!("a000000000000000000000000000000000000001")); - assert!(!Precompiles::::is_precompile(addr.as_evm_word())); + assert!(!Precompiles::::is_precompile(&addr)); assert!(!is_reserved_precompile_address(&addr)); } @@ -197,8 +196,8 @@ mod test { fn bad_index() { let eth_addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000020")); let native_addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000020")); - assert!(!Precompiles::::is_precompile(eth_addr.as_evm_word())); - assert!(!Precompiles::::is_precompile(native_addr.as_evm_word())); + 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)); From 672d771f510815fc93fc270b274e282ab74731c2 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 13 Jan 2023 14:10:41 -0800 Subject: [PATCH 256/339] EVM: Precompile test framework (#1033) * add precompile test contract, use it for actor_type * wip test update for resolve * fmt * remove test that isn't testing anything * add enum for native precompile indexes, test for market actor, test for invalid single byte input, test for edge case of bad behavior (but expected behavior) * Things: - use full word for parameters, EVM has forced my hand by making it impossible to load _just a single byte_. asm should be easier to understand now - Use full EthAddress so that we can test for invalid precompile address that are in precompile range - Fix debug fmt implementation * remove println --- actors/evm/tests/precompile.rs | 288 +++++++++++++++++++++++---------- 1 file changed, 200 insertions(+), 88 deletions(-) diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 2457603b8..5a6931447 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -1,10 +1,12 @@ mod asm; +use std::fmt::Debug; + use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, - MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, + MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, }; use fvm_shared::address::Address as FILAddress; @@ -63,66 +65,159 @@ fn test_precompile_hash() { ); } -#[test] -fn test_native_actor_type() { - let bytecode = { +fn precompile_address(prefix: u8, index: u8) -> EthAddress { + let mut buf = [0u8; 20]; + buf[0] = prefix; + buf[19] = index; + EthAddress(buf) +} + +#[repr(u8)] +#[derive(Debug, PartialEq, Eq)] +enum PrecompileExit { + Reverted = 0, + Success = 1, +} + +#[repr(u8)] +#[derive(Debug)] +pub enum NativePrecompile { + ResolveAddress = 1, + LookupDelegatedAddress = 2, + CallActor = 3, + GetActorType = 4, +} + +impl NativePrecompile { + fn as_address(&self) -> EthAddress { + precompile_address(0xfe, *self as u8) + } +} + +struct PrecompileTest { + pub expected_return: Vec, + pub expected_exit_code: PrecompileExit, + pub precompile_address: EthAddress, + pub output_size: u32, + pub input: Vec, + pub gas_avaliable: u64, +} + +impl Debug for PrecompileTest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PrecompileTest") + .field("expected_exit_code", &self.expected_exit_code) + .field("precompile_address", &self.precompile_address) + .field("input", &hex::encode(&self.input)) + .field("expected_return", &hex::encode(&self.expected_return)) + .field("output_size", &self.output_size) + .field("gas_avaliable", &self.gas_avaliable) + .finish() + } +} + +impl PrecompileTest { + 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 = util::invoke_contract( + rt, + &[ + self.precompile_address.as_evm_word().to_bytes().to_vec(), + U256::from(self.output_size).to_bytes().to_vec(), + self.input.clone(), + ] + .concat(), + ); + log::trace!("returned: {:?}", hex::encode(&result)); + 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(); + } + + fn test_runner_bytecode() -> Vec { let init = ""; let body = r#" - -# get call payload size +# store entire input to mem 0x00 calldatasize -# store payload to mem 0x00 -push1 0x00 -push1 0x00 +push1 0x00 # input offset +push1 0x00 # dst offset calldatacopy # out size +push1 0x20 # second word of input +mload + # out off -push1 0x20 -push1 0xA0 +push2 0xA000 # in size -# in off +push1 0x40 # two words calldatasize -push1 0x00 +sub +# in off +push1 0x40 # two words # value push1 0x00 -# dst (get_actor_type precompile) -push20 0xfe00000000000000000000000000000000000004 +# precompile address +push1 0x00 # first word of input is precompile +mload # gas push1 0x00 call -# copy result to mem 0x00 (overwrites input data) +# write exit code first byte of memory +push1 0x00 # offset +mstore8 + +# write precompile return to memory returndatasize -push1 0x00 -push1 0x00 +push1 0x00 # input offset +push1 0x01 # dst offset (plus 1 to accommodate exit code) returndatacopy -# return +# size returndatasize +push1 0x01 +add +# offset push1 0x00 return "#; - asm::new_contract("native_actor_type", init, body).unwrap() - }; + asm::new_contract("precompile_tester", init, body).unwrap() + } +} +#[test] +fn test_native_actor_type() { use evm::interpreter::precompiles::NativeType; - let mut rt = util::construct_and_verify(bytecode); + let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); // 0x88 is an EVM actor let evm_target = FILAddress::new_id(0x88); rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); - // f0 31 is a system actor - let system_target = FILAddress::new_id(10); - rt.set_address_actor_type(system_target, *EAM_ACTOR_CODE_ID); + // f0 10 is the EAM actor (System) + let eam_target = FILAddress::new_id(10); + rt.set_address_actor_type(eam_target, *EAM_ACTOR_CODE_ID); + + // f0 7 is the Market actor (System) + let market_target = FILAddress::new_id(7); + rt.set_address_actor_type(market_target, *MARKET_ACTOR_CODE_ID); // f0 101 is an account let account_target = FILAddress::new_id(101); @@ -141,27 +236,56 @@ return rt.set_address_actor_type(other_target, *MULTISIG_ACTOR_CODE_ID); fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(rt, &id_to_vec(&id)); - rt.verify(); - assert_eq!(&U256::from(expected as u32).to_bytes(), result.as_slice()); - rt.reset(); + let test = PrecompileTest { + precompile_address: NativePrecompile::GetActorType.as_address(), + input: id_to_vec(&id), + output_size: 32, + expected_exit_code: PrecompileExit::Success, + expected_return: U256::from(expected as u32).to_bytes().to_vec(), + gas_avaliable: 10_000_000_000, + }; + test.run_test(rt); } test_type(&mut rt, evm_target, NativeType::EVMContract); - test_type(&mut rt, system_target, NativeType::System); + test_type(&mut rt, eam_target, NativeType::System); + test_type(&mut rt, market_target, NativeType::System); test_type(&mut rt, account_target, NativeType::Account); test_type(&mut rt, placeholder_target, NativeType::Placeholder); test_type(&mut rt, miner_target, NativeType::StorageProvider); test_type(&mut rt, other_target, NativeType::OtherTypes); test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); - // invalid format address - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(&mut rt, &[0xff; 64]); - rt.verify(); - assert!(result.is_empty()); - rt.reset(); + // invalid id parameter (over) + fn test_type_invalid(rt: &mut MockRuntime, input: Vec) { + let test = PrecompileTest { + precompile_address: NativePrecompile::GetActorType.as_address(), + input, + output_size: 32, + expected_exit_code: PrecompileExit::Reverted, + expected_return: vec![], + gas_avaliable: 10_000_000_000, + }; + test.run_test(rt); + } + + // extra bytes + test_type_invalid(&mut rt, vec![0xff; 64]); + // single byte get padded and is invalid + test_type_invalid(&mut rt, vec![0xff]); + + // VERY weird and NOBODY should depend on this, but this is expected behavior soo + // ¯\_(ツ)_/¯ + { + // f0 (0xff00) + let padded_target = FILAddress::new_id(0xff00); + rt.set_address_actor_type(padded_target, *EVM_ACTOR_CODE_ID); + + // not enough bytes (but still valid id when padded) + let mut input = vec![0u8; 31]; + input[30] = 0xff; // will get padded to 0xff00 + test_type(&mut rt, evm_target, NativeType::EVMContract); + } } fn resolve_address_contract() -> Vec { @@ -216,41 +340,7 @@ return #[test] fn test_native_lookup_delegated_address() { - let bytecode = { - let init = ""; - let body = r#" - -# get call payload size -calldatasize -# store payload to mem 0x00 -push1 0x00 -push1 0x00 -calldatacopy - -push1 0x20 # out size -push1 0xA0 # out off -calldatasize # in size -push1 0x00 # in off -push1 0x00 # value -# dst (lookup_delegated_address precompile) -push20 0xfe00000000000000000000000000000000000002 -push1 0x00 # gas -call - -# copy result to mem 0x00 -returndatasize -push1 0x00 -push1 0x00 -returndatacopy -# return -returndatasize -push1 0x00 -return -"#; - - asm::new_contract("native_lookup_delegated_address", init, body).unwrap() - }; - let mut rt = util::construct_and_verify(bytecode); + let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); // f0 10101 is an EVM actor let evm_target = FILAddress::new_id(10101); @@ -262,24 +352,22 @@ return let unknown_del = FILAddress::new_delegated(1234, "foobarboxy".as_bytes()).unwrap(); rt.add_delegated_address(unknown_target, unknown_del); - fn test_reslove(rt: &mut MockRuntime, id: FILAddress, expected: Vec) { - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(rt, &id_to_vec(&id)); - rt.verify(); - assert_eq!(expected, result.as_slice()); - rt.reset(); - } + fn test_lookup_address(rt: &mut MockRuntime, id: FILAddress, expected: Vec) { + let test = PrecompileTest { + precompile_address: NativePrecompile::LookupDelegatedAddress.as_address(), + input: id_to_vec(&id), + output_size: 32, + expected_exit_code: PrecompileExit::Success, + expected_return: expected, + gas_avaliable: 10_000_000_000, + }; - test_reslove(&mut rt, evm_target, evm_del.to_bytes()); - test_reslove(&mut rt, unknown_target, unknown_del.to_bytes()); - test_reslove(&mut rt, FILAddress::new_id(11111), Vec::new()); + test.run_test(rt); + } - // invalid input - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(&mut rt, &[0xff; 42]); - rt.verify(); - assert_eq!(Vec::::new(), result); - rt.reset(); + 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] @@ -399,6 +487,30 @@ fn test_resolve_delegated() { #[test] fn test_precompile_failure() { + let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); + + // test invalid precompile address + fn invalid_address(rt: &mut MockRuntime, prefix: u8, index: u8) { + let test = PrecompileTest { + precompile_address: precompile_address(prefix, index), // precompile does not exist + input: vec![0xff; 32], // garbage input should change nothing + output_size: 32, + expected_exit_code: PrecompileExit::Reverted, + expected_return: vec![], + gas_avaliable: 10_000_000_000, + }; + test.run_test(rt); + } + + // invalid evm precompile + invalid_address(&mut rt, 0x00, 0xff); + // invalid fvm precompile + invalid_address(&mut rt, 0xfe, 0xff); + + // TODO above test can be used to test CALL normally + + // TODO: refactor these to be more clear + let bytecode = resolve_address_contract(); let mut rt = util::construct_and_verify(bytecode); From b82f4d3f5b372aa652c7a64ed94f9facbfa147a0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 15:20:42 -0800 Subject: [PATCH 257/339] fix: check for system errors in the eam (#1045) If we try to exit with a system exit code, we'll get the wrong exit code. --- actors/eam/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index fe17666de..93e50f2b2 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -224,8 +224,7 @@ fn resolve_caller_external(rt: &mut impl Runtime) -> Result<(EthAddress, EthAddr )?; if !result.exit_code.is_success() { - // TODO: rebase on https://github.com/filecoin-project/builtin-actors/pull/1039 - return Err(ActorError::unchecked( + return Err(ActorError::checked( result.exit_code, "failed to retrieve account robust address".to_string(), )); From 3b1630d1da8d411c173c39cec30429f2efccfe8f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 15:48:34 -0800 Subject: [PATCH 258/339] fix: feed delegatecall value through (#1046) We need to pass the value received in the top-level caller all the way through to delegatecall. We don't transfer it, just tell "ourselves" that we received the value. The `invoke_contract` signature was getting messy, so I factored everything out into multiple methods. fixes filecoin-project/ref-fvm#1469 --- actors/evm/src/interpreter/execution.rs | 12 +- .../evm/src/interpreter/instructions/call.rs | 1 + .../src/interpreter/instructions/context.rs | 6 +- actors/evm/src/interpreter/system.rs | 11 +- actors/evm/src/lib.rs | 131 +++++++++++------- actors/evm/tests/contracts/callvariants.hex | 2 +- .../evm/tests/contracts/callvariants_body.eas | 39 +++++- actors/evm/tests/delegate_call.rs | 2 + test_vm/tests/evm_test.rs | 31 +++++ 9 files changed, 168 insertions(+), 67 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 05dfa0a14..3bea4c126 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use fvm_shared::econ::TokenAmount; + use super::address::EthAddress; use { super::instructions, @@ -22,10 +24,17 @@ pub struct ExecutionState { 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, input_data: Bytes) -> Self { + pub fn new( + caller: EthAddress, + receiver: EthAddress, + value_received: TokenAmount, + input_data: Bytes, + ) -> Self { Self { stack: Stack::new(), memory: Memory::default(), @@ -33,6 +42,7 @@ impl ExecutionState { return_data: Default::default(), caller, receiver, + value_received, } } } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 8ebf97723..54d6574d7 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -275,6 +275,7 @@ pub fn call_generic( code, input: input_data.into(), caller: state.caller, + value: state.value_received.clone(), }; system.send( &system.rt.message().receiver(), diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 323456fec..b206f0974 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -60,10 +60,10 @@ pub fn origin( #[inline] pub fn call_value( - _state: &mut ExecutionState, - system: &System, + state: &mut ExecutionState, + _system: &System, ) -> Result { - Ok(U256::from(&system.rt.message().value_received())) + Ok(U256::from(&state.value_received)) } #[inline] diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index e23488e5c..762bba717 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -308,14 +308,9 @@ impl<'r, RT: Runtime> System<'r, RT> { Ok(()) } - /// Load the bytecode. - pub fn load_bytecode(&self) -> Result, ActorError> { - Ok(self - .bytecode - .as_ref() - .map(|EvmBytecode { cid, .. }| load_bytecode(self.rt.store(), cid)) - .transpose()? - .flatten()) + /// Get the bytecode, if any. + pub fn get_bytecode(&self) -> Option { + self.bytecode.as_ref().map(|b| b.cid) } /// Set the bytecode. diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 6763461b3..c40cf8a51 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -2,6 +2,7 @@ use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ADDR, INIT_ACTOR_A use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; use fvm_shared::address::Address; +use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use interpreter::{address::EthAddress, system::load_bytecode}; @@ -104,7 +105,9 @@ pub fn initialize_evm_contract( } // create a new execution context - let mut exec_state = ExecutionState::new(caller, receiver_eth_addr, Bytes::new()); + let value_received = system.rt.message().value_received(); + let mut exec_state = + ExecutionState::new(caller, receiver_eth_addr, value_received, Bytes::new()); // identify bytecode valid jump destinations let initcode = Bytecode::new(initcode); @@ -128,6 +131,48 @@ pub fn initialize_evm_contract( } } +fn invoke_contract_inner( + system: &mut System, + input_data: &[u8], + 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.to_vec().into()); + + let output = execute(&bytecode, &mut exec_state, system).map_err(|e| match e { + StatusCode::ActorError(e) => e, + _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), + })?; + + 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 @@ -147,65 +192,48 @@ impl EvmContractActor { initialize_evm_contract(&mut System::resurrect(rt)?, params.creator, params.initcode.into()) } - pub fn invoke_contract( + pub fn invoke_contract_delegate( rt: &mut RT, - input_data: &[u8], - with_code: Option, - with_caller: Option, + params: DelegateCallParams, ) -> Result, ActorError> where RT: Runtime, RT::Blockstore: Clone, { - if with_code.is_some() { - rt.validate_immediate_caller_is(&[rt.message().receiver()])?; - } else { - rt.validate_immediate_caller_accept_any()?; - } + 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, + ¶ms.input, + ¶ms.code, + ¶ms.caller, + params.value, + ) + } + + pub fn invoke_contract(rt: &mut RT, input_data: &[u8]) -> 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 = match match with_code { - Some(cid) => load_bytecode(system.rt.store(), &cid), - None => system.load_bytecode(), - }? { - Some(bytecode) => bytecode, + 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()), }; - // Use passed Eth address (from delegate call). - // Otherwise resolve the caller's ethereum address. If the caller doesn't have one, the caller's Eth encoded ID is used instead. - let caller_eth_addr = match with_caller { - Some(addr) => addr, - None => system.resolve_ethereum_address(&system.rt.message().caller()).unwrap(), - }; - - // 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_eth_addr, receiver_eth_addr, input_data.to_vec().into()); - - let output = execute(&bytecode, &mut exec_state, &mut system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; - - 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(), - )), - } + 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( @@ -219,7 +247,7 @@ impl EvmContractActor { { let params = args.unwrap_or(IpldBlock { codec: 0, data: vec![] }); let input = handle_filecoin_method_input(method, params.codec, params.data.as_slice()); - Self::invoke_contract(rt, &input, None, None) + Self::invoke_contract(rt, &input) } /// Returns the contract's EVM bytecode, or `None` if the contract has been deleted (has called @@ -315,7 +343,7 @@ impl ActorCode for EvmContractActor { p } }; - let value = Self::invoke_contract(rt, ¶ms, None, None)?; + let value = Self::invoke_contract(rt, ¶ms)?; Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) } Some(Method::GetBytecode) => { @@ -342,12 +370,7 @@ impl ActorCode for EvmContractActor { "method expects arguments".to_string() })? .deserialize()?; - let value = Self::invoke_contract( - rt, - ¶ms.input, - Some(params.code), - Some(params.caller), - )?; + let value = Self::invoke_contract_delegate(rt, params)?; Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) } Some(Method::Resurrect) => { @@ -396,6 +419,8 @@ pub struct DelegateCallParams { 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)] diff --git a/actors/evm/tests/contracts/callvariants.hex b/actors/evm/tests/contracts/callvariants.hex index f21d03629..9eef5886e 100644 --- a/actors/evm/tests/contracts/callvariants.hex +++ b/actors/evm/tests/contracts/callvariants.hex @@ -1 +1 @@ -306000556102058060106000396000f360003560e01c80600114607557806002146101d75780600314609557806004146101e3578060051460b5578060061460db578060071460fd578060081461012357806009146101455780600a146101655780600b1461018b5780600c146101455780600d146101b15780600e1461016557600080fd5b60206000600260e01b600052600460006004355afa156101f75760206000f35b60206000600460e01b600052600460006004355afa156101f75760206000f35b60206000600660e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600260e01b6000526004600060006004355af1156101f75760206000f35b60206000600860e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600460e01b6000526004600060006004355af1156101f75760206000f35b60206000600260e01b600052600460006004355af4156101f75760206000f35b60206000600460e01b600052600460006004355af4156101f75760005460005260206000f35b60206000600c60e01b600052602435600452602460006004355afa156101f75760206000f35b60206000600e60e01b600052602435600452602460006004355afa156101f75760206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f35b63deadbeef6000526004601cfd \ No newline at end of file +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 index a5302d4d1..2d03ed3e0 100644 --- a/actors/evm/tests/contracts/callvariants_body.eas +++ b/actors/evm/tests/contracts/callvariants_body.eas @@ -63,6 +63,10 @@ %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 @@ -203,9 +207,10 @@ push1 0x20 # ret length push1 0x00 # ret offset return -# do_delegatecall10(address) +# do_delegatecall2(address) do_delegatecall2: jumpdest + push1 0x20 # delegate ret length push1 0x00 # delegate ret offset push1 0x02 # arg: method @@ -224,6 +229,28 @@ 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 @@ -336,3 +363,13 @@ 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/delegate_call.rs b/actors/evm/tests/delegate_call.rs index 24290f8d4..a94953717 100644 --- a/actors/evm/tests/delegate_call.rs +++ b/actors/evm/tests/delegate_call.rs @@ -94,6 +94,7 @@ fn test_delegate_call_caller() { 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, @@ -105,6 +106,7 @@ fn test_delegate_call_caller() { // 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_generalized( target, diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index bdf3c64ca..be537c5b9 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use ethers::core::types::Address as EthAddress; use ethers::prelude::abigen; use ethers::providers::Provider; +use fil_actor_evm::interpreter::U256; use fil_actors_runtime::{ test_utils::{ETHACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID}, EAM_ACTOR_ADDR, EAM_ACTOR_ID, @@ -423,6 +424,36 @@ fn test_evm_delegatecall() { 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] From 077ff2ec4646aeba75d779580ba34629e6d213a8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 18:58:47 -0800 Subject: [PATCH 259/339] fix: handle value transfers when sending to a precompile (#1049) * fix: handle value transfers when sending to a precompile * fix tests * better tests * clippy --- .../evm/src/interpreter/instructions/call.rs | 38 ++++++------- actors/evm/src/interpreter/precompiles/evm.rs | 2 +- actors/evm/src/interpreter/precompiles/mod.rs | 31 +++++++++-- actors/evm/src/interpreter/system.rs | 8 ++- actors/evm/tests/precompile.rs | 55 +++++++++++-------- 5 files changed, 83 insertions(+), 51 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 54d6574d7..c2104354d 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -177,28 +177,24 @@ pub fn call_generic( let dst: EthAddress = dst.into(); if is_reserved_precompile_address(&dst) { - let context = - PrecompileContext { call_type: kind, gas_limit: effective_gas_limit(system, gas) }; - match precompiles::Precompiles::call_precompile(system, &dst, input_data, context) { - Some(res) => { - 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(512); - log::info!(target: "evm", "Call Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", dst, context, input_hex); - } + let context = PrecompileContext { + call_type: kind, + gas_limit: effective_gas_limit(system, gas), + value, + }; - match res { - Ok(return_data) => (1, return_data), - Err(err) => { - log::error!(target: "evm", "Precompile failed: error {:?}", err); - // precompile failed, exit with reverted and no output - (0, vec![]) - } - } - } - None => { - log::warn!(target: "evm", "Non-existing precompile address: {:?}", dst); + 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(512); + 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::error!(target: "evm", "Precompile failed: error {:?}", err); + // precompile failed, exit with reverted and no output (0, vec![]) } } diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 4ed061b81..664a2a7c2 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -304,7 +304,7 @@ mod tests { impl Default for PrecompileContext { fn default() -> Self { - Self { call_type: CallKind::Call, gas_limit: u64::MAX } + Self { call_type: CallKind::Call, gas_limit: u64::MAX, value: U256::ZERO } } } diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 4036a3d06..83632d5f2 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; -use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{runtime::Runtime, EAM_ACTOR_ID}; +use fvm_shared::{address::Address, econ::TokenAmount}; use substrate_bn::{CurveError, GroupError}; use super::{address::EthAddress, instructions::call::CallKind, System, U256}; @@ -89,16 +90,34 @@ impl Precompiles { } } - /// Precompile Context will be flattened to None if not calling the call_actor precompile. - /// Returns `None` if precompile does not exist at the address provided. + /// 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, - ) -> Option { - Self::lookup_precompile(precompile_addr) + ) -> PrecompileResult { + // First, try to call the precompile, if defined. + let result = Self::lookup_precompile(precompile_addr) .map(|precompile_fn| unsafe { 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() { + // Explicitly construct the precompile addr. We forbid this in the usual try_into for + // safety. + let fil_addr = Address::new_delegated(EAM_ACTOR_ID, precompile_addr.as_ref()) + .expect("incorrect address size"); + system + .transfer(&fil_addr, TokenAmount::from(&context.value)) + .map_err(|_| PrecompileError::TransferFailed)?; + } + Ok(result) } /// Checks if word is an existing precompile @@ -118,12 +137,14 @@ pub enum PrecompileError { // FVM precompile errors InvalidInput, CallForbidden, + TransferFailed, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct PrecompileContext { pub call_type: CallKind, pub gas_limit: u64, + pub value: U256, } /// Native Type of a given contract diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 762bba717..39266d39b 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -13,7 +13,7 @@ use fvm_shared::{ econ::TokenAmount, error::ExitCode, sys::SendFlags, - MethodNum, IPLD_RAW, + MethodNum, IPLD_RAW, METHOD_SEND, }; use multihash::Code; use once_cell::unsync::OnceCell; @@ -211,6 +211,12 @@ impl<'r, RT: Runtime> System<'r, RT> { nonce } + /// Transfers funds to the receiver. This doesn't bother saving/reloading state. + pub fn transfer(&mut self, to: &Address, value: TokenAmount) -> Result<(), ActorError> { + self.rt.send(to, METHOD_SEND, None, value)?; + Ok(()) + } + /// Generalized send pub fn send( &mut self, diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 5a6931447..3e572d283 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -4,11 +4,15 @@ use std::fmt::Debug; use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::{ - new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, - MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID, +use fil_actors_runtime::{ + test_utils::{ + new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, + MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + PLACEHOLDER_ACTOR_CODE_ID, + }, + EAM_ACTOR_ID, }; -use fvm_shared::address::Address as FILAddress; +use fvm_shared::{address::Address as FILAddress, econ::TokenAmount, error::ExitCode, METHOD_SEND}; mod util; use util::id_to_vec; @@ -144,8 +148,12 @@ impl PrecompileTest { } fn test_runner_bytecode() -> Vec { + Self::test_runner_bytecode_transfer_value(0) + } + fn test_runner_bytecode_transfer_value(value: u64) -> Vec { let init = ""; - let body = r#" + let body = format!( + r#" # store entire input to mem 0x00 calldatasize push1 0x00 # input offset @@ -167,7 +175,7 @@ sub push1 0x40 # two words # value -push1 0x00 +%push({value}) # precompile address push1 0x00 # first word of input is precompile @@ -195,9 +203,10 @@ add # offset push1 0x00 return -"#; +"# + ); - asm::new_contract("precompile_tester", init, body).unwrap() + asm::new_contract("precompile_tester", init, &body).unwrap() } } @@ -291,7 +300,7 @@ fn test_native_actor_type() { fn resolve_address_contract() -> Vec { let init = ""; let body = r#" - + # get call payload size calldatasize # store payload to mem 0x00 @@ -486,29 +495,29 @@ fn test_resolve_delegated() { } #[test] -fn test_precompile_failure() { - let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); - +fn test_precompile_transfer() { + let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode_transfer_value(1)); + rt.set_balance(TokenAmount::from_atto(100)); // test invalid precompile address - fn invalid_address(rt: &mut MockRuntime, prefix: u8, index: u8) { + for (prefix, index) in [(0x00, 0xff), (0xfe, 0xff)] { + let addr = precompile_address(prefix, index); let test = PrecompileTest { - precompile_address: precompile_address(prefix, index), // precompile does not exist + precompile_address: addr, input: vec![0xff; 32], // garbage input should change nothing output_size: 32, - expected_exit_code: PrecompileExit::Reverted, + expected_exit_code: PrecompileExit::Success, expected_return: vec![], gas_avaliable: 10_000_000_000, }; - test.run_test(rt); + let fil_addr = FILAddress::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap(); + rt.expect_send(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)); +} - // invalid evm precompile - invalid_address(&mut rt, 0x00, 0xff); - // invalid fvm precompile - invalid_address(&mut rt, 0xfe, 0xff); - - // TODO above test can be used to test CALL normally - +#[test] +fn test_precompile_failure() { // TODO: refactor these to be more clear let bytecode = resolve_address_contract(); From 4369a8ae317499ee2855ce8092cd65da787a5b8f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 19:24:19 -0800 Subject: [PATCH 260/339] EVM: Refactor FVM precompiles (#1047) 1. Remove the `length` from `resolve_address` (just take bytes). 2. Fix `call_actor` to follow the solidity conventions with respect to offsets and lengths. 3. Add a new `call_actor_id` that takes an ID instead of an address. --- actors/evm/src/interpreter/precompiles/fvm.rs | 117 +++++++++++++----- actors/evm/src/interpreter/precompiles/mod.rs | 15 ++- .../src/interpreter/precompiles/parameter.rs | 12 +- actors/evm/tests/call.rs | 20 +-- actors/evm/tests/precompile.rs | 54 +------- 5 files changed, 125 insertions(+), 93 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 0965fda32..d1d02034c 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -9,8 +9,8 @@ use crate::interpreter::{instructions::call::CallKind, precompiles::NativeType, use super::{parameter::ParameterReader, PrecompileContext, PrecompileError, PrecompileResult}; /// Read right padded BE encoded low u64 ID address from a u256 word. -/// Returns variant of [`BuiltinType`] encoded as a u256 word. -/// Returns nothing inputs >2^65 +/// - Reverts if the input is greater than `MAX::u64`. +/// - Returns variant of [`BuiltinType`] encoded as a u256 word. pub(super) fn get_actor_type( system: &mut System, input: &[u8], @@ -130,11 +130,7 @@ pub(super) fn resolve_address( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = ParameterReader::new(input); - - let len = input_params.read_param::()? as usize; - let addr_bytes = input_params.read_padded(len); - let addr = match Address::from_bytes(&addr_bytes) { + let addr = match Address::from_bytes(input) { Ok(o) => o, Err(e) => { log::debug!(target: "evm", "Address parsing failed: {e}"); @@ -151,14 +147,26 @@ pub(super) fn resolve_address( .unwrap_or_default()) } -/// Errors: -/// TODO should just give 0s? -/// - `IncorrectInputSize` if offset is larger than total input length -/// - `InvalidInput` if supplied address bytes isnt a filecoin address +/// 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: +/// Returns (also solidity ABI encoded): /// -/// `[int256 exit_code, uint codec, uint offset, uint size, []bytes ]` +/// ```text +/// i256 exit_code +/// u64 codec +/// bytes return_value +/// ``` /// /// for exit_code: /// - negative values are system errors @@ -168,6 +176,48 @@ 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 ------- @@ -186,8 +236,22 @@ pub(super) fn call_actor( let codec: u64 = input_params.read_param()?; - let send_data_size = input_params.read_param::()? as usize; - let address_size = input_params.read_param::()? as usize; + let params_off: u32 = input_params.read_param()?; + let id_or_addr_off: u64 = input_params.read_param()?; + + input_params.seek(params_off.try_into()?); + let params_len: u32 = input_params.read_param()?; + 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_param()?; + 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 { return Err(PrecompileError::InvalidInput); @@ -196,14 +260,10 @@ pub(super) fn call_actor( // ------ Begin Call ------- let result = { - let input_data = input_params.read_padded(send_data_size); - let address = input_params.read_padded(address_size); - let address = Address::from_bytes(&address).map_err(|_| PrecompileError::InvalidInput)?; - // TODO only CBOR or "nothing" for now let params = match codec { - fvm_ipld_encoding::DAG_CBOR => Some(IpldBlock { codec, data: input_data.into() }), - 0 if input_data.is_empty() => None, + fvm_ipld_encoding::DAG_CBOR => Some(IpldBlock { codec, data: params.into() }), + 0 if params.is_empty() => None, _ => return Err(PrecompileError::InvalidInput), }; system.send(&address, method, params, TokenAmount::from(&value), Some(ctx.gas_limit), flags) @@ -228,20 +288,19 @@ pub(super) fn call_actor( Ok(ret) => (U256::zero(), ret), }; - const NUM_OUTPUT_PARAMS: u32 = 4; - let ret_blk = data.unwrap_or(IpldBlock { codec: 0, data: vec![] }); - let offset = NUM_OUTPUT_PARAMS * 32; - let mut output = Vec::with_capacity(NUM_OUTPUT_PARAMS as usize * 32 + ret_blk.data.len()); + let mut output = Vec::with_capacity(4 * 32 + 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(offset).to_bytes()); + output.extend_from_slice(&U256::from(output.len() + 32).to_bytes()); output.extend_from_slice(&U256::from(ret_blk.data.len()).to_bytes()); - // NOTE: - // we dont pad out to 32 bytes here, the idea being that users will already be in the "everything is bytes" mode - // and will want re-pack align and whatever else by themselves output.extend_from_slice(&ret_blk.data); + // Pad out to the next increment of 32 bytes for solidity compatibility. + let offset = output.len() % 32; + if offset > 0 { + output.resize(output.len() - offset + 32, 0); + } output }; diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 83632d5f2..93801b7c2 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, num::TryFromIntError}; use fil_actors_runtime::{runtime::Runtime, EAM_ACTOR_ID}; use fvm_shared::{address::Address, econ::TokenAmount}; @@ -11,7 +11,7 @@ mod fvm; pub mod parameter; use evm::{blake2f, ec_add, ec_mul, ec_pairing, ec_recover, identity, modexp, ripemd160, sha256}; -use fvm::{call_actor, get_actor_type, lookup_delegated_address, resolve_address}; +use fvm::{call_actor, call_actor_id, get_actor_type, lookup_delegated_address, resolve_address}; // really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; @@ -53,12 +53,13 @@ const fn gen_evm_precompiles() -> [PrecompileFn; 9] { } } -const fn gen_native_precompiles() -> [PrecompileFn; 4] { +const fn gen_native_precompiles() -> [PrecompileFn; 5] { precompiles! { resolve_address, // 0xfe00..01 resolve_address lookup_delegated_address, // 0xfe00..02 lookup_delegated_address call_actor, // 0xfe00..03 call_actor get_actor_type, // 0xfe00..04 get_actor_type + call_actor_id, // 0xfe00..05 call_actor_id } } @@ -73,7 +74,7 @@ pub struct Precompiles(PhantomData); impl Precompiles { const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); - const NATIVE_PRECOMPILES: [PrecompileFn; 4] = gen_native_precompiles(); + const NATIVE_PRECOMPILES: [PrecompileFn; 5] = gen_native_precompiles(); fn lookup_precompile(addr: &EthAddress) -> Option> { let [prefix, _m @ .., index] = addr.0; @@ -140,6 +141,12 @@ pub enum PrecompileError { TransferFailed, } +impl From for PrecompileError { + fn from(_: TryFromIntError) -> Self { + Self::InvalidInput + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct PrecompileContext { pub call_type: CallKind, diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/interpreter/precompiles/parameter.rs index 1e60eb713..ae4b11e40 100644 --- a/actors/evm/src/interpreter/precompiles/parameter.rs +++ b/actors/evm/src/interpreter/precompiles/parameter.rs @@ -84,12 +84,22 @@ impl_param_int!(u16 i16 u32 i32 u64 i64); /// Provides a nice API interface for reading Parameters from input. This API treats the input as if /// it is followed by infinite zeros. pub(super) struct ParameterReader<'a> { + full: &'a [u8], slice: &'a [u8], } impl<'a> ParameterReader<'a> { pub(super) fn new(slice: &'a [u8]) -> Self { - ParameterReader { slice } + ParameterReader { 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. diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 521cf017d..18b30ef68 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -412,7 +412,7 @@ push1 0x00 # offset push1 0x01 # dest offset (we have already written the exit code of the call) returndatacopy -returndatasize +returndatasize push1 0x01 # (add 1 to the returndatasize to accommodate for the exitcode) add push1 0x00 @@ -443,7 +443,7 @@ fn test_callactor_restrict() { fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_input: bool) { let contract = callactor_proxy_contract(); - const CALLACTOR_NUM_PARAMS: usize = 6; + const CALLACTOR_NUM_PARAMS: usize = 8; // construct the proxy let mut rt = util::construct_and_verify(contract); @@ -460,20 +460,25 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i 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 proxy_call_input_data = vec![]; - let data_size = U256::from(proxy_call_input_data.len()); + let data_off = U256::from(6 * 32); + let target_off = data_off + 32 + data_size; 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); - contract_params.extend_from_slice(&proxy_call_input_data); assert_eq!( 32 * CALLACTOR_NUM_PARAMS + target_bytes.len() + proxy_call_input_data.len(), @@ -562,7 +567,8 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i assert_zero_bytes::<4>(bytes); u32::from_be_bytes(bytes[28..32].try_into().unwrap()) }; - let data = Vec::from(&src[offset as usize..(offset + size) as usize]); + + let data = Vec::from(&src[128..128 + size as usize]); Self { exit_code, codec, data_offset: offset, data_size: size, data } } @@ -572,7 +578,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i let expected = CallActorReturn { exit_code, codec: send_return.clone().codec, - data_offset: 128, + data_offset: 96, data_size: send_return.clone().data.len() as u32, data: send_return.data, }; diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 3e572d283..f1987c254 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -414,12 +414,7 @@ fn test_resolve_delegated() { fn test_resolve(rt: &mut MockRuntime, addr: FILAddress, expected: Vec) { rt.expect_gas_available(10_000_000_000u64); - let input = { - let addr = addr.to_bytes(); - let mut v = U256::from(addr.len()).to_bytes().to_vec(); - v.extend_from_slice(&addr); - v - }; + let input = addr.to_bytes(); let result = util::invoke_contract(rt, &input); rt.verify(); assert_eq!(expected, &result[1..]); @@ -434,44 +429,6 @@ fn test_resolve_delegated() { // not found test_resolve(&mut rt, unbound_del, vec![]); - // valid with extra padding - rt.expect_gas_available(10_000_000_000u64); - let input = { - let addr = evm_del.to_bytes(); - // address length to read - let mut v = U256::from(addr.len()).to_bytes().to_vec(); - // address itself - v.extend_from_slice(&addr); - // extra padding - v.extend_from_slice(&[0; 10]); - v - }; - let result = util::invoke_contract(&mut rt, &input); - rt.verify(); - assert_eq!(id_to_vec(&evm_target), &result[1..]); - assert_eq!(1, result[0]); - rt.reset(); - - // valid but needs padding - rt.expect_gas_available(10_000_000_000u64); - let input = { - // EVM f4 but subaddress len is 12 bytes - // FEEDFACECAFEBEEF00000000 - let addr = FILAddress::new_delegated(10, &util::CONTRACT_ADDRESS[..12]).unwrap(); - let addr = addr.to_bytes(); - - let read_len = addr.len() + 8; - let mut v = U256::from(read_len).to_bytes().to_vec(); - // address itself - v.extend_from_slice(&addr); - v - }; - let result = util::invoke_contract(&mut rt, &input); - rt.verify(); - assert_eq!(id_to_vec(&evm_target), &result[1..]); - assert_eq!(1, result[0]); - rt.reset(); - // invalid first param fails rt.expect_gas_available(10_000_000_000u64); let result = util::invoke_contract(&mut rt, &[0xff; 1]); @@ -532,14 +489,7 @@ fn test_precompile_failure() { // not found succeeds with empty rt.expect_gas_available(10_000_000_000u64); - let input = { - let addr = FILAddress::new_delegated(111, b"foo").unwrap().to_bytes(); - // first word is len - let mut v = U256::from(addr.len()).to_bytes().to_vec(); - // then addr - v.extend_from_slice(&addr); - v - }; + 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()); From 2cc1604a2dd38d03a4020ab219689d4a7e4b9c9e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 19:42:48 -0800 Subject: [PATCH 261/339] EVM: move parameter reader so we can use it everywhere (#1051) * EVM: move parameter reader so we can use it everywhere Basically, just make it non-precompile specific. * clippy --- actors/evm/src/interpreter/precompiles/evm.rs | 62 +++++---- actors/evm/src/interpreter/precompiles/fvm.rs | 39 +++--- actors/evm/src/interpreter/precompiles/mod.rs | 30 ++++- actors/evm/src/lib.rs | 1 + .../precompiles/parameter.rs => reader.rs} | 122 +++++++++--------- 5 files changed, 145 insertions(+), 109 deletions(-) rename actors/evm/src/{interpreter/precompiles/parameter.rs => reader.rs} (60%) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 664a2a7c2..c1eb3180a 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -11,7 +11,8 @@ use uint::byteorder::{ByteOrder, LE}; use crate::interpreter::{precompiles::PrecompileError, System, U256}; -use super::{parameter::ParameterReader, PrecompileContext, PrecompileResult}; +use super::{PrecompileContext, PrecompileResult}; +use crate::reader::ValueReader; const SECP256K1_N: U256 = U256::from_u128_words(0xfffffffffffffffffffffffffffffffe, 0xbaaedce6af48a03bbfd25e8cd0364141); @@ -26,11 +27,11 @@ fn test_secp_range() { } fn ec_recover_internal(system: &mut System, input: &[u8]) -> PrecompileResult { - let mut input_params = ParameterReader::new(input); + 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_param()?; - let r: U256 = input_params.read_param()?; - let s: U256 = input_params.read_param()?; + 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)?; @@ -104,13 +105,13 @@ pub(super) fn modexp( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut reader = ParameterReader::new(input); + 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_param::()? as usize; - let exponent_len = reader.read_param::()? as usize; - let mod_len = reader.read_param::()? as usize; + 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()); @@ -154,9 +155,9 @@ pub(super) fn ec_add( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = ParameterReader::new(input); - let point1: G1 = input_params.read_param()?; - let point2: G1 = input_params.read_param()?; + let mut input_params = ValueReader::new(input); + let point1: G1 = input_params.read_value()?; + let point2: G1 = input_params.read_value()?; curve_to_vec(point1 + point2) } @@ -167,9 +168,9 @@ pub(super) fn ec_mul( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = ParameterReader::new(input); - let point: G1 = input_params.read_param()?; - let scalar: Fr = input_params.read_param()?; + let mut input_params = ValueReader::new(input); + let point: G1 = input_params.read_value()?; + let scalar: Fr = input_params.read_value()?; curve_to_vec(point * scalar) } @@ -181,19 +182,19 @@ pub(super) fn ec_pairing( _: PrecompileContext, ) -> PrecompileResult { fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { - let mut reader = ParameterReader::new(input); + let mut reader = ValueReader::new(input); - let x: Fq = reader.read_param()?; - let y: Fq = reader.read_param()?; + let x: Fq = reader.read_value()?; + let y: Fq = reader.read_value()?; let twisted_x = { - let b: Fq = reader.read_param()?; - let a: Fq = reader.read_param()?; + let b: Fq = reader.read_value()?; + let a: Fq = reader.read_value()?; Fq2::new(a, b) }; let twisted_y = { - let b: Fq = reader.read_param()?; - let a: Fq = reader.read_param()?; + let b: Fq = reader.read_value()?; + let a: Fq = reader.read_value()?; Fq2::new(a, b) }; @@ -431,8 +432,6 @@ mod tests { // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs mod bn { - use substrate_bn::GroupError; - use super::MockRuntime; use crate::interpreter::{ @@ -494,7 +493,10 @@ mod tests { ) .unwrap(); let res = ec_add(&mut system, &input, PrecompileContext::default()); - assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); } #[test] @@ -549,7 +551,10 @@ mod tests { ) .unwrap(); let res = ec_mul(&mut system, &input, PrecompileContext::default()); - assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); } #[test] @@ -619,7 +624,10 @@ mod tests { ) .unwrap(); let res = ec_pairing(&mut system, &input, PrecompileContext::default()); - assert!(matches!(res, Err(PrecompileError::EcGroupErr(GroupError::NotOnCurve)))); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); // invalid input length let input = hex::decode( "\ diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index d1d02034c..1210102ba 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -6,7 +6,8 @@ use num_traits::FromPrimitive; use crate::interpreter::{instructions::call::CallKind, precompiles::NativeType, System, U256}; -use super::{parameter::ParameterReader, PrecompileContext, PrecompileError, PrecompileResult}; +use super::{PrecompileContext, PrecompileError, PrecompileResult}; +use crate::reader::ValueReader; /// Read right padded BE encoded low u64 ID address from a u256 word. /// - Reverts if the input is greater than `MAX::u64`. @@ -16,8 +17,8 @@ pub(super) fn get_actor_type( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut reader = ParameterReader::new(input); - let id: u64 = reader.read_param()?; + let mut reader = ValueReader::new(input); + let id: u64 = reader.read_value()?; // resolve type from code CID let builtin_type = system @@ -72,7 +73,7 @@ pub(super) fn get_randomness( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut input_params = ParameterReader::new(input); + let mut input_params = ValueReader::new(input); #[derive(num_derive::FromPrimitive)] #[repr(i32)] @@ -81,10 +82,10 @@ pub(super) fn get_randomness( Beacon = 1, } - let randomness_type = RandomnessType::from_i32(input_params.read_param::()?); - let personalization = input_params.read_param::()?; - let rand_epoch = input_params.read_param::()?; - let entropy_len = input_params.read_param::()? as usize; + let randomness_type = RandomnessType::from_i32(input_params.read_value::()?); + let personalization = input_params.read_value::()?; + let rand_epoch = input_params.read_value::()?; + let entropy_len = input_params.read_value::()? as usize; let entropy = input_params.read_padded(entropy_len); @@ -110,8 +111,8 @@ pub(super) fn lookup_delegated_address( input: &[u8], _: PrecompileContext, ) -> PrecompileResult { - let mut id_bytes = ParameterReader::new(input); - let id = id_bytes.read_param::()?; + 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 { @@ -225,29 +226,29 @@ pub(super) fn call_actor_shared( return Err(PrecompileError::CallForbidden); } - let mut input_params = ParameterReader::new(input); + let mut input_params = ValueReader::new(input); - let method: u64 = input_params.read_param()?; + let method: u64 = input_params.read_value()?; - let value: U256 = input_params.read_param()?; + let value: U256 = input_params.read_value()?; - let flags: u64 = input_params.read_param()?; + let flags: u64 = input_params.read_value()?; let flags = SendFlags::from_bits(flags).ok_or(PrecompileError::InvalidInput)?; - let codec: u64 = input_params.read_param()?; + let codec: u64 = input_params.read_value()?; - let params_off: u32 = input_params.read_param()?; - let id_or_addr_off: u64 = input_params.read_param()?; + 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_param()?; + 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_param()?; + 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)? diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 93801b7c2..cc0c78bed 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -2,13 +2,14 @@ use std::{marker::PhantomData, num::TryFromIntError}; use fil_actors_runtime::{runtime::Runtime, EAM_ACTOR_ID}; use fvm_shared::{address::Address, econ::TokenAmount}; -use substrate_bn::{CurveError, GroupError}; +use substrate_bn::{CurveError, FieldError, GroupError}; + +use crate::reader::OverflowError; use super::{address::EthAddress, instructions::call::CallKind, System, U256}; mod evm; mod fvm; -pub mod parameter; use evm::{blake2f, ec_add, ec_mul, ec_pairing, ec_recover, identity, modexp, ripemd160, sha256}; use fvm::{call_actor, call_actor_id, get_actor_type, lookup_delegated_address, resolve_address}; @@ -132,7 +133,6 @@ impl Precompiles { pub enum PrecompileError { // EVM precompile errors EcErr(CurveError), - EcGroupErr(GroupError), IncorrectInputSize, OutOfGas, // FVM precompile errors @@ -147,6 +147,30 @@ impl From for PrecompileError { } } +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, diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index c40cf8a51..77b4f0fb2 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -9,6 +9,7 @@ use interpreter::{address::EthAddress, system::load_bytecode}; use crate::interpreter::output::Outcome; pub mod interpreter; +pub(crate) mod reader; mod state; use { diff --git a/actors/evm/src/interpreter/precompiles/parameter.rs b/actors/evm/src/reader.rs similarity index 60% rename from actors/evm/src/interpreter/precompiles/parameter.rs rename to actors/evm/src/reader.rs index ae4b11e40..81b43901f 100644 --- a/actors/evm/src/interpreter/precompiles/parameter.rs +++ b/actors/evm/src/reader.rs @@ -1,73 +1,76 @@ use std::borrow::Cow; use fvm_shared::bigint::BigUint; -use substrate_bn::{AffineG1, FieldError, Fq, Fr, Group, GroupError, G1}; +use substrate_bn::{AffineG1, CurveError, FieldError, Fq, Fr, Group, G1}; use crate::interpreter::U256; -use super::PrecompileError; +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct OverflowError; -impl From for PrecompileError { - fn from(src: FieldError) -> Self { - PrecompileError::EcErr(src.into()) - } +/// 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 From for PrecompileError { - fn from(src: GroupError) -> Self { - PrecompileError::EcGroupErr(src) - } -} +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()?; -pub(super) trait Parameter: Sized { - fn read(reader: &mut ParameterReader) -> Result; -} - -impl Parameter for G1 { - fn read(reader: &mut ParameterReader) -> Result { - let x: Fq = reader.read_param()?; - let y: Fq = reader.read_param()?; - - Ok(if x.is_zero() && y.is_zero() { G1::zero() } else { AffineG1::new(x, y)?.into() }) + Ok(if x.is_zero() && y.is_zero() { + G1::zero() + } else { + AffineG1::new(x, y).map_err(|_| CurveError::NotMember)?.into() + }) } } -impl Parameter for Fq { - fn read(reader: &mut ParameterReader) -> Result { - Ok(Fq::from_slice(&reader.read_fixed::<32>())?) +impl Value for Fq { + type Error = FieldError; + fn read(reader: &mut ValueReader) -> Result { + Fq::from_slice(&reader.read_fixed::<32>()) } } -impl Parameter for Fr { - fn read(reader: &mut ParameterReader) -> Result { - Ok(Fr::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 Parameter for U256 { - fn read(reader: &mut ParameterReader) -> Result { +impl Value for U256 { + type Error = FieldError; + fn read(reader: &mut ValueReader) -> Result { Ok(U256::from(reader.read_fixed::<32>())) } } -impl Parameter for [u8; 32] { - fn read(reader: &mut ParameterReader) -> Result { +impl Value for [u8; 32] { + type Error = std::convert::Infallible; + fn read(reader: &mut ValueReader) -> Result { Ok(reader.read_fixed()) } } -impl Parameter for u8 { - fn read(reader: &mut ParameterReader) -> Result { +impl Value for u8 { + type Error = OverflowError; + fn read(reader: &mut ValueReader) -> Result { reader.drop_zeros::<31>()?; Ok(reader.read_byte()) } } -macro_rules! impl_param_int { +macro_rules! impl_value_int { ($($t:ty)*) => { $( - impl Parameter for $t { - fn read(reader: &mut ParameterReader) -> Result { + 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::()?; @@ -79,18 +82,18 @@ macro_rules! impl_param_int { }; } -impl_param_int!(u16 i16 u32 i32 u64 i64); +impl_value_int!(u16 i16 u32 i32 u64 i64); -/// Provides a nice API interface for reading Parameters from input. This API treats the input as if -/// it is followed by infinite zeros. -pub(super) struct ParameterReader<'a> { +/// 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> ParameterReader<'a> { - pub(super) fn new(slice: &'a [u8]) -> Self { - ParameterReader { full: slice, slice } +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. @@ -103,14 +106,14 @@ impl<'a> ParameterReader<'a> { } /// Drop a fixed number of bytes, and return an error if said bytes are not zeros. - pub fn drop_zeros(&mut self) -> Result<(), PrecompileError> { + 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(PrecompileError::InvalidInput) + Err(OverflowError) } } @@ -171,16 +174,15 @@ impl<'a> ParameterReader<'a> { int } - /// Read a single parameter from the input. The parameter's type decides how much input it needs + /// Read a single value from the input. The value's type decides how much input it needs /// to read. /// - /// Most parameters will read in 32 byte chunks, but that's up to the parameter's - /// implementation. - pub fn read_param(&mut self) -> Result + /// 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: Parameter, + V: Value, { - Parameter::read(self) + Value::read(self) } } @@ -190,7 +192,7 @@ mod test { #[test] fn test_read_fixed() { - let mut reader = ParameterReader::new(&[1, 2, 3]); + let mut reader = ValueReader::new(&[1, 2, 3]); assert_eq!(reader.read_fixed::<2>(), [1, 2]); assert_eq!(reader.read_fixed::<0>(), []); assert_eq!(reader.read_fixed::<5>(), [3u8, 0, 0, 0, 0]); @@ -199,7 +201,7 @@ mod test { #[test] fn test_right_pad() { - let mut reader = ParameterReader::new(&[1, 2, 3]); + 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][..]); @@ -209,19 +211,19 @@ mod test { fn test_int() { let mut data = vec![0u8; 37]; data[31] = 1; - let mut reader = ParameterReader::new(&data); - assert_eq!(reader.read_param::().unwrap(), 1); - assert_eq!(reader.read_param::().unwrap(), 0); + 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 = ParameterReader::new(&data); - assert!(matches!(reader.read_param::().unwrap_err(), PrecompileError::InvalidInput)); + let mut reader = ValueReader::new(&data); + assert_eq!(reader.read_value::().unwrap_err(), OverflowError); } #[test] fn test_big_int() { - let mut reader = ParameterReader::new(&[1, 2]); + 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()); @@ -229,7 +231,7 @@ mod test { #[test] fn test_byte() { - let mut reader = ParameterReader::new(&[1, 2]); + 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); From acf4ceefa910e01d6fd8a1f6265d9419a49ae086 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 19:59:36 -0800 Subject: [PATCH 262/339] EVM: make it easier to return values from handle_filecoin_method (#1050) Previously, we just returned the solidity return value directly. But this likely wasn't valid CBOR. Now, we decode the return value into: 1. An exit code. 2. A codec. 3. The return data. According to the Solidity ABI. --- actors/evm/src/lib.rs | 70 ++++++++++++++++--- actors/evm/src/reader.rs | 18 ++++- actors/evm/tests/call.rs | 18 ++++- .../evm/tests/contracts/FilecoinFallback.abi | 2 +- .../evm/tests/contracts/FilecoinFallback.bin | 2 +- .../evm/tests/contracts/FilecoinFallback.hex | 2 +- .../evm/tests/contracts/FilecoinFallback.sol | 13 +++- 7 files changed, 104 insertions(+), 21 deletions(-) diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 77b4f0fb2..29c9c0f9b 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -7,6 +7,7 @@ use fvm_shared::error::ExitCode; use interpreter::{address::EthAddress, system::load_bytecode}; use crate::interpreter::output::Outcome; +use crate::reader::ValueReader; pub mod interpreter; pub(crate) mod reader; @@ -241,14 +242,15 @@ impl EvmContractActor { rt: &mut RT, method: u64, args: Option, - ) -> Result, ActorError> + ) -> 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()); - Self::invoke_contract(rt, &input) + 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 @@ -313,6 +315,60 @@ fn handle_filecoin_method_input(method: u64, codec: u64, params: &[u8]) -> Vec 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. + 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 @@ -387,15 +443,7 @@ impl ActorCode for EvmContractActor { 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).map(|d| { - if d.is_empty() { - None - } else { - // TODO: Pass the codec through to here? - // https://github.com/filecoin-project/ref-fvm/issues/1422 - Some(IpldBlock { codec: DAG_CBOR, data: d }) - } - }) + Self::handle_filecoin_method(rt, method, args) } None => Err(actor_error!(unhandled_message; "Invalid method")), } diff --git a/actors/evm/src/reader.rs b/actors/evm/src/reader.rs index 81b43901f..7424915bf 100644 --- a/actors/evm/src/reader.rs +++ b/actors/evm/src/reader.rs @@ -1,6 +1,6 @@ -use std::borrow::Cow; +use std::{borrow::Cow, fmt::Display}; -use fvm_shared::bigint::BigUint; +use fvm_shared::{bigint::BigUint, error::ExitCode}; use substrate_bn::{AffineG1, CurveError, FieldError, Fq, Fr, Group, G1}; use crate::interpreter::U256; @@ -8,6 +8,12 @@ use crate::interpreter::U256; #[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 { @@ -43,6 +49,14 @@ impl Value for Fr { } } +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 { diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 18b30ef68..40727175b 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -364,11 +364,23 @@ fn test_native_call() { let contract = filecoin_fallback_contract(); let mut rt = util::construct_and_verify(contract); - // invoke the contract rt.expect_validate_caller_any(); + let result = rt.call::(1024, None).unwrap(); + assert_eq!(result, None); - let result = rt.call::(1024, None).unwrap().unwrap(); - assert_eq!(U256::from_big_endian(&result.data), U256::from(1024)); + rt.expect_validate_caller_any(); + let result = rt.call::(1025, None).unwrap(); + assert_eq!(result, Some(IpldBlock { codec: 0x71, data: "foobar".into() })); + + rt.expect_validate_caller_any(); + let err = rt.call::(1026, None).unwrap_err(); + assert_eq!(err.exit_code().value(), 42); + assert_eq!(err.data(), &[]); + + rt.expect_validate_caller_any(); + let err = rt.call::(1027, None).unwrap_err(); + assert_eq!(err.exit_code().value(), 42); + assert_eq!(err.data(), &b"foobar"[..]); } #[allow(dead_code)] diff --git a/actors/evm/tests/contracts/FilecoinFallback.abi b/actors/evm/tests/contracts/FilecoinFallback.abi index 43c775c90..bc62a9811 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.abi +++ b/actors/evm/tests/contracts/FilecoinFallback.abi @@ -1 +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":"uint64","name":"","type":"uint64"}],"stateMutability":"pure","type":"function"}] \ No newline at end of file +[{"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 index 25c8b5aaf..bade12b28 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.bin +++ b/actors/evm/tests/contracts/FilecoinFallback.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50610212806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061013e565b610060565b60405161005791906101c1565b60405180910390f35b6000808383905014151560008567ffffffffffffffff161415151461008457600080fd5b849050949350505050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6100b681610099565b81146100c157600080fd5b50565b6000813590506100d3816100ad565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126100fe576100fd6100d9565b5b8235905067ffffffffffffffff81111561011b5761011a6100de565b5b602083019150836001820283011115610137576101366100e3565b5b9250929050565b600080600080606085870312156101585761015761008f565b5b6000610166878288016100c4565b9450506020610177878288016100c4565b935050604085013567ffffffffffffffff81111561019857610197610094565b5b6101a4878288016100e8565b925092505092959194509250565b6101bb81610099565b82525050565b60006020820190506101d660008301846101b2565b9291505056fea2646970667358221220ca0553fc34ed0b668b807e982f4781af89f25219fe41d96f851f2acfb721a83764736f6c63430008110033 \ No newline at end of file +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea2646970667358221220219c8240bb34d70255de9298a7b39e5aa844709954bfc12c1c75fc2cbbcc306a64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.hex b/actors/evm/tests/contracts/FilecoinFallback.hex index 25c8b5aaf..bade12b28 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.hex +++ b/actors/evm/tests/contracts/FilecoinFallback.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b50610212806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061013e565b610060565b60405161005791906101c1565b60405180910390f35b6000808383905014151560008567ffffffffffffffff161415151461008457600080fd5b849050949350505050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6100b681610099565b81146100c157600080fd5b50565b6000813590506100d3816100ad565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126100fe576100fd6100d9565b5b8235905067ffffffffffffffff81111561011b5761011a6100de565b5b602083019150836001820283011115610137576101366100e3565b5b9250929050565b600080600080606085870312156101585761015761008f565b5b6000610166878288016100c4565b9450506020610177878288016100c4565b935050604085013567ffffffffffffffff81111561019857610197610094565b5b6101a4878288016100e8565b925092505092959194509250565b6101bb81610099565b82525050565b60006020820190506101d660008301846101b2565b9291505056fea2646970667358221220ca0553fc34ed0b668b807e982f4781af89f25219fe41d96f851f2acfb721a83764736f6c63430008110033 \ No newline at end of file +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea2646970667358221220219c8240bb34d70255de9298a7b39e5aa844709954bfc12c1c75fc2cbbcc306a64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.sol b/actors/evm/tests/contracts/FilecoinFallback.sol index 7a564ec41..d433fc28c 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.sol +++ b/actors/evm/tests/contracts/FilecoinFallback.sol @@ -2,8 +2,17 @@ pragma solidity >=0.4.25 <=0.8.17; contract FilecoinFallback { - function handle_filecoin_method(uint64 method, uint64 codec, bytes calldata params) pure public returns (uint64) { + function handle_filecoin_method(uint64 method, uint64 codec, bytes calldata params) pure public returns (uint32, uint64, bytes memory) { require((codec == 0) == (params.length == 0)); - return method; + if (method == 1024) { + return ( 0, 0, bytes("") ); + } else if (method == 1025) { + return ( 0, 0x71, bytes("foobar") ); + } else if (method == 1026) { + return ( 42, 0, bytes("") ); + } else if (method == 1027) { + return ( 42, 0x71, bytes("foobar") ); + } + revert(); } } From a9851e4964dc4a9dd514a2727fb0ee2d09aad0cd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 13 Jan 2023 21:26:06 -0800 Subject: [PATCH 263/339] chore: update amt & kamt (#1054) * chore: update amt & kamt 1. AMT: avoid flushing the root unless dirty. 2. KAMT: optimize serialization format. * chore: update storage plots --- Cargo.lock | 137 ++---- actors/evm/Cargo.toml | 2 +- .../tests/measurements/array_push_n1.jsonline | 384 ++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 17917 -> 18027 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 27808 -> 27680 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 13832 -> 13481 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 14738 -> 14532 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 14604 -> 14273 bytes .../measurements/mapping_add_n1.jsonline | 388 ++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 20173 -> 20987 bytes .../measurements/mapping_add_n100.jsonline | 398 ++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 22556 -> 22527 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22805 -> 22423 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 17361 -> 17960 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 19436 -> 20296 bytes actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/paych/Cargo.toml | 2 +- runtime/Cargo.toml | 4 +- 26 files changed, 1638 insertions(+), 1681 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da21badd6..eba0ab900 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -630,21 +630,7 @@ checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", - "multihash 0.16.3", - "serde", - "serde_bytes", - "unsigned-varint", -] - -[[package]] -name = "cid" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b68e3193982cd54187d71afdb2a271ad4cf8af157858e9cb911b91321de143" -dependencies = [ - "core2", - "multibase", - "multihash 0.17.0", + "multihash", "serde", "serde_bytes", "unsigned-varint", @@ -1796,7 +1782,7 @@ checksum = "6d0550a13a20decf920aeeb630a648b13174af5acf6b513895996ff1cd09393d" dependencies = [ "anyhow", "async-std", - "cid 0.8.6", + "cid", "clap", "futures", "fvm_ipld_blockstore", @@ -1825,7 +1811,7 @@ dependencies = [ name = "fil_actor_datacap" version = "10.0.0-alpha.1" dependencies = [ - "cid 0.8.6", + "cid", "fil_actors_runtime", "frc42_dispatch", "frc46_token", @@ -1846,7 +1832,7 @@ name = "fil_actor_eam" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actor_evm", "fil_actors_runtime", "fvm_ipld_blockstore", @@ -1854,7 +1840,7 @@ dependencies = [ "fvm_shared", "hex-literal", "log", - "multihash 0.16.3", + "multihash", "num-derive", "num-traits", "rlp", @@ -1882,7 +1868,7 @@ dependencies = [ "anyhow", "arrayvec", "bytes", - "cid 0.8.6", + "cid", "derive_more", "ethers 1.0.2", "etk-asm", @@ -1898,7 +1884,7 @@ dependencies = [ "impl-serde 0.3.2", "lazy_static", "log", - "multihash 0.16.3", + "multihash", "near-blake2", "num-derive", "num-traits", @@ -1919,7 +1905,7 @@ name = "fil_actor_init" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actors_runtime", "frc42_dispatch", "fvm_ipld_blockstore", @@ -1937,7 +1923,7 @@ name = "fil_actor_market" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actor_power", "fil_actor_reward", "fil_actor_verifreg", @@ -1954,7 +1940,7 @@ dependencies = [ "itertools", "libipld-core 0.13.1", "log", - "multihash 0.16.3", + "multihash", "num-derive", "num-traits", "regex", @@ -1967,7 +1953,7 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "byteorder", - "cid 0.8.6", + "cid", "fil_actor_account", "fil_actor_market", "fil_actor_power", @@ -1983,7 +1969,7 @@ dependencies = [ "itertools", "lazy_static", "log", - "multihash 0.16.3", + "multihash", "num-derive", "num-traits", "rand", @@ -1996,7 +1982,7 @@ name = "fil_actor_multisig" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actors_runtime", "frc42_dispatch", "fvm_actor_utils", @@ -2017,7 +2003,7 @@ name = "fil_actor_paych" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "derive_builder", "fil_actors_runtime", "frc42_dispatch", @@ -2043,7 +2029,7 @@ name = "fil_actor_power" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actor_reward", "fil_actors_runtime", "frc42_dispatch", @@ -2081,7 +2067,7 @@ name = "fil_actor_system" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2096,7 +2082,7 @@ name = "fil_actor_verifreg" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fil_actors_runtime", "frc42_dispatch", "frc46_token", @@ -2120,7 +2106,7 @@ dependencies = [ "blake2b_simd", "byteorder", "castaway", - "cid 0.8.6", + "cid", "derive_builder", "fvm_ipld_amt", "fvm_ipld_bitfield", @@ -2134,7 +2120,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "multihash 0.16.3", + "multihash", "num", "num-derive", "num-traits", @@ -2153,7 +2139,7 @@ dependencies = [ name = "fil_builtin_actors_bundle" version = "10.0.0-alpha.1" dependencies = [ - "cid 0.8.6", + "cid", "clap", "fil_actor_account", "fil_actor_bundler", @@ -2181,7 +2167,7 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "bimap", - "cid 0.8.6", + "cid", "fil_actor_account", "fil_actor_cron", "fil_actor_datacap", @@ -2294,7 +2280,7 @@ version = "3.1.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_amt", @@ -2443,7 +2429,7 @@ version = "3.0.0" source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2457,12 +2443,12 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21efabb8ae696f1cfd90b176a3600176010bd6ad3fb9919b16a24b43ba866b3d" +checksum = "e84f16d6927ce342ef86bd20fcc2d5bd498ed33ae6d7a22fea7a1b453488ec88" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fvm_ipld_blockstore", "fvm_ipld_encoding", "itertools", @@ -2490,8 +2476,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" dependencies = [ "anyhow", - "cid 0.8.6", - "multihash 0.16.3", + "cid", + "multihash", ] [[package]] @@ -2500,7 +2486,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c60423568393a284de6d7c342cd664690611f27d223eb78629fa568ddd4e7951" dependencies = [ - "cid 0.8.6", + "cid", "futures", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2516,9 +2502,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "189214585b1dc3c2a92682aa175cce1991c1a09542e5338c400d00c15b706142" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "fvm_ipld_blockstore", - "multihash 0.16.3", + "multihash", "serde", "serde_ipld_dagcbor", "serde_repr", @@ -2534,12 +2520,12 @@ checksum = "0c942494dde990aeac314311bde34c787be99cab7d0836397a75556cbaa2c3e7" dependencies = [ "anyhow", "byteorder", - "cid 0.8.6", + "cid", "forest_hash_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding", "libipld-core 0.14.0", - "multihash 0.16.3", + "multihash", "once_cell", "serde", "sha2 0.10.6", @@ -2548,18 +2534,17 @@ dependencies = [ [[package]] name = "fvm_ipld_kamt" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72011dfb8938afdd752563bbf43fb900494c9c220dda8d3029cf675f2b6cb8b8" +checksum = "5ab54acc8b19c5029ceefb3a1aa5708e1513a6ef7b17cdfeb6674c042b70d163" dependencies = [ "anyhow", "byteorder", - "cid 0.8.6", + "cid", "forest_hash_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "libipld-core 0.15.0", - "multihash 0.16.3", + "multihash", "once_cell", "serde", "sha2 0.10.6", @@ -2572,7 +2557,7 @@ version = "3.0.0-alpha.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51908aab9e7564fbcb278d78e31c12402b68c70517661780feb29adbb234aaf" dependencies = [ - "cid 0.8.6", + "cid", "fvm_ipld_encoding", "fvm_shared", "lazy_static", @@ -2591,14 +2576,14 @@ dependencies = [ "bitflags", "blake2b_simd", "byteorder", - "cid 0.8.6", + "cid", "data-encoding", "data-encoding-macro", "fvm_ipld_blockstore", "fvm_ipld_encoding", "lazy_static", "log", - "multihash 0.16.3", + "multihash", "num-bigint", "num-derive", "num-integer", @@ -3039,10 +3024,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdd758764f9680a818af33c31db733eb7c45224715d8816b9dcf0548c75f7c5" dependencies = [ "anyhow", - "cid 0.8.6", + "cid", "core2", "multibase", - "multihash 0.16.3", + "multihash", "serde", "thiserror", ] @@ -3054,25 +3039,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" dependencies = [ "anyhow", - "cid 0.8.6", - "core2", - "multibase", - "multihash 0.16.3", - "serde", - "thiserror", -] - -[[package]] -name = "libipld-core" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a704ba3b25dee9e7a2361fae2c7c19defae2a92e69ae96ffb203996705cd7c" -dependencies = [ - "anyhow", - "cid 0.9.0", + "cid", "core2", "multibase", - "multihash 0.17.0", + "multihash", "serde", "thiserror", ] @@ -3198,19 +3168,6 @@ dependencies = [ "unsigned-varint", ] -[[package]] -name = "multihash" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" -dependencies = [ - "core2", - "multihash-derive", - "serde", - "serde-big-array", - "unsigned-varint", -] - [[package]] name = "multihash-derive" version = "0.8.1" @@ -4191,7 +4148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1e23de7a4a18dff77ab9531f279a882500b8cf3549fde044d4e10481b411f1e" dependencies = [ "cbor4ii", - "cid 0.8.6", + "cid", "scopeguard", "serde", ] @@ -4490,7 +4447,7 @@ dependencies = [ "anyhow", "bimap", "blake2b_simd", - "cid 0.8.6", + "cid", "ethers 0.17.0", "fil_actor_account", "fil_actor_cron", @@ -4522,7 +4479,7 @@ dependencies = [ "integer-encoding", "lazy_static", "log", - "multihash 0.16.3", + "multihash", "num-derive", "num-traits", "rand", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index ff002f9ae..8f30a70aa 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.16", default-features = false } -fvm_ipld_kamt = { version = "0.1.0" } +fvm_ipld_kamt = { version = "0.2.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" num-traits = "0.2.14" diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index adb993afb..6e7e9c866 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -6,195 +6,195 @@ {"i":5,"series":1,"stats":{"get_bytes":2203,"get_count":3,"put_bytes":199,"put_count":2}} {"i":6,"series":1,"stats":{"get_bytes":2207,"get_count":3,"put_bytes":204,"put_count":2}} {"i":7,"series":1,"stats":{"get_bytes":2212,"get_count":3,"put_bytes":208,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":382,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":390,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2398,"get_count":4,"put_bytes":394,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2402,"get_count":4,"put_bytes":398,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2406,"get_count":4,"put_bytes":402,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2410,"get_count":4,"put_bytes":407,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2415,"get_count":4,"put_bytes":411,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2419,"get_count":4,"put_bytes":377,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2385,"get_count":4,"put_bytes":381,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2389,"get_count":4,"put_bytes":385,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2393,"get_count":4,"put_bytes":389,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2397,"get_count":4,"put_bytes":393,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2401,"get_count":4,"put_bytes":397,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2405,"get_count":4,"put_bytes":402,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2410,"get_count":4,"put_bytes":406,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2414,"get_count":4,"put_bytes":515,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2523,"get_count":5,"put_bytes":519,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2527,"get_count":5,"put_bytes":523,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2531,"get_count":5,"put_bytes":527,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2535,"get_count":5,"put_bytes":531,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2539,"get_count":5,"put_bytes":535,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2543,"get_count":5,"put_bytes":540,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2548,"get_count":5,"put_bytes":544,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2408,"get_count":4,"put_bytes":441,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2449,"get_count":4,"put_bytes":445,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2453,"get_count":4,"put_bytes":449,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2457,"get_count":4,"put_bytes":453,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2461,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":461,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2469,"get_count":4,"put_bytes":466,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2474,"get_count":4,"put_bytes":470,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":579,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2587,"get_count":5,"put_bytes":583,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2591,"get_count":5,"put_bytes":587,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2595,"get_count":5,"put_bytes":591,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2599,"get_count":5,"put_bytes":595,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":604,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2612,"get_count":5,"put_bytes":608,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":505,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2513,"get_count":4,"put_bytes":509,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2517,"get_count":4,"put_bytes":513,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2521,"get_count":4,"put_bytes":517,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2525,"get_count":4,"put_bytes":521,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2529,"get_count":4,"put_bytes":525,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2533,"get_count":4,"put_bytes":530,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2538,"get_count":4,"put_bytes":534,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2542,"get_count":4,"put_bytes":643,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2651,"get_count":5,"put_bytes":647,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2655,"get_count":5,"put_bytes":651,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2659,"get_count":5,"put_bytes":655,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2663,"get_count":5,"put_bytes":659,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2667,"get_count":5,"put_bytes":663,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2671,"get_count":5,"put_bytes":668,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2676,"get_count":5,"put_bytes":672,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2536,"get_count":4,"put_bytes":569,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2577,"get_count":4,"put_bytes":573,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2581,"get_count":4,"put_bytes":577,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2585,"get_count":4,"put_bytes":581,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2589,"get_count":4,"put_bytes":585,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2593,"get_count":4,"put_bytes":589,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2597,"get_count":4,"put_bytes":594,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2602,"get_count":4,"put_bytes":598,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2606,"get_count":4,"put_bytes":707,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":711,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2719,"get_count":5,"put_bytes":715,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2723,"get_count":5,"put_bytes":719,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2727,"get_count":5,"put_bytes":723,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2731,"get_count":5,"put_bytes":727,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2735,"get_count":5,"put_bytes":732,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2740,"get_count":5,"put_bytes":736,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2600,"get_count":4,"put_bytes":374,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":378,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2386,"get_count":4,"put_bytes":382,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":390,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2398,"get_count":4,"put_bytes":394,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2402,"get_count":4,"put_bytes":399,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2407,"get_count":4,"put_bytes":403,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2411,"get_count":4,"put_bytes":513,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2521,"get_count":5,"put_bytes":517,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2525,"get_count":5,"put_bytes":521,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2529,"get_count":5,"put_bytes":525,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2533,"get_count":5,"put_bytes":529,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2537,"get_count":5,"put_bytes":533,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2541,"get_count":5,"put_bytes":538,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2546,"get_count":5,"put_bytes":542,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2550,"get_count":5,"put_bytes":506,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2514,"get_count":5,"put_bytes":510,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2518,"get_count":5,"put_bytes":514,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":518,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2271,"get_count":3,"put_bytes":414,"put_count":3}} -{"i":1,"series":2,"stats":{"get_bytes":2422,"get_count":4,"put_bytes":418,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":2426,"get_count":4,"put_bytes":422,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":2430,"get_count":4,"put_bytes":426,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":2434,"get_count":4,"put_bytes":430,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":2438,"get_count":4,"put_bytes":434,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":2442,"get_count":4,"put_bytes":439,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":2447,"get_count":4,"put_bytes":443,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":2451,"get_count":4,"put_bytes":615,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2623,"get_count":5,"put_bytes":619,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":2627,"get_count":5,"put_bytes":623,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":2631,"get_count":5,"put_bytes":627,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":2635,"get_count":5,"put_bytes":631,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":2639,"get_count":5,"put_bytes":635,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":2643,"get_count":5,"put_bytes":640,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":2648,"get_count":5,"put_bytes":644,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":2652,"get_count":5,"put_bytes":608,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":2616,"get_count":5,"put_bytes":612,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":620,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":2628,"get_count":5,"put_bytes":624,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":2632,"get_count":5,"put_bytes":628,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":2636,"get_count":5,"put_bytes":633,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":2641,"get_count":5,"put_bytes":637,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":2645,"get_count":5,"put_bytes":746,"put_count":5}} -{"i":25,"series":2,"stats":{"get_bytes":2754,"get_count":6,"put_bytes":750,"put_count":5}} -{"i":26,"series":2,"stats":{"get_bytes":2758,"get_count":6,"put_bytes":754,"put_count":5}} -{"i":27,"series":2,"stats":{"get_bytes":2762,"get_count":6,"put_bytes":758,"put_count":5}} -{"i":28,"series":2,"stats":{"get_bytes":2766,"get_count":6,"put_bytes":762,"put_count":5}} -{"i":29,"series":2,"stats":{"get_bytes":2770,"get_count":6,"put_bytes":766,"put_count":5}} -{"i":30,"series":2,"stats":{"get_bytes":2774,"get_count":6,"put_bytes":771,"put_count":5}} -{"i":31,"series":2,"stats":{"get_bytes":2779,"get_count":6,"put_bytes":775,"put_count":5}} -{"i":32,"series":2,"stats":{"get_bytes":2639,"get_count":5,"put_bytes":672,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":2680,"get_count":5,"put_bytes":676,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":2684,"get_count":5,"put_bytes":680,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":2688,"get_count":5,"put_bytes":684,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":2692,"get_count":5,"put_bytes":688,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":2696,"get_count":5,"put_bytes":692,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":2700,"get_count":5,"put_bytes":697,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":701,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":810,"put_count":5}} -{"i":41,"series":2,"stats":{"get_bytes":2818,"get_count":6,"put_bytes":814,"put_count":5}} -{"i":42,"series":2,"stats":{"get_bytes":2822,"get_count":6,"put_bytes":818,"put_count":5}} -{"i":43,"series":2,"stats":{"get_bytes":2826,"get_count":6,"put_bytes":822,"put_count":5}} -{"i":44,"series":2,"stats":{"get_bytes":2830,"get_count":6,"put_bytes":826,"put_count":5}} -{"i":45,"series":2,"stats":{"get_bytes":2834,"get_count":6,"put_bytes":830,"put_count":5}} -{"i":46,"series":2,"stats":{"get_bytes":2838,"get_count":6,"put_bytes":835,"put_count":5}} -{"i":47,"series":2,"stats":{"get_bytes":2843,"get_count":6,"put_bytes":839,"put_count":5}} -{"i":48,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":736,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":2744,"get_count":5,"put_bytes":740,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":2748,"get_count":5,"put_bytes":744,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":2752,"get_count":5,"put_bytes":748,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":2756,"get_count":5,"put_bytes":752,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":2760,"get_count":5,"put_bytes":756,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":2764,"get_count":5,"put_bytes":761,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":2769,"get_count":5,"put_bytes":765,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":2773,"get_count":5,"put_bytes":874,"put_count":5}} -{"i":57,"series":2,"stats":{"get_bytes":2882,"get_count":6,"put_bytes":878,"put_count":5}} -{"i":58,"series":2,"stats":{"get_bytes":2886,"get_count":6,"put_bytes":882,"put_count":5}} -{"i":59,"series":2,"stats":{"get_bytes":2890,"get_count":6,"put_bytes":886,"put_count":5}} -{"i":60,"series":2,"stats":{"get_bytes":2894,"get_count":6,"put_bytes":890,"put_count":5}} -{"i":61,"series":2,"stats":{"get_bytes":2898,"get_count":6,"put_bytes":894,"put_count":5}} -{"i":62,"series":2,"stats":{"get_bytes":2902,"get_count":6,"put_bytes":899,"put_count":5}} -{"i":63,"series":2,"stats":{"get_bytes":2907,"get_count":6,"put_bytes":903,"put_count":5}} -{"i":64,"series":2,"stats":{"get_bytes":2767,"get_count":5,"put_bytes":800,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":2808,"get_count":5,"put_bytes":804,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":2812,"get_count":5,"put_bytes":808,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":2816,"get_count":5,"put_bytes":812,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":2820,"get_count":5,"put_bytes":816,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":2824,"get_count":5,"put_bytes":820,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":2828,"get_count":5,"put_bytes":825,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":2833,"get_count":5,"put_bytes":829,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":2837,"get_count":5,"put_bytes":938,"put_count":5}} -{"i":73,"series":2,"stats":{"get_bytes":2946,"get_count":6,"put_bytes":942,"put_count":5}} -{"i":74,"series":2,"stats":{"get_bytes":2950,"get_count":6,"put_bytes":946,"put_count":5}} -{"i":75,"series":2,"stats":{"get_bytes":2954,"get_count":6,"put_bytes":950,"put_count":5}} -{"i":76,"series":2,"stats":{"get_bytes":2958,"get_count":6,"put_bytes":954,"put_count":5}} -{"i":77,"series":2,"stats":{"get_bytes":2962,"get_count":6,"put_bytes":958,"put_count":5}} -{"i":78,"series":2,"stats":{"get_bytes":2966,"get_count":6,"put_bytes":963,"put_count":5}} -{"i":79,"series":2,"stats":{"get_bytes":2971,"get_count":6,"put_bytes":967,"put_count":5}} -{"i":80,"series":2,"stats":{"get_bytes":2831,"get_count":5,"put_bytes":864,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":2872,"get_count":5,"put_bytes":868,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":2876,"get_count":5,"put_bytes":872,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":2880,"get_count":5,"put_bytes":876,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":2884,"get_count":5,"put_bytes":880,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":2888,"get_count":5,"put_bytes":884,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":2892,"get_count":5,"put_bytes":889,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":2897,"get_count":5,"put_bytes":893,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":2901,"get_count":5,"put_bytes":1002,"put_count":5}} -{"i":89,"series":2,"stats":{"get_bytes":3010,"get_count":6,"put_bytes":1006,"put_count":5}} -{"i":90,"series":2,"stats":{"get_bytes":3014,"get_count":6,"put_bytes":1010,"put_count":5}} -{"i":91,"series":2,"stats":{"get_bytes":3018,"get_count":6,"put_bytes":1014,"put_count":5}} -{"i":92,"series":2,"stats":{"get_bytes":3022,"get_count":6,"put_bytes":1018,"put_count":5}} -{"i":93,"series":2,"stats":{"get_bytes":3026,"get_count":6,"put_bytes":1022,"put_count":5}} -{"i":94,"series":2,"stats":{"get_bytes":3030,"get_count":6,"put_bytes":1027,"put_count":5}} -{"i":95,"series":2,"stats":{"get_bytes":3035,"get_count":6,"put_bytes":1031,"put_count":5}} -{"i":96,"series":2,"stats":{"get_bytes":2895,"get_count":5,"put_bytes":928,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":2936,"get_count":5,"put_bytes":932,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":2940,"get_count":5,"put_bytes":936,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":2944,"get_count":5,"put_bytes":940,"put_count":4}} +{"i":8,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":336,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2344,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":344,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2352,"get_count":4,"put_bytes":348,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2356,"get_count":4,"put_bytes":352,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2360,"get_count":4,"put_bytes":356,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2364,"get_count":4,"put_bytes":361,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2369,"get_count":4,"put_bytes":365,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2373,"get_count":4,"put_bytes":317,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2325,"get_count":4,"put_bytes":321,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2329,"get_count":4,"put_bytes":325,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2333,"get_count":4,"put_bytes":329,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2337,"get_count":4,"put_bytes":333,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2341,"get_count":4,"put_bytes":337,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2345,"get_count":4,"put_bytes":342,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":346,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2354,"get_count":4,"put_bytes":440,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2448,"get_count":5,"put_bytes":444,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2452,"get_count":5,"put_bytes":448,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2456,"get_count":5,"put_bytes":452,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2460,"get_count":5,"put_bytes":456,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2464,"get_count":5,"put_bytes":460,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2468,"get_count":5,"put_bytes":465,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2473,"get_count":5,"put_bytes":469,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2333,"get_count":4,"put_bytes":366,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2374,"get_count":4,"put_bytes":370,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2378,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":378,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2386,"get_count":4,"put_bytes":382,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":391,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2399,"get_count":4,"put_bytes":395,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2403,"get_count":4,"put_bytes":489,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2497,"get_count":5,"put_bytes":493,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2501,"get_count":5,"put_bytes":497,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2505,"get_count":5,"put_bytes":501,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2509,"get_count":5,"put_bytes":505,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2513,"get_count":5,"put_bytes":509,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2517,"get_count":5,"put_bytes":514,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":518,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":415,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2423,"get_count":4,"put_bytes":419,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2427,"get_count":4,"put_bytes":423,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2431,"get_count":4,"put_bytes":427,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2435,"get_count":4,"put_bytes":431,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2439,"get_count":4,"put_bytes":435,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2443,"get_count":4,"put_bytes":440,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2448,"get_count":4,"put_bytes":444,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2452,"get_count":4,"put_bytes":538,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2546,"get_count":5,"put_bytes":542,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2550,"get_count":5,"put_bytes":546,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2554,"get_count":5,"put_bytes":550,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2558,"get_count":5,"put_bytes":554,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2562,"get_count":5,"put_bytes":558,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2566,"get_count":5,"put_bytes":563,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2571,"get_count":5,"put_bytes":567,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2431,"get_count":4,"put_bytes":464,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":484,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":489,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":493,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":587,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2595,"get_count":5,"put_bytes":591,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2599,"get_count":5,"put_bytes":595,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":603,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2611,"get_count":5,"put_bytes":607,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2615,"get_count":5,"put_bytes":612,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":319,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2327,"get_count":4,"put_bytes":323,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2331,"get_count":4,"put_bytes":327,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2335,"get_count":4,"put_bytes":331,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2339,"get_count":4,"put_bytes":335,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2343,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":344,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2352,"get_count":4,"put_bytes":439,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2447,"get_count":5,"put_bytes":443,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2451,"get_count":5,"put_bytes":447,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2455,"get_count":5,"put_bytes":451,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2459,"get_count":5,"put_bytes":455,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2463,"get_count":5,"put_bytes":459,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2467,"get_count":5,"put_bytes":464,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2472,"get_count":5,"put_bytes":468,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2476,"get_count":5,"put_bytes":417,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2425,"get_count":5,"put_bytes":421,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2429,"get_count":5,"put_bytes":425,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2433,"get_count":5,"put_bytes":429,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2227,"get_count":3,"put_bytes":355,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":363,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2371,"get_count":4,"put_bytes":367,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":371,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2379,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":380,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2388,"get_count":4,"put_bytes":384,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2392,"get_count":4,"put_bytes":512,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2520,"get_count":5,"put_bytes":516,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2524,"get_count":5,"put_bytes":520,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2528,"get_count":5,"put_bytes":524,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2532,"get_count":5,"put_bytes":528,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2536,"get_count":5,"put_bytes":532,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2540,"get_count":5,"put_bytes":537,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2545,"get_count":5,"put_bytes":541,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2549,"get_count":5,"put_bytes":491,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2499,"get_count":5,"put_bytes":495,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2503,"get_count":5,"put_bytes":499,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2507,"get_count":5,"put_bytes":503,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2511,"get_count":5,"put_bytes":507,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2515,"get_count":5,"put_bytes":511,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2519,"get_count":5,"put_bytes":516,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2524,"get_count":5,"put_bytes":520,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2528,"get_count":5,"put_bytes":614,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2622,"get_count":6,"put_bytes":618,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2626,"get_count":6,"put_bytes":622,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2630,"get_count":6,"put_bytes":626,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2634,"get_count":6,"put_bytes":630,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2638,"get_count":6,"put_bytes":634,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2642,"get_count":6,"put_bytes":639,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2647,"get_count":6,"put_bytes":643,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2507,"get_count":5,"put_bytes":540,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2548,"get_count":5,"put_bytes":544,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2552,"get_count":5,"put_bytes":548,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":552,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2560,"get_count":5,"put_bytes":556,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2564,"get_count":5,"put_bytes":560,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2568,"get_count":5,"put_bytes":565,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2573,"get_count":5,"put_bytes":569,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2577,"get_count":5,"put_bytes":663,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2671,"get_count":6,"put_bytes":667,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2675,"get_count":6,"put_bytes":671,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2679,"get_count":6,"put_bytes":675,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2683,"get_count":6,"put_bytes":679,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2687,"get_count":6,"put_bytes":683,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2691,"get_count":6,"put_bytes":688,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2696,"get_count":6,"put_bytes":692,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":589,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":597,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":605,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":614,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2622,"get_count":5,"put_bytes":618,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2626,"get_count":5,"put_bytes":712,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2720,"get_count":6,"put_bytes":716,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2724,"get_count":6,"put_bytes":720,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2728,"get_count":6,"put_bytes":724,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2732,"get_count":6,"put_bytes":728,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2736,"get_count":6,"put_bytes":732,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2740,"get_count":6,"put_bytes":737,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2745,"get_count":6,"put_bytes":741,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":638,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2646,"get_count":5,"put_bytes":642,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2650,"get_count":5,"put_bytes":646,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2654,"get_count":5,"put_bytes":650,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2658,"get_count":5,"put_bytes":654,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2662,"get_count":5,"put_bytes":658,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2666,"get_count":5,"put_bytes":663,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2671,"get_count":5,"put_bytes":667,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2675,"get_count":5,"put_bytes":761,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":2769,"get_count":6,"put_bytes":765,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":2773,"get_count":6,"put_bytes":769,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":2777,"get_count":6,"put_bytes":773,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":2781,"get_count":6,"put_bytes":777,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":2785,"get_count":6,"put_bytes":781,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":2789,"get_count":6,"put_bytes":786,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":2794,"get_count":6,"put_bytes":790,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2654,"get_count":5,"put_bytes":687,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2695,"get_count":5,"put_bytes":691,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2699,"get_count":5,"put_bytes":695,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":699,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2707,"get_count":5,"put_bytes":703,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2711,"get_count":5,"put_bytes":707,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":712,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2720,"get_count":5,"put_bytes":716,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2724,"get_count":5,"put_bytes":810,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":2818,"get_count":6,"put_bytes":814,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":2822,"get_count":6,"put_bytes":818,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":2826,"get_count":6,"put_bytes":822,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":2830,"get_count":6,"put_bytes":826,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":2834,"get_count":6,"put_bytes":830,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":2838,"get_count":6,"put_bytes":835,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":2843,"get_count":6,"put_bytes":839,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":736,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":2744,"get_count":5,"put_bytes":740,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":2748,"get_count":5,"put_bytes":744,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":2752,"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 index 58d8b6e8799cfea7dcdc15fed82e43b48f0a2ffa..0317912c6f5eb39f365af15710cc36ee0e8b493a 100644 GIT binary patch literal 18027 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 Ji!~mH{XaMvj^6+P diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline index 2248ccab3..3b9c7fa1c 100644 --- a/actors/evm/tests/measurements/array_push_n100.jsonline +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":3843,"put_count":14}} -{"i":1,"series":1,"stats":{"get_bytes":2526,"get_count":5,"put_bytes":1783,"put_count":10}} -{"i":2,"series":1,"stats":{"get_bytes":2927,"get_count":5,"put_bytes":2290,"put_count":11}} -{"i":3,"series":1,"stats":{"get_bytes":3434,"get_count":6,"put_bytes":2692,"put_count":11}} -{"i":4,"series":1,"stats":{"get_bytes":3692,"get_count":5,"put_bytes":2987,"put_count":10}} -{"i":5,"series":1,"stats":{"get_bytes":4131,"get_count":5,"put_bytes":3388,"put_count":10}} -{"i":6,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":1905,"put_count":11}} -{"i":7,"series":1,"stats":{"get_bytes":3049,"get_count":6,"put_bytes":2307,"put_count":11}} -{"i":8,"series":1,"stats":{"get_bytes":3307,"get_count":5,"put_bytes":2601,"put_count":10}} -{"i":9,"series":1,"stats":{"get_bytes":3745,"get_count":5,"put_bytes":3004,"put_count":10}} -{"i":10,"series":1,"stats":{"get_bytes":4148,"get_count":5,"put_bytes":3509,"put_count":11}} -{"i":11,"series":1,"stats":{"get_bytes":4653,"get_count":6,"put_bytes":3978,"put_count":12}} -{"i":12,"series":1,"stats":{"get_bytes":2922,"get_count":5,"put_bytes":2216,"put_count":10}} -{"i":13,"series":1,"stats":{"get_bytes":3360,"get_count":5,"put_bytes":2618,"put_count":10}} -{"i":14,"series":1,"stats":{"get_bytes":3762,"get_count":5,"put_bytes":3125,"put_count":11}} -{"i":15,"series":1,"stats":{"get_bytes":4269,"get_count":6,"put_bytes":3526,"put_count":11}} -{"i":16,"series":1,"stats":{"get_bytes":4526,"get_count":5,"put_bytes":3887,"put_count":11}} -{"i":17,"series":1,"stats":{"get_bytes":2975,"get_count":5,"put_bytes":2233,"put_count":10}} -{"i":18,"series":1,"stats":{"get_bytes":3377,"get_count":5,"put_bytes":2739,"put_count":11}} -{"i":19,"series":1,"stats":{"get_bytes":3883,"get_count":6,"put_bytes":3141,"put_count":11}} -{"i":20,"series":1,"stats":{"get_bytes":4141,"get_count":5,"put_bytes":3435,"put_count":10}} -{"i":21,"series":1,"stats":{"get_bytes":4579,"get_count":5,"put_bytes":3905,"put_count":11}} -{"i":22,"series":1,"stats":{"get_bytes":2993,"get_count":5,"put_bytes":2355,"put_count":11}} -{"i":23,"series":1,"stats":{"get_bytes":3499,"get_count":6,"put_bytes":2757,"put_count":11}} -{"i":24,"series":1,"stats":{"get_bytes":3757,"get_count":5,"put_bytes":3051,"put_count":10}} -{"i":25,"series":1,"stats":{"get_bytes":4195,"get_count":5,"put_bytes":3453,"put_count":10}} -{"i":26,"series":1,"stats":{"get_bytes":4597,"get_count":5,"put_bytes":4026,"put_count":12}} -{"i":27,"series":1,"stats":{"get_bytes":3114,"get_count":6,"put_bytes":2372,"put_count":11}} -{"i":28,"series":1,"stats":{"get_bytes":3372,"get_count":5,"put_bytes":2666,"put_count":10}} -{"i":29,"series":1,"stats":{"get_bytes":3810,"get_count":5,"put_bytes":3067,"put_count":10}} -{"i":30,"series":1,"stats":{"get_bytes":4211,"get_count":5,"put_bytes":3574,"put_count":11}} -{"i":31,"series":1,"stats":{"get_bytes":4718,"get_count":6,"put_bytes":4043,"put_count":12}} -{"i":32,"series":1,"stats":{"get_bytes":2987,"get_count":5,"put_bytes":2281,"put_count":10}} -{"i":33,"series":1,"stats":{"get_bytes":3425,"get_count":5,"put_bytes":2682,"put_count":10}} -{"i":34,"series":1,"stats":{"get_bytes":3826,"get_count":5,"put_bytes":3188,"put_count":11}} -{"i":35,"series":1,"stats":{"get_bytes":4332,"get_count":6,"put_bytes":3591,"put_count":11}} -{"i":36,"series":1,"stats":{"get_bytes":4591,"get_count":5,"put_bytes":3952,"put_count":11}} -{"i":37,"series":1,"stats":{"get_bytes":3040,"get_count":5,"put_bytes":2298,"put_count":10}} -{"i":38,"series":1,"stats":{"get_bytes":3442,"get_count":5,"put_bytes":2803,"put_count":11}} -{"i":39,"series":1,"stats":{"get_bytes":3947,"get_count":6,"put_bytes":3205,"put_count":11}} -{"i":40,"series":1,"stats":{"get_bytes":4205,"get_count":5,"put_bytes":3500,"put_count":10}} -{"i":41,"series":1,"stats":{"get_bytes":4644,"get_count":5,"put_bytes":3969,"put_count":11}} -{"i":42,"series":1,"stats":{"get_bytes":3057,"get_count":5,"put_bytes":2418,"put_count":11}} -{"i":43,"series":1,"stats":{"get_bytes":3562,"get_count":6,"put_bytes":2820,"put_count":11}} -{"i":44,"series":1,"stats":{"get_bytes":3820,"get_count":5,"put_bytes":3114,"put_count":10}} -{"i":45,"series":1,"stats":{"get_bytes":4258,"get_count":5,"put_bytes":3517,"put_count":10}} -{"i":46,"series":1,"stats":{"get_bytes":4661,"get_count":5,"put_bytes":4023,"put_count":11}} -{"i":47,"series":1,"stats":{"get_bytes":3111,"get_count":5,"put_bytes":2435,"put_count":11}} -{"i":48,"series":1,"stats":{"get_bytes":3435,"get_count":5,"put_bytes":2729,"put_count":10}} -{"i":49,"series":1,"stats":{"get_bytes":3873,"get_count":5,"put_bytes":3131,"put_count":10}} -{"i":50,"series":1,"stats":{"get_bytes":4275,"get_count":5,"put_bytes":3638,"put_count":11}} -{"i":51,"series":1,"stats":{"get_bytes":4782,"get_count":6,"put_bytes":4039,"put_count":11}} -{"i":52,"series":1,"stats":{"get_bytes":2983,"get_count":4,"put_bytes":2344,"put_count":10}} -{"i":53,"series":1,"stats":{"get_bytes":3488,"get_count":5,"put_bytes":2746,"put_count":10}} -{"i":54,"series":1,"stats":{"get_bytes":3890,"get_count":5,"put_bytes":3252,"put_count":11}} -{"i":55,"series":1,"stats":{"get_bytes":4396,"get_count":6,"put_bytes":3655,"put_count":11}} -{"i":56,"series":1,"stats":{"get_bytes":4655,"get_count":5,"put_bytes":3948,"put_count":10}} -{"i":57,"series":1,"stats":{"get_bytes":5092,"get_count":5,"put_bytes":4417,"put_count":11}} -{"i":58,"series":1,"stats":{"get_bytes":3505,"get_count":5,"put_bytes":2867,"put_count":11}} -{"i":59,"series":1,"stats":{"get_bytes":4011,"get_count":6,"put_bytes":3269,"put_count":11}} -{"i":60,"series":1,"stats":{"get_bytes":4269,"get_count":5,"put_bytes":3564,"put_count":10}} -{"i":61,"series":1,"stats":{"get_bytes":4708,"get_count":5,"put_bytes":3965,"put_count":10}} -{"i":62,"series":1,"stats":{"get_bytes":5109,"get_count":5,"put_bytes":4607,"put_count":13}} -{"i":63,"series":1,"stats":{"get_bytes":2856,"get_count":6,"put_bytes":2114,"put_count":11}} -{"i":64,"series":1,"stats":{"get_bytes":3114,"get_count":5,"put_bytes":2408,"put_count":10}} -{"i":65,"series":1,"stats":{"get_bytes":3552,"get_count":5,"put_bytes":2810,"put_count":10}} -{"i":66,"series":1,"stats":{"get_bytes":3954,"get_count":5,"put_bytes":3316,"put_count":11}} -{"i":67,"series":1,"stats":{"get_bytes":4460,"get_count":6,"put_bytes":3852,"put_count":13}} -{"i":68,"series":1,"stats":{"get_bytes":2796,"get_count":6,"put_bytes":2090,"put_count":11}} -{"i":69,"series":1,"stats":{"get_bytes":3234,"get_count":6,"put_bytes":2492,"put_count":11}} -{"i":70,"series":1,"stats":{"get_bytes":3636,"get_count":6,"put_bytes":2997,"put_count":12}} -{"i":71,"series":1,"stats":{"get_bytes":4141,"get_count":7,"put_bytes":3400,"put_count":12}} -{"i":72,"series":1,"stats":{"get_bytes":4400,"get_count":6,"put_bytes":3761,"put_count":12}} -{"i":73,"series":1,"stats":{"get_bytes":2849,"get_count":6,"put_bytes":2107,"put_count":11}} -{"i":74,"series":1,"stats":{"get_bytes":3251,"get_count":6,"put_bytes":2612,"put_count":12}} -{"i":75,"series":1,"stats":{"get_bytes":3756,"get_count":7,"put_bytes":3014,"put_count":12}} -{"i":76,"series":1,"stats":{"get_bytes":4014,"get_count":6,"put_bytes":3309,"put_count":11}} -{"i":77,"series":1,"stats":{"get_bytes":4453,"get_count":6,"put_bytes":3778,"put_count":12}} -{"i":78,"series":1,"stats":{"get_bytes":2866,"get_count":6,"put_bytes":2228,"put_count":12}} -{"i":79,"series":1,"stats":{"get_bytes":3372,"get_count":7,"put_bytes":2629,"put_count":12}} -{"i":80,"series":1,"stats":{"get_bytes":3629,"get_count":6,"put_bytes":2923,"put_count":11}} -{"i":81,"series":1,"stats":{"get_bytes":4067,"get_count":6,"put_bytes":3326,"put_count":11}} -{"i":82,"series":1,"stats":{"get_bytes":4470,"get_count":6,"put_bytes":3899,"put_count":13}} -{"i":83,"series":1,"stats":{"get_bytes":2987,"get_count":7,"put_bytes":2244,"put_count":12}} -{"i":84,"series":1,"stats":{"get_bytes":3244,"get_count":6,"put_bytes":2538,"put_count":11}} -{"i":85,"series":1,"stats":{"get_bytes":3682,"get_count":6,"put_bytes":2940,"put_count":11}} -{"i":86,"series":1,"stats":{"get_bytes":4084,"get_count":6,"put_bytes":3447,"put_count":12}} -{"i":87,"series":1,"stats":{"get_bytes":4591,"get_count":7,"put_bytes":3849,"put_count":12}} -{"i":88,"series":1,"stats":{"get_bytes":2937,"get_count":6,"put_bytes":2153,"put_count":11}} -{"i":89,"series":1,"stats":{"get_bytes":3297,"get_count":6,"put_bytes":2555,"put_count":11}} -{"i":90,"series":1,"stats":{"get_bytes":3699,"get_count":6,"put_bytes":3061,"put_count":12}} -{"i":91,"series":1,"stats":{"get_bytes":4205,"get_count":7,"put_bytes":3464,"put_count":12}} -{"i":92,"series":1,"stats":{"get_bytes":4464,"get_count":6,"put_bytes":3757,"put_count":11}} -{"i":93,"series":1,"stats":{"get_bytes":2845,"get_count":5,"put_bytes":2170,"put_count":11}} -{"i":94,"series":1,"stats":{"get_bytes":3314,"get_count":6,"put_bytes":2676,"put_count":12}} -{"i":95,"series":1,"stats":{"get_bytes":3820,"get_count":7,"put_bytes":3078,"put_count":12}} -{"i":96,"series":1,"stats":{"get_bytes":4078,"get_count":6,"put_bytes":3373,"put_count":11}} -{"i":97,"series":1,"stats":{"get_bytes":4517,"get_count":6,"put_bytes":3774,"put_count":11}} -{"i":98,"series":1,"stats":{"get_bytes":4918,"get_count":6,"put_bytes":4347,"put_count":13}} -{"i":99,"series":1,"stats":{"get_bytes":3435,"get_count":7,"put_bytes":2693,"put_count":12}} -{"i":0,"series":2,"stats":{"get_bytes":2272,"get_count":3,"put_bytes":1805,"put_count":10}} -{"i":1,"series":2,"stats":{"get_bytes":2949,"get_count":5,"put_bytes":2207,"put_count":10}} -{"i":2,"series":2,"stats":{"get_bytes":3351,"get_count":5,"put_bytes":2714,"put_count":11}} -{"i":3,"series":2,"stats":{"get_bytes":3858,"get_count":6,"put_bytes":3116,"put_count":11}} -{"i":4,"series":2,"stats":{"get_bytes":4116,"get_count":5,"put_bytes":1936,"put_count":11}} -{"i":5,"series":2,"stats":{"get_bytes":3080,"get_count":6,"put_bytes":2338,"put_count":11}} -{"i":6,"series":2,"stats":{"get_bytes":3482,"get_count":6,"put_bytes":2844,"put_count":12}} -{"i":7,"series":2,"stats":{"get_bytes":3988,"get_count":7,"put_bytes":3247,"put_count":12}} -{"i":8,"series":2,"stats":{"get_bytes":4247,"get_count":6,"put_bytes":3540,"put_count":11}} -{"i":9,"series":2,"stats":{"get_bytes":4684,"get_count":6,"put_bytes":4009,"put_count":12}} -{"i":10,"series":2,"stats":{"get_bytes":3097,"get_count":6,"put_bytes":2459,"put_count":12}} -{"i":11,"series":2,"stats":{"get_bytes":3603,"get_count":7,"put_bytes":2861,"put_count":12}} -{"i":12,"series":2,"stats":{"get_bytes":3861,"get_count":6,"put_bytes":3156,"put_count":11}} -{"i":13,"series":2,"stats":{"get_bytes":4300,"get_count":6,"put_bytes":3557,"put_count":11}} -{"i":14,"series":2,"stats":{"get_bytes":4701,"get_count":6,"put_bytes":4130,"put_count":13}} -{"i":15,"series":2,"stats":{"get_bytes":3218,"get_count":7,"put_bytes":2476,"put_count":12}} -{"i":16,"series":2,"stats":{"get_bytes":3476,"get_count":6,"put_bytes":2770,"put_count":11}} -{"i":17,"series":2,"stats":{"get_bytes":3914,"get_count":6,"put_bytes":3172,"put_count":11}} -{"i":18,"series":2,"stats":{"get_bytes":4316,"get_count":6,"put_bytes":3678,"put_count":12}} -{"i":19,"series":2,"stats":{"get_bytes":4822,"get_count":7,"put_bytes":4147,"put_count":13}} -{"i":20,"series":2,"stats":{"get_bytes":3091,"get_count":6,"put_bytes":2385,"put_count":11}} -{"i":21,"series":2,"stats":{"get_bytes":3529,"get_count":6,"put_bytes":2787,"put_count":11}} -{"i":22,"series":2,"stats":{"get_bytes":3931,"get_count":6,"put_bytes":3292,"put_count":12}} -{"i":23,"series":2,"stats":{"get_bytes":4436,"get_count":7,"put_bytes":3695,"put_count":12}} -{"i":24,"series":2,"stats":{"get_bytes":4695,"get_count":6,"put_bytes":4057,"put_count":12}} -{"i":25,"series":2,"stats":{"get_bytes":3145,"get_count":6,"put_bytes":2403,"put_count":11}} -{"i":26,"series":2,"stats":{"get_bytes":3547,"get_count":6,"put_bytes":2908,"put_count":12}} -{"i":27,"series":2,"stats":{"get_bytes":4052,"get_count":7,"put_bytes":3310,"put_count":12}} -{"i":28,"series":2,"stats":{"get_bytes":4310,"get_count":6,"put_bytes":3605,"put_count":11}} -{"i":29,"series":2,"stats":{"get_bytes":4749,"get_count":6,"put_bytes":4074,"put_count":12}} -{"i":30,"series":2,"stats":{"get_bytes":3162,"get_count":6,"put_bytes":2524,"put_count":12}} -{"i":31,"series":2,"stats":{"get_bytes":3668,"get_count":7,"put_bytes":2925,"put_count":12}} -{"i":32,"series":2,"stats":{"get_bytes":3925,"get_count":6,"put_bytes":3219,"put_count":11}} -{"i":33,"series":2,"stats":{"get_bytes":4363,"get_count":6,"put_bytes":3622,"put_count":11}} -{"i":34,"series":2,"stats":{"get_bytes":4766,"get_count":6,"put_bytes":4195,"put_count":13}} -{"i":35,"series":2,"stats":{"get_bytes":3283,"get_count":7,"put_bytes":2540,"put_count":12}} -{"i":36,"series":2,"stats":{"get_bytes":3540,"get_count":6,"put_bytes":2834,"put_count":11}} -{"i":37,"series":2,"stats":{"get_bytes":3978,"get_count":6,"put_bytes":3236,"put_count":11}} -{"i":38,"series":2,"stats":{"get_bytes":4380,"get_count":6,"put_bytes":3743,"put_count":12}} -{"i":39,"series":2,"stats":{"get_bytes":4887,"get_count":7,"put_bytes":4145,"put_count":12}} -{"i":40,"series":2,"stats":{"get_bytes":3233,"get_count":6,"put_bytes":2449,"put_count":11}} -{"i":41,"series":2,"stats":{"get_bytes":3593,"get_count":6,"put_bytes":2851,"put_count":11}} -{"i":42,"series":2,"stats":{"get_bytes":3995,"get_count":6,"put_bytes":3357,"put_count":12}} -{"i":43,"series":2,"stats":{"get_bytes":4501,"get_count":7,"put_bytes":3760,"put_count":12}} -{"i":44,"series":2,"stats":{"get_bytes":4760,"get_count":6,"put_bytes":4053,"put_count":11}} -{"i":45,"series":2,"stats":{"get_bytes":3141,"get_count":5,"put_bytes":2466,"put_count":11}} -{"i":46,"series":2,"stats":{"get_bytes":3610,"get_count":6,"put_bytes":2972,"put_count":12}} -{"i":47,"series":2,"stats":{"get_bytes":4116,"get_count":7,"put_bytes":3374,"put_count":12}} -{"i":48,"series":2,"stats":{"get_bytes":4374,"get_count":6,"put_bytes":3669,"put_count":11}} -{"i":49,"series":2,"stats":{"get_bytes":4813,"get_count":6,"put_bytes":4070,"put_count":11}} -{"i":50,"series":2,"stats":{"get_bytes":5214,"get_count":6,"put_bytes":4643,"put_count":13}} -{"i":51,"series":2,"stats":{"get_bytes":3731,"get_count":7,"put_bytes":2989,"put_count":12}} -{"i":52,"series":2,"stats":{"get_bytes":3989,"get_count":6,"put_bytes":3283,"put_count":11}} -{"i":53,"series":2,"stats":{"get_bytes":4427,"get_count":6,"put_bytes":3686,"put_count":11}} -{"i":54,"series":2,"stats":{"get_bytes":4830,"get_count":6,"put_bytes":4191,"put_count":12}} -{"i":55,"series":2,"stats":{"get_bytes":5335,"get_count":7,"put_bytes":4660,"put_count":13}} -{"i":56,"series":2,"stats":{"get_bytes":3604,"get_count":6,"put_bytes":2898,"put_count":11}} -{"i":57,"series":2,"stats":{"get_bytes":4042,"get_count":6,"put_bytes":3300,"put_count":11}} -{"i":58,"series":2,"stats":{"get_bytes":4444,"get_count":6,"put_bytes":3806,"put_count":12}} -{"i":59,"series":2,"stats":{"get_bytes":4950,"get_count":7,"put_bytes":4208,"put_count":12}} -{"i":60,"series":2,"stats":{"get_bytes":5208,"get_count":6,"put_bytes":4569,"put_count":12}} -{"i":61,"series":2,"stats":{"get_bytes":3657,"get_count":6,"put_bytes":2915,"put_count":11}} -{"i":62,"series":2,"stats":{"get_bytes":4059,"get_count":6,"put_bytes":3421,"put_count":12}} -{"i":63,"series":2,"stats":{"get_bytes":4565,"get_count":7,"put_bytes":3822,"put_count":12}} -{"i":64,"series":2,"stats":{"get_bytes":4822,"get_count":6,"put_bytes":4117,"put_count":11}} -{"i":65,"series":2,"stats":{"get_bytes":5261,"get_count":6,"put_bytes":4587,"put_count":12}} -{"i":66,"series":2,"stats":{"get_bytes":3675,"get_count":6,"put_bytes":3037,"put_count":12}} -{"i":67,"series":2,"stats":{"get_bytes":4181,"get_count":7,"put_bytes":3438,"put_count":12}} -{"i":68,"series":2,"stats":{"get_bytes":4438,"get_count":6,"put_bytes":3732,"put_count":11}} -{"i":69,"series":2,"stats":{"get_bytes":4876,"get_count":6,"put_bytes":4135,"put_count":11}} -{"i":70,"series":2,"stats":{"get_bytes":5279,"get_count":6,"put_bytes":4708,"put_count":13}} -{"i":71,"series":2,"stats":{"get_bytes":3796,"get_count":7,"put_bytes":3054,"put_count":12}} -{"i":72,"series":2,"stats":{"get_bytes":4054,"get_count":6,"put_bytes":3347,"put_count":11}} -{"i":73,"series":2,"stats":{"get_bytes":4491,"get_count":6,"put_bytes":3749,"put_count":11}} -{"i":74,"series":2,"stats":{"get_bytes":4893,"get_count":6,"put_bytes":4256,"put_count":12}} -{"i":75,"series":2,"stats":{"get_bytes":5400,"get_count":7,"put_bytes":4725,"put_count":13}} -{"i":76,"series":2,"stats":{"get_bytes":3669,"get_count":6,"put_bytes":2963,"put_count":11}} -{"i":77,"series":2,"stats":{"get_bytes":4107,"get_count":6,"put_bytes":3364,"put_count":11}} -{"i":78,"series":2,"stats":{"get_bytes":4508,"get_count":6,"put_bytes":3870,"put_count":12}} -{"i":79,"series":2,"stats":{"get_bytes":5014,"get_count":7,"put_bytes":4273,"put_count":12}} -{"i":80,"series":2,"stats":{"get_bytes":5273,"get_count":6,"put_bytes":4634,"put_count":12}} -{"i":81,"series":2,"stats":{"get_bytes":3722,"get_count":6,"put_bytes":2979,"put_count":11}} -{"i":82,"series":2,"stats":{"get_bytes":4123,"get_count":6,"put_bytes":3485,"put_count":12}} -{"i":83,"series":2,"stats":{"get_bytes":4629,"get_count":7,"put_bytes":3887,"put_count":12}} -{"i":84,"series":2,"stats":{"get_bytes":4887,"get_count":6,"put_bytes":4182,"put_count":11}} -{"i":85,"series":2,"stats":{"get_bytes":5326,"get_count":6,"put_bytes":4583,"put_count":11}} -{"i":86,"series":2,"stats":{"get_bytes":3671,"get_count":5,"put_bytes":3100,"put_count":12}} -{"i":87,"series":2,"stats":{"get_bytes":4244,"get_count":7,"put_bytes":3502,"put_count":12}} -{"i":88,"series":2,"stats":{"get_bytes":4502,"get_count":6,"put_bytes":3796,"put_count":11}} -{"i":89,"series":2,"stats":{"get_bytes":4940,"get_count":6,"put_bytes":4199,"put_count":11}} -{"i":90,"series":2,"stats":{"get_bytes":5343,"get_count":6,"put_bytes":4704,"put_count":12}} -{"i":91,"series":2,"stats":{"get_bytes":5848,"get_count":7,"put_bytes":5173,"put_count":13}} -{"i":92,"series":2,"stats":{"get_bytes":4117,"get_count":6,"put_bytes":3411,"put_count":11}} -{"i":93,"series":2,"stats":{"get_bytes":4555,"get_count":6,"put_bytes":3813,"put_count":11}} -{"i":94,"series":2,"stats":{"get_bytes":4957,"get_count":6,"put_bytes":4320,"put_count":12}} -{"i":95,"series":2,"stats":{"get_bytes":5464,"get_count":7,"put_bytes":4721,"put_count":12}} -{"i":96,"series":2,"stats":{"get_bytes":5721,"get_count":6,"put_bytes":5082,"put_count":12}} -{"i":97,"series":2,"stats":{"get_bytes":4170,"get_count":6,"put_bytes":3428,"put_count":11}} -{"i":98,"series":2,"stats":{"get_bytes":4572,"get_count":6,"put_bytes":3934,"put_count":12}} -{"i":99,"series":2,"stats":{"get_bytes":5078,"get_count":7,"put_bytes":4336,"put_count":12}} +{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":3679,"put_count":14}} +{"i":1,"series":1,"stats":{"get_bytes":2437,"get_count":5,"put_bytes":1604,"put_count":10}} +{"i":2,"series":1,"stats":{"get_bytes":2748,"get_count":5,"put_bytes":2006,"put_count":11}} +{"i":3,"series":1,"stats":{"get_bytes":3150,"get_count":6,"put_bytes":2318,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":3318,"get_count":5,"put_bytes":2523,"put_count":10}} +{"i":5,"series":1,"stats":{"get_bytes":3667,"get_count":5,"put_bytes":2834,"put_count":10}} +{"i":6,"series":1,"stats":{"get_bytes":2402,"get_count":4,"put_bytes":1711,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":2855,"get_count":6,"put_bytes":2023,"put_count":11}} +{"i":8,"series":1,"stats":{"get_bytes":3023,"get_count":5,"put_bytes":2227,"put_count":10}} +{"i":9,"series":1,"stats":{"get_bytes":3371,"get_count":5,"put_bytes":2540,"put_count":10}} +{"i":10,"series":1,"stats":{"get_bytes":3684,"get_count":5,"put_bytes":2940,"put_count":11}} +{"i":11,"series":1,"stats":{"get_bytes":4084,"get_count":6,"put_bytes":3304,"put_count":12}} +{"i":12,"series":1,"stats":{"get_bytes":2728,"get_count":5,"put_bytes":1932,"put_count":10}} +{"i":13,"series":1,"stats":{"get_bytes":3076,"get_count":5,"put_bytes":2244,"put_count":10}} +{"i":14,"series":1,"stats":{"get_bytes":3388,"get_count":5,"put_bytes":2646,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":3790,"get_count":6,"put_bytes":2957,"put_count":11}} +{"i":16,"series":1,"stats":{"get_bytes":3957,"get_count":5,"put_bytes":3213,"put_count":11}} +{"i":17,"series":1,"stats":{"get_bytes":2781,"get_count":5,"put_bytes":1949,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":3093,"get_count":5,"put_bytes":2350,"put_count":11}} +{"i":19,"series":1,"stats":{"get_bytes":3494,"get_count":6,"put_bytes":2662,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":3662,"get_count":5,"put_bytes":2866,"put_count":10}} +{"i":21,"series":1,"stats":{"get_bytes":4010,"get_count":5,"put_bytes":3231,"put_count":11}} +{"i":22,"series":1,"stats":{"get_bytes":2799,"get_count":5,"put_bytes":2056,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":3200,"get_count":6,"put_bytes":2368,"put_count":11}} +{"i":24,"series":1,"stats":{"get_bytes":3368,"get_count":5,"put_bytes":2572,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":3716,"get_count":5,"put_bytes":2884,"put_count":10}} +{"i":26,"series":1,"stats":{"get_bytes":4028,"get_count":5,"put_bytes":3337,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":2905,"get_count":6,"put_bytes":2073,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":3073,"get_count":5,"put_bytes":2277,"put_count":10}} +{"i":29,"series":1,"stats":{"get_bytes":3421,"get_count":5,"put_bytes":2588,"put_count":10}} +{"i":30,"series":1,"stats":{"get_bytes":3732,"get_count":5,"put_bytes":2990,"put_count":11}} +{"i":31,"series":1,"stats":{"get_bytes":4134,"get_count":6,"put_bytes":3354,"put_count":12}} +{"i":32,"series":1,"stats":{"get_bytes":2778,"get_count":5,"put_bytes":1982,"put_count":10}} +{"i":33,"series":1,"stats":{"get_bytes":3126,"get_count":5,"put_bytes":2293,"put_count":10}} +{"i":34,"series":1,"stats":{"get_bytes":3437,"get_count":5,"put_bytes":2694,"put_count":11}} +{"i":35,"series":1,"stats":{"get_bytes":3838,"get_count":6,"put_bytes":3007,"put_count":11}} +{"i":36,"series":1,"stats":{"get_bytes":4007,"get_count":5,"put_bytes":3263,"put_count":11}} +{"i":37,"series":1,"stats":{"get_bytes":2831,"get_count":5,"put_bytes":1999,"put_count":10}} +{"i":38,"series":1,"stats":{"get_bytes":3143,"get_count":5,"put_bytes":2399,"put_count":11}} +{"i":39,"series":1,"stats":{"get_bytes":3543,"get_count":6,"put_bytes":2711,"put_count":11}} +{"i":40,"series":1,"stats":{"get_bytes":3711,"get_count":5,"put_bytes":2916,"put_count":10}} +{"i":41,"series":1,"stats":{"get_bytes":4060,"get_count":5,"put_bytes":3280,"put_count":11}} +{"i":42,"series":1,"stats":{"get_bytes":2848,"get_count":5,"put_bytes":2104,"put_count":11}} +{"i":43,"series":1,"stats":{"get_bytes":3248,"get_count":6,"put_bytes":2416,"put_count":11}} +{"i":44,"series":1,"stats":{"get_bytes":3416,"get_count":5,"put_bytes":2620,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":3764,"get_count":5,"put_bytes":2933,"put_count":10}} +{"i":46,"series":1,"stats":{"get_bytes":4077,"get_count":5,"put_bytes":3334,"put_count":11}} +{"i":47,"series":1,"stats":{"get_bytes":2902,"get_count":5,"put_bytes":2121,"put_count":11}} +{"i":48,"series":1,"stats":{"get_bytes":3121,"get_count":5,"put_bytes":2325,"put_count":10}} +{"i":49,"series":1,"stats":{"get_bytes":3469,"get_count":5,"put_bytes":2637,"put_count":10}} +{"i":50,"series":1,"stats":{"get_bytes":3781,"get_count":5,"put_bytes":3039,"put_count":11}} +{"i":51,"series":1,"stats":{"get_bytes":4183,"get_count":6,"put_bytes":3350,"put_count":11}} +{"i":52,"series":1,"stats":{"get_bytes":2774,"get_count":4,"put_bytes":2030,"put_count":10}} +{"i":53,"series":1,"stats":{"get_bytes":3174,"get_count":5,"put_bytes":2342,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":3486,"get_count":5,"put_bytes":2743,"put_count":11}} +{"i":55,"series":1,"stats":{"get_bytes":3887,"get_count":6,"put_bytes":3056,"put_count":11}} +{"i":56,"series":1,"stats":{"get_bytes":4056,"get_count":5,"put_bytes":3259,"put_count":10}} +{"i":57,"series":1,"stats":{"get_bytes":4403,"get_count":5,"put_bytes":3623,"put_count":11}} +{"i":58,"series":1,"stats":{"get_bytes":3191,"get_count":5,"put_bytes":2448,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":3592,"get_count":6,"put_bytes":2760,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":3760,"get_count":5,"put_bytes":2965,"put_count":10}} +{"i":61,"series":1,"stats":{"get_bytes":4109,"get_count":5,"put_bytes":3276,"put_count":10}} +{"i":62,"series":1,"stats":{"get_bytes":4420,"get_count":5,"put_bytes":3783,"put_count":13}} +{"i":63,"series":1,"stats":{"get_bytes":2707,"get_count":6,"put_bytes":1875,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":2875,"get_count":5,"put_bytes":2079,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":3223,"get_count":5,"put_bytes":2391,"put_count":10}} +{"i":66,"series":1,"stats":{"get_bytes":3535,"get_count":5,"put_bytes":2792,"put_count":11}} +{"i":67,"series":1,"stats":{"get_bytes":3936,"get_count":6,"put_bytes":3208,"put_count":13}} +{"i":68,"series":1,"stats":{"get_bytes":2632,"get_count":6,"put_bytes":1836,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":2980,"get_count":6,"put_bytes":2148,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":3292,"get_count":6,"put_bytes":2548,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":3692,"get_count":7,"put_bytes":2861,"put_count":12}} +{"i":72,"series":1,"stats":{"get_bytes":3861,"get_count":6,"put_bytes":3117,"put_count":12}} +{"i":73,"series":1,"stats":{"get_bytes":2685,"get_count":6,"put_bytes":1853,"put_count":11}} +{"i":74,"series":1,"stats":{"get_bytes":2997,"get_count":6,"put_bytes":2253,"put_count":12}} +{"i":75,"series":1,"stats":{"get_bytes":3397,"get_count":7,"put_bytes":2565,"put_count":12}} +{"i":76,"series":1,"stats":{"get_bytes":3565,"get_count":6,"put_bytes":2770,"put_count":11}} +{"i":77,"series":1,"stats":{"get_bytes":3914,"get_count":6,"put_bytes":3134,"put_count":12}} +{"i":78,"series":1,"stats":{"get_bytes":2702,"get_count":6,"put_bytes":1959,"put_count":12}} +{"i":79,"series":1,"stats":{"get_bytes":3103,"get_count":7,"put_bytes":2270,"put_count":12}} +{"i":80,"series":1,"stats":{"get_bytes":3270,"get_count":6,"put_bytes":2474,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":3618,"get_count":6,"put_bytes":2787,"put_count":11}} +{"i":82,"series":1,"stats":{"get_bytes":3931,"get_count":6,"put_bytes":3240,"put_count":13}} +{"i":83,"series":1,"stats":{"get_bytes":2808,"get_count":7,"put_bytes":1975,"put_count":12}} +{"i":84,"series":1,"stats":{"get_bytes":2975,"get_count":6,"put_bytes":2179,"put_count":11}} +{"i":85,"series":1,"stats":{"get_bytes":3323,"get_count":6,"put_bytes":2491,"put_count":11}} +{"i":86,"series":1,"stats":{"get_bytes":3635,"get_count":6,"put_bytes":2893,"put_count":12}} +{"i":87,"series":1,"stats":{"get_bytes":4037,"get_count":7,"put_bytes":3205,"put_count":12}} +{"i":88,"series":1,"stats":{"get_bytes":2773,"get_count":6,"put_bytes":1884,"put_count":11}} +{"i":89,"series":1,"stats":{"get_bytes":3028,"get_count":6,"put_bytes":2196,"put_count":11}} +{"i":90,"series":1,"stats":{"get_bytes":3340,"get_count":6,"put_bytes":2597,"put_count":12}} +{"i":91,"series":1,"stats":{"get_bytes":3741,"get_count":7,"put_bytes":2910,"put_count":12}} +{"i":92,"series":1,"stats":{"get_bytes":3910,"get_count":6,"put_bytes":3113,"put_count":11}} +{"i":93,"series":1,"stats":{"get_bytes":2681,"get_count":5,"put_bytes":1901,"put_count":11}} +{"i":94,"series":1,"stats":{"get_bytes":3045,"get_count":6,"put_bytes":2302,"put_count":12}} +{"i":95,"series":1,"stats":{"get_bytes":3446,"get_count":7,"put_bytes":2614,"put_count":12}} +{"i":96,"series":1,"stats":{"get_bytes":3614,"get_count":6,"put_bytes":2819,"put_count":11}} +{"i":97,"series":1,"stats":{"get_bytes":3963,"get_count":6,"put_bytes":3130,"put_count":11}} +{"i":98,"series":1,"stats":{"get_bytes":4274,"get_count":6,"put_bytes":3583,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":3151,"get_count":7,"put_bytes":2319,"put_count":12}} +{"i":0,"series":2,"stats":{"get_bytes":2228,"get_count":3,"put_bytes":1613,"put_count":10}} +{"i":1,"series":2,"stats":{"get_bytes":2757,"get_count":5,"put_bytes":1925,"put_count":10}} +{"i":2,"series":2,"stats":{"get_bytes":3069,"get_count":5,"put_bytes":2327,"put_count":11}} +{"i":3,"series":2,"stats":{"get_bytes":3471,"get_count":6,"put_bytes":2639,"put_count":11}} +{"i":4,"series":2,"stats":{"get_bytes":3639,"get_count":5,"put_bytes":1715,"put_count":11}} +{"i":5,"series":2,"stats":{"get_bytes":2859,"get_count":6,"put_bytes":2027,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":3171,"get_count":6,"put_bytes":2428,"put_count":12}} +{"i":7,"series":2,"stats":{"get_bytes":3572,"get_count":7,"put_bytes":2741,"put_count":12}} +{"i":8,"series":2,"stats":{"get_bytes":3741,"get_count":6,"put_bytes":2944,"put_count":11}} +{"i":9,"series":2,"stats":{"get_bytes":4088,"get_count":6,"put_bytes":3308,"put_count":12}} +{"i":10,"series":2,"stats":{"get_bytes":2876,"get_count":6,"put_bytes":2133,"put_count":12}} +{"i":11,"series":2,"stats":{"get_bytes":3277,"get_count":7,"put_bytes":2445,"put_count":12}} +{"i":12,"series":2,"stats":{"get_bytes":3445,"get_count":6,"put_bytes":2650,"put_count":11}} +{"i":13,"series":2,"stats":{"get_bytes":3794,"get_count":6,"put_bytes":2961,"put_count":11}} +{"i":14,"series":2,"stats":{"get_bytes":4105,"get_count":6,"put_bytes":3414,"put_count":13}} +{"i":15,"series":2,"stats":{"get_bytes":2982,"get_count":7,"put_bytes":2150,"put_count":12}} +{"i":16,"series":2,"stats":{"get_bytes":3150,"get_count":6,"put_bytes":2354,"put_count":11}} +{"i":17,"series":2,"stats":{"get_bytes":3498,"get_count":6,"put_bytes":2666,"put_count":11}} +{"i":18,"series":2,"stats":{"get_bytes":3810,"get_count":6,"put_bytes":3067,"put_count":12}} +{"i":19,"series":2,"stats":{"get_bytes":4211,"get_count":7,"put_bytes":3431,"put_count":13}} +{"i":20,"series":2,"stats":{"get_bytes":2855,"get_count":6,"put_bytes":2059,"put_count":11}} +{"i":21,"series":2,"stats":{"get_bytes":3203,"get_count":6,"put_bytes":2371,"put_count":11}} +{"i":22,"series":2,"stats":{"get_bytes":3515,"get_count":6,"put_bytes":2771,"put_count":12}} +{"i":23,"series":2,"stats":{"get_bytes":3915,"get_count":7,"put_bytes":3084,"put_count":12}} +{"i":24,"series":2,"stats":{"get_bytes":4084,"get_count":6,"put_bytes":3341,"put_count":12}} +{"i":25,"series":2,"stats":{"get_bytes":2909,"get_count":6,"put_bytes":2077,"put_count":11}} +{"i":26,"series":2,"stats":{"get_bytes":3221,"get_count":6,"put_bytes":2477,"put_count":12}} +{"i":27,"series":2,"stats":{"get_bytes":3621,"get_count":7,"put_bytes":2789,"put_count":12}} +{"i":28,"series":2,"stats":{"get_bytes":3789,"get_count":6,"put_bytes":2994,"put_count":11}} +{"i":29,"series":2,"stats":{"get_bytes":4138,"get_count":6,"put_bytes":3358,"put_count":12}} +{"i":30,"series":2,"stats":{"get_bytes":2926,"get_count":6,"put_bytes":2183,"put_count":12}} +{"i":31,"series":2,"stats":{"get_bytes":3327,"get_count":7,"put_bytes":2494,"put_count":12}} +{"i":32,"series":2,"stats":{"get_bytes":3494,"get_count":6,"put_bytes":2698,"put_count":11}} +{"i":33,"series":2,"stats":{"get_bytes":3842,"get_count":6,"put_bytes":3011,"put_count":11}} +{"i":34,"series":2,"stats":{"get_bytes":4155,"get_count":6,"put_bytes":3464,"put_count":13}} +{"i":35,"series":2,"stats":{"get_bytes":3032,"get_count":7,"put_bytes":2199,"put_count":12}} +{"i":36,"series":2,"stats":{"get_bytes":3199,"get_count":6,"put_bytes":2403,"put_count":11}} +{"i":37,"series":2,"stats":{"get_bytes":3547,"get_count":6,"put_bytes":2715,"put_count":11}} +{"i":38,"series":2,"stats":{"get_bytes":3859,"get_count":6,"put_bytes":3117,"put_count":12}} +{"i":39,"series":2,"stats":{"get_bytes":4261,"get_count":7,"put_bytes":3429,"put_count":12}} +{"i":40,"series":2,"stats":{"get_bytes":2997,"get_count":6,"put_bytes":2108,"put_count":11}} +{"i":41,"series":2,"stats":{"get_bytes":3252,"get_count":6,"put_bytes":2420,"put_count":11}} +{"i":42,"series":2,"stats":{"get_bytes":3564,"get_count":6,"put_bytes":2821,"put_count":12}} +{"i":43,"series":2,"stats":{"get_bytes":3965,"get_count":7,"put_bytes":3134,"put_count":12}} +{"i":44,"series":2,"stats":{"get_bytes":4134,"get_count":6,"put_bytes":3337,"put_count":11}} +{"i":45,"series":2,"stats":{"get_bytes":2905,"get_count":5,"put_bytes":2125,"put_count":11}} +{"i":46,"series":2,"stats":{"get_bytes":3269,"get_count":6,"put_bytes":2526,"put_count":12}} +{"i":47,"series":2,"stats":{"get_bytes":3670,"get_count":7,"put_bytes":2838,"put_count":12}} +{"i":48,"series":2,"stats":{"get_bytes":3838,"get_count":6,"put_bytes":3043,"put_count":11}} +{"i":49,"series":2,"stats":{"get_bytes":4187,"get_count":6,"put_bytes":3354,"put_count":11}} +{"i":50,"series":2,"stats":{"get_bytes":4498,"get_count":6,"put_bytes":3807,"put_count":13}} +{"i":51,"series":2,"stats":{"get_bytes":3375,"get_count":7,"put_bytes":2543,"put_count":12}} +{"i":52,"series":2,"stats":{"get_bytes":3543,"get_count":6,"put_bytes":2747,"put_count":11}} +{"i":53,"series":2,"stats":{"get_bytes":3891,"get_count":6,"put_bytes":3060,"put_count":11}} +{"i":54,"series":2,"stats":{"get_bytes":4204,"get_count":6,"put_bytes":3460,"put_count":12}} +{"i":55,"series":2,"stats":{"get_bytes":4604,"get_count":7,"put_bytes":3824,"put_count":13}} +{"i":56,"series":2,"stats":{"get_bytes":3248,"get_count":6,"put_bytes":2452,"put_count":11}} +{"i":57,"series":2,"stats":{"get_bytes":3596,"get_count":6,"put_bytes":2764,"put_count":11}} +{"i":58,"series":2,"stats":{"get_bytes":3908,"get_count":6,"put_bytes":3165,"put_count":12}} +{"i":59,"series":2,"stats":{"get_bytes":4309,"get_count":7,"put_bytes":3477,"put_count":12}} +{"i":60,"series":2,"stats":{"get_bytes":4477,"get_count":6,"put_bytes":3733,"put_count":12}} +{"i":61,"series":2,"stats":{"get_bytes":3301,"get_count":6,"put_bytes":2469,"put_count":11}} +{"i":62,"series":2,"stats":{"get_bytes":3613,"get_count":6,"put_bytes":2870,"put_count":12}} +{"i":63,"series":2,"stats":{"get_bytes":4014,"get_count":7,"put_bytes":3181,"put_count":12}} +{"i":64,"series":2,"stats":{"get_bytes":4181,"get_count":6,"put_bytes":3386,"put_count":11}} +{"i":65,"series":2,"stats":{"get_bytes":4530,"get_count":6,"put_bytes":3751,"put_count":12}} +{"i":66,"series":2,"stats":{"get_bytes":3319,"get_count":6,"put_bytes":2576,"put_count":12}} +{"i":67,"series":2,"stats":{"get_bytes":3720,"get_count":7,"put_bytes":2887,"put_count":12}} +{"i":68,"series":2,"stats":{"get_bytes":3887,"get_count":6,"put_bytes":3091,"put_count":11}} +{"i":69,"series":2,"stats":{"get_bytes":4235,"get_count":6,"put_bytes":3404,"put_count":11}} +{"i":70,"series":2,"stats":{"get_bytes":4548,"get_count":6,"put_bytes":3857,"put_count":13}} +{"i":71,"series":2,"stats":{"get_bytes":3425,"get_count":7,"put_bytes":2593,"put_count":12}} +{"i":72,"series":2,"stats":{"get_bytes":3593,"get_count":6,"put_bytes":2796,"put_count":11}} +{"i":73,"series":2,"stats":{"get_bytes":3940,"get_count":6,"put_bytes":3108,"put_count":11}} +{"i":74,"series":2,"stats":{"get_bytes":4252,"get_count":6,"put_bytes":3510,"put_count":12}} +{"i":75,"series":2,"stats":{"get_bytes":4654,"get_count":7,"put_bytes":3874,"put_count":13}} +{"i":76,"series":2,"stats":{"get_bytes":3298,"get_count":6,"put_bytes":2502,"put_count":11}} +{"i":77,"series":2,"stats":{"get_bytes":3646,"get_count":6,"put_bytes":2813,"put_count":11}} +{"i":78,"series":2,"stats":{"get_bytes":3957,"get_count":6,"put_bytes":3214,"put_count":12}} +{"i":79,"series":2,"stats":{"get_bytes":4358,"get_count":7,"put_bytes":3527,"put_count":12}} +{"i":80,"series":2,"stats":{"get_bytes":4527,"get_count":6,"put_bytes":3783,"put_count":12}} +{"i":81,"series":2,"stats":{"get_bytes":3351,"get_count":6,"put_bytes":2518,"put_count":11}} +{"i":82,"series":2,"stats":{"get_bytes":3662,"get_count":6,"put_bytes":2919,"put_count":12}} +{"i":83,"series":2,"stats":{"get_bytes":4063,"get_count":7,"put_bytes":3231,"put_count":12}} +{"i":84,"series":2,"stats":{"get_bytes":4231,"get_count":6,"put_bytes":3436,"put_count":11}} +{"i":85,"series":2,"stats":{"get_bytes":4580,"get_count":6,"put_bytes":3747,"put_count":11}} +{"i":86,"series":2,"stats":{"get_bytes":3315,"get_count":5,"put_bytes":2624,"put_count":12}} +{"i":87,"series":2,"stats":{"get_bytes":3768,"get_count":7,"put_bytes":2936,"put_count":12}} +{"i":88,"series":2,"stats":{"get_bytes":3936,"get_count":6,"put_bytes":3140,"put_count":11}} +{"i":89,"series":2,"stats":{"get_bytes":4284,"get_count":6,"put_bytes":3453,"put_count":11}} +{"i":90,"series":2,"stats":{"get_bytes":4597,"get_count":6,"put_bytes":3853,"put_count":12}} +{"i":91,"series":2,"stats":{"get_bytes":4997,"get_count":7,"put_bytes":4217,"put_count":13}} +{"i":92,"series":2,"stats":{"get_bytes":3641,"get_count":6,"put_bytes":2845,"put_count":11}} +{"i":93,"series":2,"stats":{"get_bytes":3989,"get_count":6,"put_bytes":3157,"put_count":11}} +{"i":94,"series":2,"stats":{"get_bytes":4301,"get_count":6,"put_bytes":3559,"put_count":12}} +{"i":95,"series":2,"stats":{"get_bytes":4703,"get_count":7,"put_bytes":3870,"put_count":12}} +{"i":96,"series":2,"stats":{"get_bytes":4870,"get_count":6,"put_bytes":4126,"put_count":12}} +{"i":97,"series":2,"stats":{"get_bytes":3694,"get_count":6,"put_bytes":2862,"put_count":11}} +{"i":98,"series":2,"stats":{"get_bytes":4006,"get_count":6,"put_bytes":3263,"put_count":12}} +{"i":99,"series":2,"stats":{"get_bytes":4407,"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 index 151ddffa1aa0f4f178bba264a8b42643b9da69ba..c89a2b6b96ab1f2ba97c67cb0f49f5fb7441590b 100644 GIT binary patch literal 27680 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 diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline index ab71d1d60..43f0ce6f8 100644 --- a/actors/evm/tests/measurements/array_read_n1.jsonline +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":3714,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":5443,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3370,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":4694,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4694,"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 index b09e5a8de69da3f8502ba896aca3813d3f7ff613..86fc61247153b719d10b4b45aa55267da1419ddf 100644 GIT binary patch literal 13481 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 VBKBS5g2#zv+IRIeKB+@r{SQW9_LKks literal 13832 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 diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index 00eee2005..0466b2203 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6634,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6307,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6314,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":8377,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6321,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":8893,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":8054,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5998,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6771,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":4715,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":5810,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":5558,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":5565,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":7544,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5917,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4341,"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 index 4be588637995d49e954c522434f1136f13479ed0..5be6a77250f0e84fc0de846fcfa701deeda111d8 100644 GIT binary patch literal 14532 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 literal 14738 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 literal 14604 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 diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline index b8694042d..4268e6397 100644 --- a/actors/evm/tests/measurements/mapping_add_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -4,197 +4,197 @@ {"i":3,"series":1,"stats":{"get_bytes":2220,"get_count":3,"put_bytes":253,"put_count":2}} {"i":4,"series":1,"stats":{"get_bytes":2261,"get_count":3,"put_bytes":294,"put_count":2}} {"i":5,"series":1,"stats":{"get_bytes":2302,"get_count":3,"put_bytes":335,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2343,"get_count":3,"put_bytes":447,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2366,"get_count":3,"put_bytes":399,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2407,"get_count":3,"put_bytes":510,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2430,"get_count":3,"put_bytes":463,"put_count":2}} -{"i":10,"series":1,"stats":{"get_bytes":2471,"get_count":3,"put_bytes":504,"put_count":2}} -{"i":11,"series":1,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":545,"put_count":2}} -{"i":12,"series":1,"stats":{"get_bytes":2553,"get_count":3,"put_bytes":586,"put_count":2}} -{"i":13,"series":1,"stats":{"get_bytes":2594,"get_count":3,"put_bytes":627,"put_count":2}} -{"i":14,"series":1,"stats":{"get_bytes":2635,"get_count":3,"put_bytes":739,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2658,"get_count":3,"put_bytes":762,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2681,"get_count":3,"put_bytes":785,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2704,"get_count":3,"put_bytes":737,"put_count":2}} -{"i":18,"series":1,"stats":{"get_bytes":2745,"get_count":3,"put_bytes":778,"put_count":2}} -{"i":19,"series":1,"stats":{"get_bytes":2786,"get_count":3,"put_bytes":889,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2811,"get_count":3,"put_bytes":844,"put_count":2}} -{"i":21,"series":1,"stats":{"get_bytes":2852,"get_count":3,"put_bytes":885,"put_count":2}} -{"i":22,"series":1,"stats":{"get_bytes":2893,"get_count":3,"put_bytes":926,"put_count":2}} -{"i":23,"series":1,"stats":{"get_bytes":2934,"get_count":3,"put_bytes":1038,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2957,"get_count":3,"put_bytes":1059,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":2980,"get_count":3,"put_bytes":1083,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":3003,"get_count":3,"put_bytes":1105,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":3026,"get_count":3,"put_bytes":1129,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":3049,"get_count":3,"put_bytes":1082,"put_count":2}} -{"i":29,"series":1,"stats":{"get_bytes":3090,"get_count":3,"put_bytes":1123,"put_count":2}} -{"i":30,"series":1,"stats":{"get_bytes":3131,"get_count":3,"put_bytes":1164,"put_count":2}} -{"i":31,"series":1,"stats":{"get_bytes":3172,"get_count":3,"put_bytes":1205,"put_count":2}} -{"i":32,"series":1,"stats":{"get_bytes":3213,"get_count":3,"put_bytes":1246,"put_count":2}} -{"i":33,"series":1,"stats":{"get_bytes":3254,"get_count":3,"put_bytes":1287,"put_count":2}} -{"i":34,"series":1,"stats":{"get_bytes":3295,"get_count":3,"put_bytes":1399,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":3318,"get_count":3,"put_bytes":1422,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":3341,"get_count":3,"put_bytes":1374,"put_count":2}} -{"i":37,"series":1,"stats":{"get_bytes":3382,"get_count":3,"put_bytes":1486,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":3492,"get_count":4,"put_bytes":1527,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":3405,"get_count":3,"put_bytes":1511,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":3519,"get_count":4,"put_bytes":1552,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":3517,"get_count":4,"put_bytes":1552,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":3430,"get_count":3,"put_bytes":1533,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":3542,"get_count":4,"put_bytes":1575,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":3453,"get_count":3,"put_bytes":1487,"put_count":2}} -{"i":45,"series":1,"stats":{"get_bytes":3625,"get_count":4,"put_bytes":1658,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":3666,"get_count":4,"put_bytes":1699,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":3581,"get_count":4,"put_bytes":1595,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":3493,"get_count":3,"put_bytes":1526,"put_count":2}} -{"i":49,"series":1,"stats":{"get_bytes":3534,"get_count":3,"put_bytes":1637,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":3557,"get_count":3,"put_bytes":1590,"put_count":2}} -{"i":51,"series":1,"stats":{"get_bytes":3687,"get_count":4,"put_bytes":1720,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":3598,"get_count":3,"put_bytes":1631,"put_count":2}} -{"i":53,"series":1,"stats":{"get_bytes":3769,"get_count":4,"put_bytes":1802,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":3639,"get_count":3,"put_bytes":1742,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":3750,"get_count":4,"put_bytes":1783,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":3662,"get_count":3,"put_bytes":1695,"put_count":2}} -{"i":57,"series":1,"stats":{"get_bytes":3703,"get_count":3,"put_bytes":1736,"put_count":2}} -{"i":58,"series":1,"stats":{"get_bytes":3833,"get_count":4,"put_bytes":1866,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":3833,"get_count":4,"put_bytes":1866,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":3744,"get_count":3,"put_bytes":1777,"put_count":2}} -{"i":61,"series":1,"stats":{"get_bytes":3997,"get_count":4,"put_bytes":2101,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":3785,"get_count":3,"put_bytes":1818,"put_count":2}} -{"i":63,"series":1,"stats":{"get_bytes":3915,"get_count":4,"put_bytes":1928,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":3936,"get_count":4,"put_bytes":1969,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":3913,"get_count":4,"put_bytes":1946,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":3912,"get_count":4,"put_bytes":1945,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":3995,"get_count":4,"put_bytes":2028,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":3913,"get_count":4,"put_bytes":1946,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":3934,"get_count":4,"put_bytes":1967,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":3824,"get_count":3,"put_bytes":1927,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":3847,"get_count":3,"put_bytes":1951,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1991,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":3870,"get_count":3,"put_bytes":1903,"put_count":2}} -{"i":74,"series":1,"stats":{"get_bytes":4041,"get_count":4,"put_bytes":2074,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":3911,"get_count":3,"put_bytes":2015,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":4022,"get_count":4,"put_bytes":2055,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":4064,"get_count":4,"put_bytes":2097,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":4064,"get_count":4,"put_bytes":2097,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":4064,"get_count":4,"put_bytes":2097,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":3934,"get_count":3,"put_bytes":2038,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":4046,"get_count":4,"put_bytes":2079,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":3957,"get_count":3,"put_bytes":2059,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":3980,"get_count":3,"put_bytes":2084,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":4132,"get_count":4,"put_bytes":2165,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":4090,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":4133,"get_count":4,"put_bytes":2234,"put_count":4}} -{"i":87,"series":1,"stats":{"get_bytes":4238,"get_count":4,"put_bytes":2342,"put_count":4}} -{"i":88,"series":1,"stats":{"get_bytes":4003,"get_count":3,"put_bytes":2105,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":4154,"get_count":4,"put_bytes":2189,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":4197,"get_count":4,"put_bytes":2230,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":4156,"get_count":4,"put_bytes":2189,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":4155,"get_count":4,"put_bytes":2188,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":4197,"get_count":4,"put_bytes":2230,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":4197,"get_count":4,"put_bytes":2230,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":4115,"get_count":4,"put_bytes":2148,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":4238,"get_count":4,"put_bytes":2271,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":4026,"get_count":3,"put_bytes":2130,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":4137,"get_count":4,"put_bytes":2170,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":4049,"get_count":3,"put_bytes":2150,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":4225,"get_count":4,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":4160,"get_count":4,"put_bytes":2193,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":4201,"get_count":4,"put_bytes":2234,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":4201,"get_count":4,"put_bytes":2235,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":4160,"get_count":4,"put_bytes":2193,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":4072,"get_count":3,"put_bytes":2175,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":4248,"get_count":4,"put_bytes":2281,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":4225,"get_count":4,"put_bytes":2258,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2408,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":4225,"get_count":4,"put_bytes":2258,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":4184,"get_count":4,"put_bytes":2217,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":4307,"get_count":4,"put_bytes":2340,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":4248,"get_count":4,"put_bytes":2281,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":4184,"get_count":4,"put_bytes":2217,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":4181,"get_count":4,"put_bytes":2217,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":4266,"get_count":4,"put_bytes":2299,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":4224,"get_count":4,"put_bytes":2257,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":4095,"get_count":3,"put_bytes":2199,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":4248,"get_count":4,"put_bytes":2281,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":4289,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":4247,"get_count":4,"put_bytes":2280,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":4288,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":4288,"get_count":4,"put_bytes":2322,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":4288,"get_count":4,"put_bytes":2321,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":4248,"get_count":4,"put_bytes":2281,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":4330,"get_count":4,"put_bytes":2363,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":4247,"get_count":4,"put_bytes":2349,"put_count":4}} -{"i":28,"series":2,"stats":{"get_bytes":4371,"get_count":4,"put_bytes":2404,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":4118,"get_count":3,"put_bytes":2222,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":4311,"get_count":4,"put_bytes":2344,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":4352,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":4352,"get_count":4,"put_bytes":2385,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":4230,"get_count":4,"put_bytes":2263,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":4230,"get_count":4,"put_bytes":2263,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2416,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":4311,"get_count":4,"put_bytes":2344,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":4293,"get_count":4,"put_bytes":2326,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2414,"put_count":4}} -{"i":43,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":4228,"get_count":4,"put_bytes":2263,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4292,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2414,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":4229,"get_count":4,"put_bytes":2263,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4230,"get_count":4,"put_bytes":2263,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4271,"get_count":4,"put_bytes":2374,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":4352,"get_count":4,"put_bytes":2456,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4230,"get_count":4,"put_bytes":2331,"put_count":4}} -{"i":58,"series":2,"stats":{"get_bytes":4440,"get_count":4,"put_bytes":2473,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2538,"put_count":4}} -{"i":60,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2538,"put_count":4}} -{"i":63,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2538,"put_count":4}} -{"i":64,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2457,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4334,"get_count":4,"put_bytes":2437,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":4376,"get_count":4,"put_bytes":2409,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4294,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4394,"get_count":4,"put_bytes":2427,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4312,"get_count":4,"put_bytes":2345,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4353,"get_count":4,"put_bytes":2386,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4435,"get_count":4,"put_bytes":2468,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4271,"get_count":4,"put_bytes":2304,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4141,"get_count":3,"put_bytes":2245,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2491,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2432,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4398,"get_count":4,"put_bytes":2431,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4481,"get_count":4,"put_bytes":2514,"put_count":3}} -{"i":81,"series":2,"stats":{"get_bytes":4294,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":82,"series":2,"stats":{"get_bytes":4294,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":83,"series":2,"stats":{"get_bytes":4458,"get_count":4,"put_bytes":2561,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4440,"get_count":4,"put_bytes":2473,"put_count":3}} -{"i":85,"series":2,"stats":{"get_bytes":4294,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":86,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2432,"put_count":3}} -{"i":87,"series":2,"stats":{"get_bytes":4440,"get_count":4,"put_bytes":2473,"put_count":3}} -{"i":88,"series":2,"stats":{"get_bytes":4440,"get_count":4,"put_bytes":2473,"put_count":3}} -{"i":89,"series":2,"stats":{"get_bytes":4253,"get_count":4,"put_bytes":2286,"put_count":3}} -{"i":90,"series":2,"stats":{"get_bytes":4380,"get_count":4,"put_bytes":2414,"put_count":3}} -{"i":91,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":92,"series":2,"stats":{"get_bytes":4440,"get_count":4,"put_bytes":2473,"put_count":3}} -{"i":93,"series":2,"stats":{"get_bytes":4294,"get_count":4,"put_bytes":2327,"put_count":3}} -{"i":94,"series":2,"stats":{"get_bytes":4417,"get_count":4,"put_bytes":2450,"put_count":3}} -{"i":95,"series":2,"stats":{"get_bytes":4335,"get_count":4,"put_bytes":2368,"put_count":3}} -{"i":96,"series":2,"stats":{"get_bytes":4164,"get_count":3,"put_bytes":2266,"put_count":3}} -{"i":97,"series":2,"stats":{"get_bytes":4399,"get_count":4,"put_bytes":2432,"put_count":3}} -{"i":98,"series":2,"stats":{"get_bytes":4463,"get_count":4,"put_bytes":2496,"put_count":3}} -{"i":99,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2350,"put_count":3}} +{"i":6,"series":1,"stats":{"get_bytes":2343,"get_count":3,"put_bytes":432,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2351,"get_count":3,"put_bytes":384,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2392,"get_count":3,"put_bytes":480,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2400,"get_count":3,"put_bytes":433,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2441,"get_count":3,"put_bytes":474,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2482,"get_count":3,"put_bytes":515,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2523,"get_count":3,"put_bytes":556,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2564,"get_count":3,"put_bytes":597,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2605,"get_count":3,"put_bytes":694,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2613,"get_count":3,"put_bytes":702,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2621,"get_count":3,"put_bytes":710,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2629,"get_count":3,"put_bytes":662,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2670,"get_count":3,"put_bytes":703,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2711,"get_count":3,"put_bytes":798,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2720,"get_count":3,"put_bytes":753,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2761,"get_count":3,"put_bytes":794,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2802,"get_count":3,"put_bytes":835,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2843,"get_count":3,"put_bytes":932,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2851,"get_count":3,"put_bytes":938,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2859,"get_count":3,"put_bytes":947,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2867,"get_count":3,"put_bytes":954,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2875,"get_count":3,"put_bytes":963,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2883,"get_count":3,"put_bytes":916,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2924,"get_count":3,"put_bytes":957,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2965,"get_count":3,"put_bytes":998,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3006,"get_count":3,"put_bytes":1039,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3047,"get_count":3,"put_bytes":1080,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3088,"get_count":3,"put_bytes":1121,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3129,"get_count":3,"put_bytes":1218,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":3137,"get_count":3,"put_bytes":1226,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":3145,"get_count":3,"put_bytes":1178,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3186,"get_count":3,"put_bytes":1275,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":3281,"get_count":4,"put_bytes":1316,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":3194,"get_count":3,"put_bytes":1284,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":3292,"get_count":4,"put_bytes":1325,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":3290,"get_count":4,"put_bytes":1325,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":3203,"get_count":3,"put_bytes":1291,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":3300,"get_count":4,"put_bytes":1333,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":3211,"get_count":3,"put_bytes":1245,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3383,"get_count":4,"put_bytes":1416,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":3424,"get_count":4,"put_bytes":1457,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":3339,"get_count":4,"put_bytes":1339,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":3252,"get_count":3,"put_bytes":1285,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3293,"get_count":3,"put_bytes":1381,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":3301,"get_count":3,"put_bytes":1334,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3431,"get_count":4,"put_bytes":1464,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":3342,"get_count":3,"put_bytes":1375,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3513,"get_count":4,"put_bytes":1546,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":3383,"get_count":3,"put_bytes":1471,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":3479,"get_count":4,"put_bytes":1512,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":3391,"get_count":3,"put_bytes":1424,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3432,"get_count":3,"put_bytes":1465,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3562,"get_count":4,"put_bytes":1595,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":3562,"get_count":4,"put_bytes":1595,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":3473,"get_count":3,"put_bytes":1506,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3726,"get_count":4,"put_bytes":1815,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":3514,"get_count":3,"put_bytes":1547,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3644,"get_count":4,"put_bytes":1643,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":3651,"get_count":4,"put_bytes":1684,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":3643,"get_count":4,"put_bytes":1676,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":3642,"get_count":4,"put_bytes":1675,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":3725,"get_count":4,"put_bytes":1758,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":3643,"get_count":4,"put_bytes":1676,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":3649,"get_count":4,"put_bytes":1682,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":3554,"get_count":3,"put_bytes":1642,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":3562,"get_count":3,"put_bytes":1651,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":3658,"get_count":4,"put_bytes":1691,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":3570,"get_count":3,"put_bytes":1603,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":3741,"get_count":4,"put_bytes":1774,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":3611,"get_count":3,"put_bytes":1700,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":3707,"get_count":4,"put_bytes":1740,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":3749,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":3619,"get_count":3,"put_bytes":1708,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":3716,"get_count":4,"put_bytes":1749,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":3627,"get_count":3,"put_bytes":1714,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":3635,"get_count":3,"put_bytes":1724,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":3772,"get_count":4,"put_bytes":1805,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":3730,"get_count":4,"put_bytes":1763,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":3773,"get_count":4,"put_bytes":1859,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":3863,"get_count":4,"put_bytes":1952,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":3643,"get_count":3,"put_bytes":1730,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":3779,"get_count":4,"put_bytes":1814,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":3822,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":3781,"get_count":4,"put_bytes":1814,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":3822,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":3822,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":3740,"get_count":4,"put_bytes":1773,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":3863,"get_count":4,"put_bytes":1896,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":3651,"get_count":3,"put_bytes":1740,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1780,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":3659,"get_count":3,"put_bytes":1745,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":3805,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":3755,"get_count":4,"put_bytes":1788,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":3796,"get_count":4,"put_bytes":1829,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":3796,"get_count":4,"put_bytes":1830,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":3755,"get_count":4,"put_bytes":1788,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":3667,"get_count":3,"put_bytes":1755,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":3813,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":3805,"get_count":4,"put_bytes":1838,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":3887,"get_count":4,"put_bytes":1973,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":3805,"get_count":4,"put_bytes":1838,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":3764,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":3887,"get_count":4,"put_bytes":1920,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":3813,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":3764,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":3761,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":3846,"get_count":4,"put_bytes":1879,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":3804,"get_count":4,"put_bytes":1837,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":3675,"get_count":3,"put_bytes":1764,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":3813,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":3854,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":3812,"get_count":4,"put_bytes":1845,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":3853,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":3853,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":3853,"get_count":4,"put_bytes":1886,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":3813,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":3895,"get_count":4,"put_bytes":1928,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":3812,"get_count":4,"put_bytes":1899,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":3936,"get_count":4,"put_bytes":1969,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":3683,"get_count":3,"put_bytes":1772,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":3861,"get_count":4,"put_bytes":1894,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":3902,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":3902,"get_count":4,"put_bytes":1935,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1951,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":3861,"get_count":4,"put_bytes":1894,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":3828,"get_count":4,"put_bytes":1861,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1949,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":3944,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":3911,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":3944,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1949,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":3779,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":3821,"get_count":4,"put_bytes":1909,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":3902,"get_count":4,"put_bytes":1991,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":3944,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":3780,"get_count":4,"put_bytes":1866,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":3985,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":3911,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":3944,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":3985,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":3985,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1992,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":3869,"get_count":4,"put_bytes":1957,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":3911,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":3829,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":3993,"get_count":4,"put_bytes":2026,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":3944,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":3862,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":3903,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":3985,"get_count":4,"put_bytes":2018,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":3821,"get_count":4,"put_bytes":1854,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":3691,"get_count":3,"put_bytes":1780,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":3993,"get_count":4,"put_bytes":2026,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":3919,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":3918,"get_count":4,"put_bytes":1951,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4001,"get_count":4,"put_bytes":2034,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":3829,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":3829,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":3993,"get_count":4,"put_bytes":2081,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":3829,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":3919,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":3788,"get_count":4,"put_bytes":1821,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1919,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":3960,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":3829,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":3952,"get_count":4,"put_bytes":1985,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":3870,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":3699,"get_count":3,"put_bytes":1786,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":3919,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":3968,"get_count":4,"put_bytes":2001,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":3837,"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 index 29c0241965b7bfca06c93c75550829b2ce27440a..d0d2143593fdc3212769b7320640617942cf20d2 100644 GIT binary patch literal 20987 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 Mt(CoHiG>&CUvJAn%m4rY literal 20173 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 diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index cb6ff04d9..fa60e24c9 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ {"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2134,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":6554,"put_count":35}} -{"i":2,"series":1,"stats":{"get_bytes":7777,"get_count":28,"put_bytes":11006,"put_count":43}} -{"i":3,"series":1,"stats":{"get_bytes":11312,"get_count":32,"put_bytes":14463,"put_count":46}} -{"i":4,"series":1,"stats":{"get_bytes":15896,"get_count":37,"put_bytes":20233,"put_count":68}} -{"i":5,"series":1,"stats":{"get_bytes":19343,"get_count":43,"put_bytes":22985,"put_count":64}} -{"i":6,"series":1,"stats":{"get_bytes":20968,"get_count":38,"put_bytes":25593,"put_count":73}} -{"i":7,"series":1,"stats":{"get_bytes":25697,"get_count":47,"put_bytes":30253,"put_count":81}} -{"i":8,"series":1,"stats":{"get_bytes":30179,"get_count":48,"put_bytes":34725,"put_count":82}} -{"i":9,"series":1,"stats":{"get_bytes":32139,"get_count":55,"put_bytes":36932,"put_count":93}} -{"i":10,"series":1,"stats":{"get_bytes":35652,"get_count":49,"put_bytes":40066,"put_count":81}} -{"i":11,"series":1,"stats":{"get_bytes":38862,"get_count":54,"put_bytes":43831,"put_count":94}} -{"i":12,"series":1,"stats":{"get_bytes":41782,"get_count":70,"put_bytes":46581,"put_count":108}} -{"i":13,"series":1,"stats":{"get_bytes":41333,"get_count":68,"put_bytes":45879,"put_count":102}} -{"i":14,"series":1,"stats":{"get_bytes":45849,"get_count":67,"put_bytes":50943,"put_count":109}} -{"i":15,"series":1,"stats":{"get_bytes":47278,"get_count":71,"put_bytes":51977,"put_count":107}} -{"i":16,"series":1,"stats":{"get_bytes":49879,"get_count":72,"put_bytes":54909,"put_count":113}} -{"i":17,"series":1,"stats":{"get_bytes":50940,"get_count":74,"put_bytes":55922,"put_count":114}} -{"i":18,"series":1,"stats":{"get_bytes":49574,"get_count":75,"put_bytes":54469,"put_count":114}} -{"i":19,"series":1,"stats":{"get_bytes":59239,"get_count":92,"put_bytes":63375,"put_count":120}} -{"i":20,"series":1,"stats":{"get_bytes":54031,"get_count":81,"put_bytes":58457,"put_count":113}} -{"i":21,"series":1,"stats":{"get_bytes":56913,"get_count":80,"put_bytes":61973,"put_count":121}} -{"i":22,"series":1,"stats":{"get_bytes":57246,"get_count":83,"put_bytes":61810,"put_count":117}} -{"i":23,"series":1,"stats":{"get_bytes":65864,"get_count":108,"put_bytes":68920,"put_count":121}} -{"i":24,"series":1,"stats":{"get_bytes":64955,"get_count":104,"put_bytes":68656,"put_count":126}} -{"i":25,"series":1,"stats":{"get_bytes":65391,"get_count":102,"put_bytes":69591,"put_count":131}} -{"i":26,"series":1,"stats":{"get_bytes":64408,"get_count":94,"put_bytes":69045,"put_count":129}} -{"i":27,"series":1,"stats":{"get_bytes":67513,"get_count":107,"put_bytes":70956,"put_count":125}} -{"i":28,"series":1,"stats":{"get_bytes":67904,"get_count":106,"put_bytes":72047,"put_count":134}} -{"i":29,"series":1,"stats":{"get_bytes":68580,"get_count":106,"put_bytes":71955,"put_count":123}} -{"i":30,"series":1,"stats":{"get_bytes":73646,"get_count":113,"put_bytes":77706,"put_count":140}} -{"i":31,"series":1,"stats":{"get_bytes":67804,"get_count":107,"put_bytes":71640,"put_count":131}} -{"i":32,"series":1,"stats":{"get_bytes":74910,"get_count":111,"put_bytes":78747,"put_count":135}} -{"i":33,"series":1,"stats":{"get_bytes":72432,"get_count":114,"put_bytes":76476,"put_count":141}} -{"i":34,"series":1,"stats":{"get_bytes":74763,"get_count":113,"put_bytes":78079,"put_count":130}} -{"i":35,"series":1,"stats":{"get_bytes":74285,"get_count":116,"put_bytes":77473,"put_count":131}} -{"i":36,"series":1,"stats":{"get_bytes":75305,"get_count":113,"put_bytes":78580,"put_count":129}} -{"i":37,"series":1,"stats":{"get_bytes":76323,"get_count":117,"put_bytes":79980,"put_count":138}} -{"i":38,"series":1,"stats":{"get_bytes":78507,"get_count":120,"put_bytes":82351,"put_count":144}} -{"i":39,"series":1,"stats":{"get_bytes":73026,"get_count":115,"put_bytes":76261,"put_count":130}} -{"i":40,"series":1,"stats":{"get_bytes":78518,"get_count":121,"put_bytes":81598,"put_count":134}} -{"i":41,"series":1,"stats":{"get_bytes":78078,"get_count":118,"put_bytes":81384,"put_count":134}} -{"i":42,"series":1,"stats":{"get_bytes":80554,"get_count":118,"put_bytes":84343,"put_count":141}} -{"i":43,"series":1,"stats":{"get_bytes":79037,"get_count":121,"put_bytes":82319,"put_count":137}} -{"i":44,"series":1,"stats":{"get_bytes":84665,"get_count":127,"put_bytes":87620,"put_count":138}} -{"i":45,"series":1,"stats":{"get_bytes":81928,"get_count":123,"put_bytes":85547,"put_count":144}} -{"i":46,"series":1,"stats":{"get_bytes":83852,"get_count":128,"put_bytes":87353,"put_count":147}} -{"i":47,"series":1,"stats":{"get_bytes":83588,"get_count":128,"put_bytes":86955,"put_count":145}} -{"i":48,"series":1,"stats":{"get_bytes":87187,"get_count":125,"put_bytes":90351,"put_count":139}} -{"i":49,"series":1,"stats":{"get_bytes":85091,"get_count":127,"put_bytes":88527,"put_count":145}} -{"i":50,"series":1,"stats":{"get_bytes":81640,"get_count":117,"put_bytes":84793,"put_count":131}} -{"i":51,"series":1,"stats":{"get_bytes":85325,"get_count":119,"put_bytes":88411,"put_count":132}} -{"i":52,"series":1,"stats":{"get_bytes":86109,"get_count":128,"put_bytes":89538,"put_count":146}} -{"i":53,"series":1,"stats":{"get_bytes":82852,"get_count":122,"put_bytes":85722,"put_count":132}} -{"i":54,"series":1,"stats":{"get_bytes":87816,"get_count":130,"put_bytes":91116,"put_count":146}} -{"i":55,"series":1,"stats":{"get_bytes":85249,"get_count":120,"put_bytes":88308,"put_count":133}} -{"i":56,"series":1,"stats":{"get_bytes":85708,"get_count":127,"put_bytes":88934,"put_count":142}} -{"i":57,"series":1,"stats":{"get_bytes":90012,"get_count":132,"put_bytes":92945,"put_count":143}} -{"i":58,"series":1,"stats":{"get_bytes":87346,"get_count":129,"put_bytes":90555,"put_count":144}} -{"i":59,"series":1,"stats":{"get_bytes":89677,"get_count":135,"put_bytes":92404,"put_count":143}} -{"i":60,"series":1,"stats":{"get_bytes":88187,"get_count":129,"put_bytes":91481,"put_count":145}} -{"i":61,"series":1,"stats":{"get_bytes":92745,"get_count":131,"put_bytes":95898,"put_count":145}} -{"i":62,"series":1,"stats":{"get_bytes":85466,"get_count":127,"put_bytes":88420,"put_count":138}} -{"i":63,"series":1,"stats":{"get_bytes":93748,"get_count":134,"put_bytes":96984,"put_count":150}} -{"i":64,"series":1,"stats":{"get_bytes":93617,"get_count":131,"put_bytes":96847,"put_count":146}} -{"i":65,"series":1,"stats":{"get_bytes":86322,"get_count":126,"put_bytes":89756,"put_count":144}} -{"i":66,"series":1,"stats":{"get_bytes":92153,"get_count":128,"put_bytes":95654,"put_count":147}} -{"i":67,"series":1,"stats":{"get_bytes":92910,"get_count":134,"put_bytes":95854,"put_count":145}} -{"i":68,"series":1,"stats":{"get_bytes":94491,"get_count":130,"put_bytes":97645,"put_count":144}} -{"i":69,"series":1,"stats":{"get_bytes":94275,"get_count":131,"put_bytes":97562,"put_count":147}} -{"i":70,"series":1,"stats":{"get_bytes":95375,"get_count":131,"put_bytes":98797,"put_count":149}} -{"i":71,"series":1,"stats":{"get_bytes":91888,"get_count":126,"put_bytes":95390,"put_count":145}} -{"i":72,"series":1,"stats":{"get_bytes":91880,"get_count":131,"put_bytes":95170,"put_count":147}} -{"i":73,"series":1,"stats":{"get_bytes":93479,"get_count":132,"put_bytes":96344,"put_count":142}} -{"i":74,"series":1,"stats":{"get_bytes":90639,"get_count":127,"put_bytes":94002,"put_count":144}} -{"i":75,"series":1,"stats":{"get_bytes":97901,"get_count":134,"put_bytes":101335,"put_count":152}} -{"i":76,"series":1,"stats":{"get_bytes":96107,"get_count":131,"put_bytes":99400,"put_count":147}} -{"i":77,"series":1,"stats":{"get_bytes":93884,"get_count":132,"put_bytes":96615,"put_count":140}} -{"i":78,"series":1,"stats":{"get_bytes":95412,"get_count":130,"put_bytes":98491,"put_count":143}} -{"i":79,"series":1,"stats":{"get_bytes":95309,"get_count":132,"put_bytes":99091,"put_count":155}} -{"i":80,"series":1,"stats":{"get_bytes":96163,"get_count":133,"put_bytes":99590,"put_count":151}} -{"i":81,"series":1,"stats":{"get_bytes":99744,"get_count":138,"put_bytes":103172,"put_count":156}} -{"i":82,"series":1,"stats":{"get_bytes":95866,"get_count":127,"put_bytes":99719,"put_count":151}} -{"i":83,"series":1,"stats":{"get_bytes":92750,"get_count":123,"put_bytes":95900,"put_count":137}} -{"i":84,"series":1,"stats":{"get_bytes":102159,"get_count":134,"put_bytes":105649,"put_count":153}} -{"i":85,"series":1,"stats":{"get_bytes":97126,"get_count":129,"put_bytes":100204,"put_count":142}} -{"i":86,"series":1,"stats":{"get_bytes":99483,"get_count":131,"put_bytes":102636,"put_count":145}} -{"i":87,"series":1,"stats":{"get_bytes":97394,"get_count":129,"put_bytes":100825,"put_count":147}} -{"i":88,"series":1,"stats":{"get_bytes":98292,"get_count":131,"put_bytes":102136,"put_count":155}} -{"i":89,"series":1,"stats":{"get_bytes":101855,"get_count":133,"put_bytes":105768,"put_count":158}} -{"i":90,"series":1,"stats":{"get_bytes":105111,"get_count":135,"put_bytes":108822,"put_count":157}} -{"i":91,"series":1,"stats":{"get_bytes":98957,"get_count":135,"put_bytes":102457,"put_count":154}} -{"i":92,"series":1,"stats":{"get_bytes":105017,"get_count":132,"put_bytes":108868,"put_count":156}} -{"i":93,"series":1,"stats":{"get_bytes":99211,"get_count":130,"put_bytes":103621,"put_count":162}} -{"i":94,"series":1,"stats":{"get_bytes":102248,"get_count":130,"put_bytes":105809,"put_count":150}} -{"i":95,"series":1,"stats":{"get_bytes":102425,"get_count":132,"put_bytes":106280,"put_count":156}} -{"i":96,"series":1,"stats":{"get_bytes":99907,"get_count":134,"put_bytes":102985,"put_count":147}} -{"i":97,"series":1,"stats":{"get_bytes":100975,"get_count":135,"put_bytes":104693,"put_count":157}} -{"i":98,"series":1,"stats":{"get_bytes":99863,"get_count":133,"put_bytes":103356,"put_count":152}} -{"i":99,"series":1,"stats":{"get_bytes":103299,"get_count":135,"put_bytes":106661,"put_count":152}} -{"i":0,"series":2,"stats":{"get_bytes":106272,"get_count":140,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":104139,"get_count":131,"put_bytes":107776,"put_count":152}} -{"i":2,"series":2,"stats":{"get_bytes":103514,"get_count":132,"put_bytes":107501,"put_count":158}} -{"i":3,"series":2,"stats":{"get_bytes":105431,"get_count":133,"put_bytes":109496,"put_count":160}} -{"i":4,"series":2,"stats":{"get_bytes":99321,"get_count":130,"put_bytes":103237,"put_count":155}} -{"i":5,"series":2,"stats":{"get_bytes":104827,"get_count":131,"put_bytes":108888,"put_count":158}} -{"i":6,"series":2,"stats":{"get_bytes":108351,"get_count":133,"put_bytes":112277,"put_count":158}} -{"i":7,"series":2,"stats":{"get_bytes":108257,"get_count":134,"put_bytes":112325,"put_count":161}} -{"i":8,"series":2,"stats":{"get_bytes":110773,"get_count":138,"put_bytes":114692,"put_count":163}} -{"i":9,"series":2,"stats":{"get_bytes":109402,"get_count":134,"put_bytes":113183,"put_count":157}} -{"i":10,"series":2,"stats":{"get_bytes":105106,"get_count":131,"put_bytes":108886,"put_count":154}} -{"i":11,"series":2,"stats":{"get_bytes":108427,"get_count":137,"put_bytes":111996,"put_count":157}} -{"i":12,"series":2,"stats":{"get_bytes":106116,"get_count":130,"put_bytes":109951,"put_count":154}} -{"i":13,"series":2,"stats":{"get_bytes":105888,"get_count":132,"put_bytes":109742,"put_count":156}} -{"i":14,"series":2,"stats":{"get_bytes":109993,"get_count":135,"put_bytes":114037,"put_count":162}} -{"i":15,"series":2,"stats":{"get_bytes":97883,"get_count":127,"put_bytes":101239,"put_count":144}} -{"i":16,"series":2,"stats":{"get_bytes":109591,"get_count":134,"put_bytes":113309,"put_count":156}} -{"i":17,"series":2,"stats":{"get_bytes":110968,"get_count":136,"put_bytes":114534,"put_count":156}} -{"i":18,"series":2,"stats":{"get_bytes":111564,"get_count":135,"put_bytes":115698,"put_count":163}} -{"i":19,"series":2,"stats":{"get_bytes":108183,"get_count":129,"put_bytes":111476,"put_count":145}} -{"i":20,"series":2,"stats":{"get_bytes":108872,"get_count":132,"put_bytes":112719,"put_count":156}} -{"i":21,"series":2,"stats":{"get_bytes":115281,"get_count":139,"put_bytes":119275,"put_count":165}} -{"i":22,"series":2,"stats":{"get_bytes":111999,"get_count":135,"put_bytes":115919,"put_count":160}} -{"i":23,"series":2,"stats":{"get_bytes":109424,"get_count":131,"put_bytes":113136,"put_count":153}} -{"i":24,"series":2,"stats":{"get_bytes":111843,"get_count":131,"put_bytes":115825,"put_count":157}} -{"i":25,"series":2,"stats":{"get_bytes":112963,"get_count":129,"put_bytes":116793,"put_count":153}} -{"i":26,"series":2,"stats":{"get_bytes":115012,"get_count":139,"put_bytes":118518,"put_count":158}} -{"i":27,"series":2,"stats":{"get_bytes":116226,"get_count":138,"put_bytes":119983,"put_count":161}} -{"i":28,"series":2,"stats":{"get_bytes":114351,"get_count":135,"put_bytes":118611,"put_count":165}} -{"i":29,"series":2,"stats":{"get_bytes":107988,"get_count":133,"put_bytes":111496,"put_count":152}} -{"i":30,"series":2,"stats":{"get_bytes":112770,"get_count":136,"put_bytes":116902,"put_count":164}} -{"i":31,"series":2,"stats":{"get_bytes":115528,"get_count":137,"put_bytes":119726,"put_count":166}} -{"i":32,"series":2,"stats":{"get_bytes":116339,"get_count":139,"put_bytes":120818,"put_count":172}} -{"i":33,"series":2,"stats":{"get_bytes":109765,"get_count":131,"put_bytes":114023,"put_count":161}} -{"i":34,"series":2,"stats":{"get_bytes":115903,"get_count":136,"put_bytes":119887,"put_count":162}} -{"i":35,"series":2,"stats":{"get_bytes":117199,"get_count":133,"put_bytes":121241,"put_count":160}} -{"i":36,"series":2,"stats":{"get_bytes":116435,"get_count":134,"put_bytes":121129,"put_count":170}} -{"i":37,"series":2,"stats":{"get_bytes":114810,"get_count":132,"put_bytes":118734,"put_count":157}} -{"i":38,"series":2,"stats":{"get_bytes":112792,"get_count":131,"put_bytes":117423,"put_count":166}} -{"i":39,"series":2,"stats":{"get_bytes":110344,"get_count":131,"put_bytes":113906,"put_count":151}} -{"i":40,"series":2,"stats":{"get_bytes":111377,"get_count":131,"put_bytes":115433,"put_count":158}} -{"i":41,"series":2,"stats":{"get_bytes":115744,"get_count":143,"put_bytes":119593,"put_count":167}} -{"i":42,"series":2,"stats":{"get_bytes":110677,"get_count":127,"put_bytes":114950,"put_count":157}} -{"i":43,"series":2,"stats":{"get_bytes":117708,"get_count":131,"put_bytes":121270,"put_count":151}} -{"i":44,"series":2,"stats":{"get_bytes":117734,"get_count":137,"put_bytes":121516,"put_count":160}} -{"i":45,"series":2,"stats":{"get_bytes":121388,"get_count":142,"put_bytes":125530,"put_count":170}} -{"i":46,"series":2,"stats":{"get_bytes":120221,"get_count":140,"put_bytes":123856,"put_count":161}} -{"i":47,"series":2,"stats":{"get_bytes":118483,"get_count":135,"put_bytes":122960,"put_count":168}} -{"i":48,"series":2,"stats":{"get_bytes":118504,"get_count":137,"put_bytes":122986,"put_count":170}} -{"i":49,"series":2,"stats":{"get_bytes":119796,"get_count":136,"put_bytes":123852,"put_count":163}} -{"i":50,"series":2,"stats":{"get_bytes":117451,"get_count":136,"put_bytes":121236,"put_count":159}} -{"i":51,"series":2,"stats":{"get_bytes":120115,"get_count":139,"put_bytes":123469,"put_count":156}} -{"i":52,"series":2,"stats":{"get_bytes":122231,"get_count":144,"put_bytes":126707,"put_count":177}} -{"i":53,"series":2,"stats":{"get_bytes":115835,"get_count":129,"put_bytes":119820,"put_count":155}} -{"i":54,"series":2,"stats":{"get_bytes":118455,"get_count":142,"put_bytes":122592,"put_count":170}} -{"i":55,"series":2,"stats":{"get_bytes":118066,"get_count":130,"put_bytes":122054,"put_count":156}} -{"i":56,"series":2,"stats":{"get_bytes":120400,"get_count":140,"put_bytes":124809,"put_count":172}} -{"i":57,"series":2,"stats":{"get_bytes":120361,"get_count":139,"put_bytes":124610,"put_count":169}} -{"i":58,"series":2,"stats":{"get_bytes":126864,"get_count":139,"put_bytes":131421,"put_count":173}} -{"i":59,"series":2,"stats":{"get_bytes":125764,"get_count":133,"put_bytes":130867,"put_count":175}} -{"i":60,"series":2,"stats":{"get_bytes":127198,"get_count":136,"put_bytes":131813,"put_count":171}} -{"i":61,"series":2,"stats":{"get_bytes":126657,"get_count":134,"put_bytes":131498,"put_count":172}} -{"i":62,"series":2,"stats":{"get_bytes":120411,"get_count":131,"put_bytes":124757,"put_count":162}} -{"i":63,"series":2,"stats":{"get_bytes":118027,"get_count":138,"put_bytes":122564,"put_count":172}} -{"i":64,"series":2,"stats":{"get_bytes":124715,"get_count":139,"put_bytes":129125,"put_count":171}} -{"i":65,"series":2,"stats":{"get_bytes":125516,"get_count":137,"put_bytes":129996,"put_count":170}} -{"i":66,"series":2,"stats":{"get_bytes":125363,"get_count":138,"put_bytes":129570,"put_count":167}} -{"i":67,"series":2,"stats":{"get_bytes":127780,"get_count":144,"put_bytes":131907,"put_count":172}} -{"i":68,"series":2,"stats":{"get_bytes":128249,"get_count":140,"put_bytes":132509,"put_count":170}} -{"i":69,"series":2,"stats":{"get_bytes":124544,"get_count":140,"put_bytes":129085,"put_count":174}} -{"i":70,"series":2,"stats":{"get_bytes":128376,"get_count":142,"put_bytes":132154,"put_count":165}} -{"i":71,"series":2,"stats":{"get_bytes":125358,"get_count":134,"put_bytes":129623,"put_count":164}} -{"i":72,"series":2,"stats":{"get_bytes":123856,"get_count":135,"put_bytes":128127,"put_count":165}} -{"i":73,"series":2,"stats":{"get_bytes":125370,"get_count":140,"put_bytes":130274,"put_count":179}} -{"i":74,"series":2,"stats":{"get_bytes":122818,"get_count":134,"put_bytes":127304,"put_count":167}} -{"i":75,"series":2,"stats":{"get_bytes":124449,"get_count":141,"put_bytes":128508,"put_count":168}} -{"i":76,"series":2,"stats":{"get_bytes":126823,"get_count":142,"put_bytes":131449,"put_count":177}} -{"i":77,"series":2,"stats":{"get_bytes":131220,"get_count":135,"put_bytes":135689,"put_count":168}} -{"i":78,"series":2,"stats":{"get_bytes":129206,"get_count":140,"put_bytes":133557,"put_count":171}} -{"i":79,"series":2,"stats":{"get_bytes":128769,"get_count":134,"put_bytes":133402,"put_count":169}} -{"i":80,"series":2,"stats":{"get_bytes":131230,"get_count":143,"put_bytes":135914,"put_count":179}} -{"i":81,"series":2,"stats":{"get_bytes":125465,"get_count":137,"put_bytes":129672,"put_count":166}} -{"i":82,"series":2,"stats":{"get_bytes":132500,"get_count":145,"put_bytes":136842,"put_count":176}} -{"i":83,"series":2,"stats":{"get_bytes":129060,"get_count":136,"put_bytes":133969,"put_count":175}} -{"i":84,"series":2,"stats":{"get_bytes":128101,"get_count":136,"put_bytes":132284,"put_count":165}} -{"i":85,"series":2,"stats":{"get_bytes":128646,"get_count":137,"put_bytes":133129,"put_count":170}} -{"i":86,"series":2,"stats":{"get_bytes":125988,"get_count":142,"put_bytes":130618,"put_count":177}} -{"i":87,"series":2,"stats":{"get_bytes":134311,"get_count":140,"put_bytes":138871,"put_count":174}} -{"i":88,"series":2,"stats":{"get_bytes":128634,"get_count":138,"put_bytes":133038,"put_count":170}} -{"i":89,"series":2,"stats":{"get_bytes":126113,"get_count":140,"put_bytes":130182,"put_count":167}} -{"i":90,"series":2,"stats":{"get_bytes":127919,"get_count":141,"put_bytes":131979,"put_count":168}} -{"i":91,"series":2,"stats":{"get_bytes":134268,"get_count":143,"put_bytes":138279,"put_count":170}} -{"i":92,"series":2,"stats":{"get_bytes":128724,"get_count":140,"put_bytes":133045,"put_count":171}} -{"i":93,"series":2,"stats":{"get_bytes":129847,"get_count":140,"put_bytes":134714,"put_count":179}} -{"i":94,"series":2,"stats":{"get_bytes":132404,"get_count":143,"put_bytes":136603,"put_count":172}} -{"i":95,"series":2,"stats":{"get_bytes":129791,"get_count":142,"put_bytes":133989,"put_count":171}} -{"i":96,"series":2,"stats":{"get_bytes":133858,"get_count":142,"put_bytes":138391,"put_count":176}} -{"i":97,"series":2,"stats":{"get_bytes":133425,"get_count":141,"put_bytes":137983,"put_count":175}} -{"i":98,"series":2,"stats":{"get_bytes":136800,"get_count":146,"put_bytes":140655,"put_count":170}} -{"i":99,"series":2,"stats":{"get_bytes":126455,"get_count":139,"put_bytes":130936,"put_count":172}} +{"i":1,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":6059,"put_count":35}} +{"i":2,"series":1,"stats":{"get_bytes":7282,"get_count":28,"put_bytes":10271,"put_count":43}} +{"i":3,"series":1,"stats":{"get_bytes":10592,"get_count":32,"put_bytes":13517,"put_count":46}} +{"i":4,"series":1,"stats":{"get_bytes":14965,"get_count":37,"put_bytes":18822,"put_count":68}} +{"i":5,"series":1,"stats":{"get_bytes":17977,"get_count":43,"put_bytes":21287,"put_count":64}} +{"i":6,"series":1,"stats":{"get_bytes":19376,"get_count":38,"put_bytes":23461,"put_count":73}} +{"i":7,"series":1,"stats":{"get_bytes":23549,"get_count":47,"put_bytes":27579,"put_count":81}} +{"i":8,"series":1,"stats":{"get_bytes":27385,"get_count":48,"put_bytes":31406,"put_count":82}} +{"i":9,"series":1,"stats":{"get_bytes":29030,"get_count":55,"put_bytes":33209,"put_count":93}} +{"i":10,"series":1,"stats":{"get_bytes":31809,"get_count":49,"put_bytes":35728,"put_count":81}} +{"i":11,"series":1,"stats":{"get_bytes":34524,"get_count":54,"put_bytes":38862,"put_count":94}} +{"i":12,"series":1,"stats":{"get_bytes":37024,"get_count":70,"put_bytes":41207,"put_count":108}} +{"i":13,"series":1,"stats":{"get_bytes":36349,"get_count":68,"put_bytes":40355,"put_count":102}} +{"i":14,"series":1,"stats":{"get_bytes":39934,"get_count":67,"put_bytes":44368,"put_count":109}} +{"i":15,"series":1,"stats":{"get_bytes":40959,"get_count":71,"put_bytes":45103,"put_count":107}} +{"i":16,"series":1,"stats":{"get_bytes":42959,"get_count":72,"put_bytes":47345,"put_count":113}} +{"i":17,"series":1,"stats":{"get_bytes":43707,"get_count":74,"put_bytes":48073,"put_count":114}} +{"i":18,"series":1,"stats":{"get_bytes":42205,"get_count":75,"put_bytes":46485,"put_count":114}} +{"i":19,"series":1,"stats":{"get_bytes":50340,"get_count":92,"put_bytes":54039,"put_count":120}} +{"i":20,"series":1,"stats":{"get_bytes":45837,"get_count":81,"put_bytes":49764,"put_count":113}} +{"i":21,"series":1,"stats":{"get_bytes":47965,"get_count":80,"put_bytes":52393,"put_count":121}} +{"i":22,"series":1,"stats":{"get_bytes":48089,"get_count":83,"put_bytes":52126,"put_count":117}} +{"i":23,"series":1,"stats":{"get_bytes":55277,"get_count":108,"put_bytes":58095,"put_count":121}} +{"i":24,"series":1,"stats":{"get_bytes":54445,"get_count":104,"put_bytes":57787,"put_count":126}} +{"i":25,"series":1,"stats":{"get_bytes":54687,"get_count":102,"put_bytes":58422,"put_count":131}} +{"i":26,"series":1,"stats":{"get_bytes":53703,"get_count":94,"put_bytes":57798,"put_count":129}} +{"i":27,"series":1,"stats":{"get_bytes":56313,"get_count":107,"put_bytes":59471,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":56493,"get_count":106,"put_bytes":60200,"put_count":134}} +{"i":29,"series":1,"stats":{"get_bytes":56898,"get_count":106,"put_bytes":60003,"put_count":123}} +{"i":30,"series":1,"stats":{"get_bytes":61064,"get_count":113,"put_bytes":64690,"put_count":140}} +{"i":31,"series":1,"stats":{"get_bytes":56141,"get_count":107,"put_bytes":59588,"put_count":131}} +{"i":32,"series":1,"stats":{"get_bytes":61820,"get_count":111,"put_bytes":65267,"put_count":135}} +{"i":33,"series":1,"stats":{"get_bytes":59943,"get_count":114,"put_bytes":63552,"put_count":141}} +{"i":34,"series":1,"stats":{"get_bytes":61479,"get_count":113,"put_bytes":64497,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":61304,"get_count":116,"put_bytes":64224,"put_count":131}} +{"i":36,"series":1,"stats":{"get_bytes":62205,"get_count":113,"put_bytes":65210,"put_count":129}} +{"i":37,"series":1,"stats":{"get_bytes":63059,"get_count":117,"put_bytes":66386,"put_count":138}} +{"i":38,"series":1,"stats":{"get_bytes":64628,"get_count":120,"put_bytes":68083,"put_count":144}} +{"i":39,"series":1,"stats":{"get_bytes":60348,"get_count":115,"put_bytes":63342,"put_count":130}} +{"i":40,"series":1,"stats":{"get_bytes":64881,"get_count":121,"put_bytes":67750,"put_count":134}} +{"i":41,"series":1,"stats":{"get_bytes":64290,"get_count":118,"put_bytes":67341,"put_count":134}} +{"i":42,"series":1,"stats":{"get_bytes":66194,"get_count":118,"put_bytes":69621,"put_count":141}} +{"i":43,"series":1,"stats":{"get_bytes":64767,"get_count":121,"put_bytes":67779,"put_count":137}} +{"i":44,"series":1,"stats":{"get_bytes":69588,"get_count":127,"put_bytes":72362,"put_count":138}} +{"i":45,"series":1,"stats":{"get_bytes":67390,"get_count":123,"put_bytes":70665,"put_count":144}} +{"i":46,"series":1,"stats":{"get_bytes":69075,"get_count":128,"put_bytes":72275,"put_count":147}} +{"i":47,"series":1,"stats":{"get_bytes":69169,"get_count":128,"put_bytes":72266,"put_count":145}} +{"i":48,"series":1,"stats":{"get_bytes":71822,"get_count":125,"put_bytes":74761,"put_count":139}} +{"i":49,"series":1,"stats":{"get_bytes":70013,"get_count":127,"put_bytes":73164,"put_count":145}} +{"i":50,"series":1,"stats":{"get_bytes":67056,"get_count":117,"put_bytes":69983,"put_count":131}} +{"i":51,"series":1,"stats":{"get_bytes":69735,"get_count":119,"put_bytes":72610,"put_count":132}} +{"i":52,"series":1,"stats":{"get_bytes":70906,"get_count":128,"put_bytes":74050,"put_count":146}} +{"i":53,"series":1,"stats":{"get_bytes":67923,"get_count":122,"put_bytes":70628,"put_count":132}} +{"i":54,"series":1,"stats":{"get_bytes":72422,"get_count":130,"put_bytes":75467,"put_count":146}} +{"i":55,"series":1,"stats":{"get_bytes":70213,"get_count":120,"put_bytes":73048,"put_count":133}} +{"i":56,"series":1,"stats":{"get_bytes":70600,"get_count":127,"put_bytes":73586,"put_count":142}} +{"i":57,"series":1,"stats":{"get_bytes":74392,"get_count":132,"put_bytes":77130,"put_count":143}} +{"i":58,"series":1,"stats":{"get_bytes":71984,"get_count":129,"put_bytes":74939,"put_count":144}} +{"i":59,"series":1,"stats":{"get_bytes":73941,"get_count":135,"put_bytes":76533,"put_count":143}} +{"i":60,"series":1,"stats":{"get_bytes":72541,"get_count":129,"put_bytes":75580,"put_count":145}} +{"i":61,"series":1,"stats":{"get_bytes":76572,"get_count":131,"put_bytes":79500,"put_count":145}} +{"i":62,"series":1,"stats":{"get_bytes":70675,"get_count":127,"put_bytes":73448,"put_count":138}} +{"i":63,"series":1,"stats":{"get_bytes":77306,"get_count":134,"put_bytes":80243,"put_count":150}} +{"i":64,"series":1,"stats":{"get_bytes":77142,"get_count":131,"put_bytes":80132,"put_count":146}} +{"i":65,"series":1,"stats":{"get_bytes":71319,"get_count":126,"put_bytes":74467,"put_count":144}} +{"i":66,"series":1,"stats":{"get_bytes":76162,"get_count":128,"put_bytes":79363,"put_count":147}} +{"i":67,"series":1,"stats":{"get_bytes":76992,"get_count":134,"put_bytes":79756,"put_count":145}} +{"i":68,"series":1,"stats":{"get_bytes":78036,"get_count":130,"put_bytes":80965,"put_count":144}} +{"i":69,"series":1,"stats":{"get_bytes":77909,"get_count":131,"put_bytes":80941,"put_count":147}} +{"i":70,"series":1,"stats":{"get_bytes":79204,"get_count":131,"put_bytes":82341,"put_count":149}} +{"i":71,"series":1,"stats":{"get_bytes":76227,"get_count":126,"put_bytes":79429,"put_count":145}} +{"i":72,"series":1,"stats":{"get_bytes":76114,"get_count":131,"put_bytes":79148,"put_count":147}} +{"i":73,"series":1,"stats":{"get_bytes":77667,"get_count":132,"put_bytes":80367,"put_count":142}} +{"i":74,"series":1,"stats":{"get_bytes":74918,"get_count":127,"put_bytes":78011,"put_count":144}} +{"i":75,"series":1,"stats":{"get_bytes":81086,"get_count":134,"put_bytes":84235,"put_count":152}} +{"i":76,"series":1,"stats":{"get_bytes":79892,"get_count":131,"put_bytes":82929,"put_count":147}} +{"i":77,"series":1,"stats":{"get_bytes":77938,"get_count":132,"put_bytes":80533,"put_count":140}} +{"i":78,"series":1,"stats":{"get_bytes":79481,"get_count":130,"put_bytes":82349,"put_count":143}} +{"i":79,"series":1,"stats":{"get_bytes":79364,"get_count":132,"put_bytes":82785,"put_count":155}} +{"i":80,"series":1,"stats":{"get_bytes":80127,"get_count":133,"put_bytes":83269,"put_count":151}} +{"i":81,"series":1,"stats":{"get_bytes":82896,"get_count":138,"put_bytes":86039,"put_count":156}} +{"i":82,"series":1,"stats":{"get_bytes":79516,"get_count":127,"put_bytes":82994,"put_count":151}} +{"i":83,"series":1,"stats":{"get_bytes":77208,"get_count":123,"put_bytes":80133,"put_count":137}} +{"i":84,"series":1,"stats":{"get_bytes":85058,"get_count":134,"put_bytes":88247,"put_count":153}} +{"i":85,"series":1,"stats":{"get_bytes":81271,"get_count":129,"put_bytes":84138,"put_count":142}} +{"i":86,"series":1,"stats":{"get_bytes":83145,"get_count":131,"put_bytes":86073,"put_count":145}} +{"i":87,"series":1,"stats":{"get_bytes":81449,"get_count":129,"put_bytes":84594,"put_count":147}} +{"i":88,"series":1,"stats":{"get_bytes":82196,"get_count":131,"put_bytes":85665,"put_count":155}} +{"i":89,"series":1,"stats":{"get_bytes":85098,"get_count":133,"put_bytes":88621,"put_count":158}} +{"i":90,"series":1,"stats":{"get_bytes":87905,"get_count":135,"put_bytes":91270,"put_count":157}} +{"i":91,"series":1,"stats":{"get_bytes":82797,"get_count":135,"put_bytes":85996,"put_count":154}} +{"i":92,"series":1,"stats":{"get_bytes":87779,"get_count":132,"put_bytes":91254,"put_count":156}} +{"i":93,"series":1,"stats":{"get_bytes":83264,"get_count":130,"put_bytes":87177,"put_count":162}} +{"i":94,"series":1,"stats":{"get_bytes":85490,"get_count":130,"put_bytes":88736,"put_count":150}} +{"i":95,"series":1,"stats":{"get_bytes":85700,"get_count":132,"put_bytes":89180,"put_count":156}} +{"i":96,"series":1,"stats":{"get_bytes":83630,"get_count":134,"put_bytes":86498,"put_count":147}} +{"i":97,"series":1,"stats":{"get_bytes":84325,"get_count":135,"put_bytes":87697,"put_count":157}} +{"i":98,"series":1,"stats":{"get_bytes":83690,"get_count":133,"put_bytes":86883,"put_count":152}} +{"i":99,"series":1,"stats":{"get_bytes":86498,"get_count":135,"put_bytes":89589,"put_count":152}} +{"i":0,"series":2,"stats":{"get_bytes":88973,"get_count":140,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":86991,"get_count":131,"put_bytes":90297,"put_count":152}} +{"i":2,"series":2,"stats":{"get_bytes":86667,"get_count":132,"put_bytes":90249,"put_count":158}} +{"i":3,"series":2,"stats":{"get_bytes":88119,"get_count":133,"put_bytes":91763,"put_count":160}} +{"i":4,"series":2,"stats":{"get_bytes":83224,"get_count":130,"put_bytes":86748,"put_count":155}} +{"i":5,"series":2,"stats":{"get_bytes":88113,"get_count":131,"put_bytes":91754,"put_count":158}} +{"i":6,"series":2,"stats":{"get_bytes":90632,"get_count":133,"put_bytes":94167,"put_count":158}} +{"i":7,"series":2,"stats":{"get_bytes":90522,"get_count":134,"put_bytes":94169,"put_count":161}} +{"i":8,"series":2,"stats":{"get_bytes":92697,"get_count":138,"put_bytes":96226,"put_count":163}} +{"i":9,"series":2,"stats":{"get_bytes":91637,"get_count":134,"put_bytes":95058,"put_count":157}} +{"i":10,"series":2,"stats":{"get_bytes":88288,"get_count":131,"put_bytes":91708,"put_count":154}} +{"i":11,"series":2,"stats":{"get_bytes":91071,"get_count":137,"put_bytes":94324,"put_count":157}} +{"i":12,"series":2,"stats":{"get_bytes":88847,"get_count":130,"put_bytes":92307,"put_count":154}} +{"i":13,"series":2,"stats":{"get_bytes":88802,"get_count":132,"put_bytes":92279,"put_count":156}} +{"i":14,"series":2,"stats":{"get_bytes":92333,"get_count":135,"put_bytes":95957,"put_count":162}} +{"i":15,"series":2,"stats":{"get_bytes":82488,"get_count":127,"put_bytes":85574,"put_count":144}} +{"i":16,"series":2,"stats":{"get_bytes":92260,"get_count":134,"put_bytes":95630,"put_count":156}} +{"i":17,"series":2,"stats":{"get_bytes":93132,"get_count":136,"put_bytes":96383,"put_count":156}} +{"i":18,"series":2,"stats":{"get_bytes":93814,"get_count":135,"put_bytes":97513,"put_count":163}} +{"i":19,"series":2,"stats":{"get_bytes":90552,"get_count":129,"put_bytes":93590,"put_count":145}} +{"i":20,"series":2,"stats":{"get_bytes":91607,"get_count":132,"put_bytes":95079,"put_count":156}} +{"i":21,"series":2,"stats":{"get_bytes":97157,"get_count":139,"put_bytes":100745,"put_count":165}} +{"i":22,"series":2,"stats":{"get_bytes":93937,"get_count":135,"put_bytes":97466,"put_count":160}} +{"i":23,"series":2,"stats":{"get_bytes":92035,"get_count":131,"put_bytes":95401,"put_count":153}} +{"i":24,"series":2,"stats":{"get_bytes":93463,"get_count":131,"put_bytes":97039,"put_count":157}} +{"i":25,"series":2,"stats":{"get_bytes":94600,"get_count":129,"put_bytes":98040,"put_count":153}} +{"i":26,"series":2,"stats":{"get_bytes":96794,"get_count":139,"put_bytes":99999,"put_count":158}} +{"i":27,"series":2,"stats":{"get_bytes":97667,"get_count":138,"put_bytes":101048,"put_count":161}} +{"i":28,"series":2,"stats":{"get_bytes":96496,"get_count":135,"put_bytes":100291,"put_count":165}} +{"i":29,"series":2,"stats":{"get_bytes":90707,"get_count":133,"put_bytes":93913,"put_count":152}} +{"i":30,"series":2,"stats":{"get_bytes":94478,"get_count":136,"put_bytes":98173,"put_count":164}} +{"i":31,"series":2,"stats":{"get_bytes":97256,"get_count":137,"put_bytes":101003,"put_count":166}} +{"i":32,"series":2,"stats":{"get_bytes":97930,"get_count":139,"put_bytes":101899,"put_count":172}} +{"i":33,"series":2,"stats":{"get_bytes":92900,"get_count":131,"put_bytes":96677,"put_count":161}} +{"i":34,"series":2,"stats":{"get_bytes":97465,"get_count":136,"put_bytes":101043,"put_count":162}} +{"i":35,"series":2,"stats":{"get_bytes":98818,"get_count":133,"put_bytes":102423,"put_count":160}} +{"i":36,"series":2,"stats":{"get_bytes":98278,"get_count":134,"put_bytes":102415,"put_count":170}} +{"i":37,"series":2,"stats":{"get_bytes":97044,"get_count":132,"put_bytes":100576,"put_count":157}} +{"i":38,"series":2,"stats":{"get_bytes":94969,"get_count":131,"put_bytes":99060,"put_count":166}} +{"i":39,"series":2,"stats":{"get_bytes":93266,"get_count":131,"put_bytes":96512,"put_count":151}} +{"i":40,"series":2,"stats":{"get_bytes":93837,"get_count":131,"put_bytes":97471,"put_count":158}} +{"i":41,"series":2,"stats":{"get_bytes":98112,"get_count":143,"put_bytes":101585,"put_count":167}} +{"i":42,"series":2,"stats":{"get_bytes":93678,"get_count":127,"put_bytes":97484,"put_count":157}} +{"i":43,"series":2,"stats":{"get_bytes":98980,"get_count":131,"put_bytes":102225,"put_count":151}} +{"i":44,"series":2,"stats":{"get_bytes":99369,"get_count":137,"put_bytes":102791,"put_count":160}} +{"i":45,"series":2,"stats":{"get_bytes":102316,"get_count":142,"put_bytes":106022,"put_count":170}} +{"i":46,"series":2,"stats":{"get_bytes":101498,"get_count":140,"put_bytes":104803,"put_count":161}} +{"i":47,"series":2,"stats":{"get_bytes":99667,"get_count":135,"put_bytes":103634,"put_count":168}} +{"i":48,"series":2,"stats":{"get_bytes":100277,"get_count":137,"put_bytes":104247,"put_count":170}} +{"i":49,"series":2,"stats":{"get_bytes":101446,"get_count":136,"put_bytes":105081,"put_count":163}} +{"i":50,"series":2,"stats":{"get_bytes":99251,"get_count":136,"put_bytes":102673,"put_count":159}} +{"i":51,"series":2,"stats":{"get_bytes":101481,"get_count":139,"put_bytes":104565,"put_count":156}} +{"i":52,"series":2,"stats":{"get_bytes":103598,"get_count":144,"put_bytes":107563,"put_count":177}} +{"i":53,"series":2,"stats":{"get_bytes":97680,"get_count":129,"put_bytes":101260,"put_count":155}} +{"i":54,"series":2,"stats":{"get_bytes":100734,"get_count":142,"put_bytes":104434,"put_count":170}} +{"i":55,"series":2,"stats":{"get_bytes":99908,"get_count":130,"put_bytes":103490,"put_count":156}} +{"i":56,"series":2,"stats":{"get_bytes":101669,"get_count":140,"put_bytes":105581,"put_count":172}} +{"i":57,"series":2,"stats":{"get_bytes":101452,"get_count":139,"put_bytes":105222,"put_count":169}} +{"i":58,"series":2,"stats":{"get_bytes":107314,"get_count":139,"put_bytes":111346,"put_count":173}} +{"i":59,"series":2,"stats":{"get_bytes":106208,"get_count":133,"put_bytes":110666,"put_count":175}} +{"i":60,"series":2,"stats":{"get_bytes":107512,"get_count":136,"put_bytes":111587,"put_count":171}} +{"i":61,"series":2,"stats":{"get_bytes":106970,"get_count":134,"put_bytes":111225,"put_count":172}} +{"i":62,"series":2,"stats":{"get_bytes":101583,"get_count":131,"put_bytes":105447,"put_count":162}} +{"i":63,"series":2,"stats":{"get_bytes":99593,"get_count":138,"put_bytes":103591,"put_count":172}} +{"i":64,"series":2,"stats":{"get_bytes":105299,"get_count":139,"put_bytes":109214,"put_count":171}} +{"i":65,"series":2,"stats":{"get_bytes":106401,"get_count":137,"put_bytes":110370,"put_count":170}} +{"i":66,"series":2,"stats":{"get_bytes":105792,"get_count":138,"put_bytes":109548,"put_count":167}} +{"i":67,"series":2,"stats":{"get_bytes":107865,"get_count":144,"put_bytes":111557,"put_count":172}} +{"i":68,"series":2,"stats":{"get_bytes":108228,"get_count":140,"put_bytes":112023,"put_count":170}} +{"i":69,"series":2,"stats":{"get_bytes":105412,"get_count":140,"put_bytes":109412,"put_count":174}} +{"i":70,"series":2,"stats":{"get_bytes":108242,"get_count":142,"put_bytes":111660,"put_count":165}} +{"i":71,"series":2,"stats":{"get_bytes":105940,"get_count":134,"put_bytes":109740,"put_count":164}} +{"i":72,"series":2,"stats":{"get_bytes":104966,"get_count":135,"put_bytes":108772,"put_count":165}} +{"i":73,"series":2,"stats":{"get_bytes":105967,"get_count":140,"put_bytes":110270,"put_count":179}} +{"i":74,"series":2,"stats":{"get_bytes":103449,"get_count":134,"put_bytes":107424,"put_count":167}} +{"i":75,"series":2,"stats":{"get_bytes":105512,"get_count":141,"put_bytes":109151,"put_count":168}} +{"i":76,"series":2,"stats":{"get_bytes":107464,"get_count":142,"put_bytes":111549,"put_count":177}} +{"i":77,"series":2,"stats":{"get_bytes":110884,"get_count":135,"put_bytes":114843,"put_count":168}} +{"i":78,"series":2,"stats":{"get_bytes":108559,"get_count":140,"put_bytes":112429,"put_count":171}} +{"i":79,"series":2,"stats":{"get_bytes":108811,"get_count":134,"put_bytes":112903,"put_count":169}} +{"i":80,"series":2,"stats":{"get_bytes":110954,"get_count":143,"put_bytes":115082,"put_count":179}} +{"i":81,"series":2,"stats":{"get_bytes":106133,"get_count":137,"put_bytes":109889,"put_count":166}} +{"i":82,"series":2,"stats":{"get_bytes":112044,"get_count":145,"put_bytes":115906,"put_count":176}} +{"i":83,"series":2,"stats":{"get_bytes":109102,"get_count":136,"put_bytes":113411,"put_count":175}} +{"i":84,"series":2,"stats":{"get_bytes":107996,"get_count":136,"put_bytes":111714,"put_count":165}} +{"i":85,"series":2,"stats":{"get_bytes":108645,"get_count":137,"put_bytes":112617,"put_count":170}} +{"i":86,"series":2,"stats":{"get_bytes":106875,"get_count":142,"put_bytes":110964,"put_count":177}} +{"i":87,"series":2,"stats":{"get_bytes":113455,"get_count":140,"put_bytes":117489,"put_count":174}} +{"i":88,"series":2,"stats":{"get_bytes":109122,"get_count":138,"put_bytes":113031,"put_count":170}} +{"i":89,"series":2,"stats":{"get_bytes":106679,"get_count":140,"put_bytes":110326,"put_count":167}} +{"i":90,"series":2,"stats":{"get_bytes":108678,"get_count":141,"put_bytes":112317,"put_count":168}} +{"i":91,"series":2,"stats":{"get_bytes":113650,"get_count":143,"put_bytes":117213,"put_count":170}} +{"i":92,"series":2,"stats":{"get_bytes":109183,"get_count":140,"put_bytes":113010,"put_count":171}} +{"i":93,"series":2,"stats":{"get_bytes":109740,"get_count":140,"put_bytes":113978,"put_count":179}} +{"i":94,"series":2,"stats":{"get_bytes":111827,"get_count":143,"put_bytes":115576,"put_count":172}} +{"i":95,"series":2,"stats":{"get_bytes":109814,"get_count":142,"put_bytes":113561,"put_count":171}} +{"i":96,"series":2,"stats":{"get_bytes":113342,"get_count":142,"put_bytes":117333,"put_count":176}} +{"i":97,"series":2,"stats":{"get_bytes":113106,"get_count":141,"put_bytes":117137,"put_count":175}} +{"i":98,"series":2,"stats":{"get_bytes":115775,"get_count":146,"put_bytes":119253,"put_count":170}} +{"i":99,"series":2,"stats":{"get_bytes":107141,"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 index 8e61e90e3097ba6611654c4456751dee91bbe569..3eb3a947901f298249ebaaa8803a95449f23ec59 100644 GIT binary patch literal 22527 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 literal 22556 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 L+2Jz%QxX3GIMXN6 diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index 6888c5b7d..a0ad60348 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":13283,"get_count":20,"put_bytes":11275,"put_count":19}} -{"i":1,"series":1,"stats":{"get_bytes":14201,"get_count":15,"put_bytes":12193,"put_count":14}} -{"i":2,"series":1,"stats":{"get_bytes":12506,"get_count":14,"put_bytes":10498,"put_count":13}} -{"i":3,"series":1,"stats":{"get_bytes":14090,"get_count":20,"put_bytes":12082,"put_count":19}} -{"i":4,"series":1,"stats":{"get_bytes":13426,"get_count":18,"put_bytes":11418,"put_count":17}} -{"i":5,"series":1,"stats":{"get_bytes":14254,"get_count":16,"put_bytes":12246,"put_count":15}} -{"i":6,"series":1,"stats":{"get_bytes":14462,"get_count":20,"put_bytes":12454,"put_count":19}} -{"i":7,"series":1,"stats":{"get_bytes":14120,"get_count":17,"put_bytes":12112,"put_count":16}} -{"i":8,"series":1,"stats":{"get_bytes":14409,"get_count":21,"put_bytes":12401,"put_count":20}} -{"i":9,"series":1,"stats":{"get_bytes":13804,"get_count":16,"put_bytes":11796,"put_count":15}} -{"i":10,"series":1,"stats":{"get_bytes":15299,"get_count":20,"put_bytes":13291,"put_count":19}} -{"i":11,"series":1,"stats":{"get_bytes":12750,"get_count":14,"put_bytes":10742,"put_count":13}} -{"i":12,"series":1,"stats":{"get_bytes":13242,"get_count":18,"put_bytes":11234,"put_count":17}} -{"i":13,"series":1,"stats":{"get_bytes":14791,"get_count":18,"put_bytes":12783,"put_count":17}} -{"i":14,"series":1,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":12070,"put_count":17}} -{"i":15,"series":1,"stats":{"get_bytes":13348,"get_count":17,"put_bytes":11340,"put_count":16}} -{"i":16,"series":1,"stats":{"get_bytes":11437,"get_count":13,"put_bytes":9429,"put_count":12}} -{"i":17,"series":1,"stats":{"get_bytes":12302,"get_count":17,"put_bytes":10294,"put_count":16}} -{"i":18,"series":1,"stats":{"get_bytes":15056,"get_count":18,"put_bytes":13048,"put_count":17}} -{"i":19,"series":1,"stats":{"get_bytes":13996,"get_count":19,"put_bytes":11988,"put_count":18}} -{"i":20,"series":1,"stats":{"get_bytes":14615,"get_count":20,"put_bytes":12607,"put_count":19}} -{"i":21,"series":1,"stats":{"get_bytes":13083,"get_count":17,"put_bytes":11075,"put_count":16}} -{"i":22,"series":1,"stats":{"get_bytes":15207,"get_count":19,"put_bytes":13199,"put_count":18}} -{"i":23,"series":1,"stats":{"get_bytes":12419,"get_count":19,"put_bytes":10411,"put_count":18}} -{"i":24,"series":1,"stats":{"get_bytes":14199,"get_count":18,"put_bytes":12191,"put_count":17}} -{"i":25,"series":1,"stats":{"get_bytes":14462,"get_count":18,"put_bytes":12454,"put_count":17}} -{"i":26,"series":1,"stats":{"get_bytes":13573,"get_count":18,"put_bytes":11565,"put_count":17}} -{"i":27,"series":1,"stats":{"get_bytes":13002,"get_count":14,"put_bytes":10994,"put_count":13}} -{"i":28,"series":1,"stats":{"get_bytes":14777,"get_count":19,"put_bytes":12769,"put_count":18}} -{"i":29,"series":1,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":12432,"put_count":17}} -{"i":30,"series":1,"stats":{"get_bytes":15342,"get_count":21,"put_bytes":13334,"put_count":20}} -{"i":31,"series":1,"stats":{"get_bytes":13320,"get_count":16,"put_bytes":11312,"put_count":15}} -{"i":32,"series":1,"stats":{"get_bytes":14639,"get_count":19,"put_bytes":12631,"put_count":18}} -{"i":33,"series":1,"stats":{"get_bytes":14004,"get_count":19,"put_bytes":11996,"put_count":18}} -{"i":34,"series":1,"stats":{"get_bytes":13484,"get_count":15,"put_bytes":11476,"put_count":14}} -{"i":35,"series":1,"stats":{"get_bytes":14110,"get_count":19,"put_bytes":12102,"put_count":18}} -{"i":36,"series":1,"stats":{"get_bytes":15145,"get_count":19,"put_bytes":13137,"put_count":18}} -{"i":37,"series":1,"stats":{"get_bytes":12984,"get_count":16,"put_bytes":10976,"put_count":15}} -{"i":38,"series":1,"stats":{"get_bytes":12322,"get_count":16,"put_bytes":10314,"put_count":15}} -{"i":39,"series":1,"stats":{"get_bytes":14180,"get_count":18,"put_bytes":12172,"put_count":17}} -{"i":40,"series":1,"stats":{"get_bytes":13933,"get_count":22,"put_bytes":11925,"put_count":21}} -{"i":41,"series":1,"stats":{"get_bytes":15744,"get_count":20,"put_bytes":13736,"put_count":19}} -{"i":42,"series":1,"stats":{"get_bytes":14424,"get_count":20,"put_bytes":12416,"put_count":19}} -{"i":43,"series":1,"stats":{"get_bytes":14661,"get_count":17,"put_bytes":12653,"put_count":16}} -{"i":44,"series":1,"stats":{"get_bytes":15488,"get_count":21,"put_bytes":13480,"put_count":20}} -{"i":45,"series":1,"stats":{"get_bytes":12951,"get_count":17,"put_bytes":10943,"put_count":16}} -{"i":46,"series":1,"stats":{"get_bytes":14271,"get_count":18,"put_bytes":12263,"put_count":17}} -{"i":47,"series":1,"stats":{"get_bytes":15205,"get_count":19,"put_bytes":13197,"put_count":18}} -{"i":48,"series":1,"stats":{"get_bytes":14470,"get_count":20,"put_bytes":12462,"put_count":19}} -{"i":49,"series":1,"stats":{"get_bytes":13954,"get_count":20,"put_bytes":11946,"put_count":19}} -{"i":50,"series":1,"stats":{"get_bytes":14983,"get_count":19,"put_bytes":12975,"put_count":18}} -{"i":51,"series":1,"stats":{"get_bytes":12830,"get_count":15,"put_bytes":10822,"put_count":14}} -{"i":52,"series":1,"stats":{"get_bytes":13105,"get_count":17,"put_bytes":11097,"put_count":16}} -{"i":53,"series":1,"stats":{"get_bytes":14482,"get_count":18,"put_bytes":12474,"put_count":17}} -{"i":54,"series":1,"stats":{"get_bytes":14737,"get_count":22,"put_bytes":12729,"put_count":21}} -{"i":55,"series":1,"stats":{"get_bytes":12328,"get_count":17,"put_bytes":10320,"put_count":16}} -{"i":56,"series":1,"stats":{"get_bytes":13373,"get_count":17,"put_bytes":11365,"put_count":16}} -{"i":57,"series":1,"stats":{"get_bytes":15067,"get_count":17,"put_bytes":13059,"put_count":16}} -{"i":58,"series":1,"stats":{"get_bytes":15369,"get_count":21,"put_bytes":13361,"put_count":20}} -{"i":59,"series":1,"stats":{"get_bytes":14499,"get_count":16,"put_bytes":12491,"put_count":15}} -{"i":60,"series":1,"stats":{"get_bytes":14475,"get_count":18,"put_bytes":12467,"put_count":17}} -{"i":61,"series":1,"stats":{"get_bytes":15923,"get_count":23,"put_bytes":13915,"put_count":22}} -{"i":62,"series":1,"stats":{"get_bytes":14176,"get_count":18,"put_bytes":12168,"put_count":17}} -{"i":63,"series":1,"stats":{"get_bytes":15246,"get_count":19,"put_bytes":13238,"put_count":18}} -{"i":64,"series":1,"stats":{"get_bytes":13564,"get_count":19,"put_bytes":11556,"put_count":18}} -{"i":65,"series":1,"stats":{"get_bytes":13887,"get_count":19,"put_bytes":11879,"put_count":18}} -{"i":66,"series":1,"stats":{"get_bytes":12479,"get_count":16,"put_bytes":10471,"put_count":15}} -{"i":67,"series":1,"stats":{"get_bytes":14411,"get_count":20,"put_bytes":12403,"put_count":19}} -{"i":68,"series":1,"stats":{"get_bytes":13992,"get_count":17,"put_bytes":11984,"put_count":16}} -{"i":69,"series":1,"stats":{"get_bytes":12386,"get_count":17,"put_bytes":10378,"put_count":16}} -{"i":70,"series":1,"stats":{"get_bytes":15388,"get_count":18,"put_bytes":13380,"put_count":17}} -{"i":71,"series":1,"stats":{"get_bytes":11661,"get_count":16,"put_bytes":9653,"put_count":15}} -{"i":72,"series":1,"stats":{"get_bytes":13808,"get_count":15,"put_bytes":11800,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":14338,"get_count":19,"put_bytes":12330,"put_count":18}} -{"i":74,"series":1,"stats":{"get_bytes":14602,"get_count":20,"put_bytes":12594,"put_count":19}} -{"i":75,"series":1,"stats":{"get_bytes":12940,"get_count":18,"put_bytes":10932,"put_count":17}} -{"i":76,"series":1,"stats":{"get_bytes":14345,"get_count":19,"put_bytes":12337,"put_count":18}} -{"i":77,"series":1,"stats":{"get_bytes":15069,"get_count":18,"put_bytes":13061,"put_count":17}} -{"i":78,"series":1,"stats":{"get_bytes":14205,"get_count":18,"put_bytes":12197,"put_count":17}} -{"i":79,"series":1,"stats":{"get_bytes":14482,"get_count":21,"put_bytes":12474,"put_count":20}} -{"i":80,"series":1,"stats":{"get_bytes":13717,"get_count":18,"put_bytes":11709,"put_count":17}} -{"i":81,"series":1,"stats":{"get_bytes":13135,"get_count":18,"put_bytes":11127,"put_count":17}} -{"i":82,"series":1,"stats":{"get_bytes":13060,"get_count":19,"put_bytes":11052,"put_count":18}} -{"i":83,"series":1,"stats":{"get_bytes":15534,"get_count":17,"put_bytes":13526,"put_count":16}} -{"i":84,"series":1,"stats":{"get_bytes":14085,"get_count":18,"put_bytes":12077,"put_count":17}} -{"i":85,"series":1,"stats":{"get_bytes":13638,"get_count":17,"put_bytes":11630,"put_count":16}} -{"i":86,"series":1,"stats":{"get_bytes":13995,"get_count":18,"put_bytes":11987,"put_count":17}} -{"i":87,"series":1,"stats":{"get_bytes":13706,"get_count":16,"put_bytes":11698,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":15154,"get_count":20,"put_bytes":13146,"put_count":19}} -{"i":89,"series":1,"stats":{"get_bytes":15352,"get_count":18,"put_bytes":13344,"put_count":17}} -{"i":90,"series":1,"stats":{"get_bytes":14675,"get_count":19,"put_bytes":12667,"put_count":18}} -{"i":91,"series":1,"stats":{"get_bytes":14657,"get_count":18,"put_bytes":12649,"put_count":17}} -{"i":92,"series":1,"stats":{"get_bytes":12941,"get_count":16,"put_bytes":10933,"put_count":15}} -{"i":93,"series":1,"stats":{"get_bytes":14453,"get_count":18,"put_bytes":12445,"put_count":17}} -{"i":94,"series":1,"stats":{"get_bytes":13138,"get_count":18,"put_bytes":11130,"put_count":17}} -{"i":95,"series":1,"stats":{"get_bytes":12882,"get_count":17,"put_bytes":10874,"put_count":16}} -{"i":96,"series":1,"stats":{"get_bytes":12921,"get_count":19,"put_bytes":10913,"put_count":18}} -{"i":97,"series":1,"stats":{"get_bytes":13177,"get_count":16,"put_bytes":11169,"put_count":15}} -{"i":98,"series":1,"stats":{"get_bytes":14239,"get_count":20,"put_bytes":12231,"put_count":19}} -{"i":99,"series":1,"stats":{"get_bytes":14133,"get_count":21,"put_bytes":12125,"put_count":20}} -{"i":0,"series":2,"stats":{"get_bytes":13283,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":14201,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":2,"series":2,"stats":{"get_bytes":12506,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":3,"series":2,"stats":{"get_bytes":14090,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":4,"series":2,"stats":{"get_bytes":13426,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":5,"series":2,"stats":{"get_bytes":14254,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":6,"series":2,"stats":{"get_bytes":14462,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":7,"series":2,"stats":{"get_bytes":14120,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":8,"series":2,"stats":{"get_bytes":14409,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":9,"series":2,"stats":{"get_bytes":13804,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":10,"series":2,"stats":{"get_bytes":15299,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":11,"series":2,"stats":{"get_bytes":12750,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":2,"stats":{"get_bytes":13242,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":13,"series":2,"stats":{"get_bytes":14791,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":14,"series":2,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":15,"series":2,"stats":{"get_bytes":13348,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":16,"series":2,"stats":{"get_bytes":11437,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":17,"series":2,"stats":{"get_bytes":12302,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":18,"series":2,"stats":{"get_bytes":15056,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":19,"series":2,"stats":{"get_bytes":13996,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":20,"series":2,"stats":{"get_bytes":14615,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":21,"series":2,"stats":{"get_bytes":13083,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":22,"series":2,"stats":{"get_bytes":15207,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":23,"series":2,"stats":{"get_bytes":12419,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":24,"series":2,"stats":{"get_bytes":14199,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":25,"series":2,"stats":{"get_bytes":14462,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":26,"series":2,"stats":{"get_bytes":13573,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":27,"series":2,"stats":{"get_bytes":13002,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":28,"series":2,"stats":{"get_bytes":14777,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":29,"series":2,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":30,"series":2,"stats":{"get_bytes":15342,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":31,"series":2,"stats":{"get_bytes":13320,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":32,"series":2,"stats":{"get_bytes":14639,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":33,"series":2,"stats":{"get_bytes":14004,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":34,"series":2,"stats":{"get_bytes":13484,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":35,"series":2,"stats":{"get_bytes":14110,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":36,"series":2,"stats":{"get_bytes":15145,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":37,"series":2,"stats":{"get_bytes":12984,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":38,"series":2,"stats":{"get_bytes":12322,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":39,"series":2,"stats":{"get_bytes":14180,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":40,"series":2,"stats":{"get_bytes":13933,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":41,"series":2,"stats":{"get_bytes":15744,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":42,"series":2,"stats":{"get_bytes":14424,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":43,"series":2,"stats":{"get_bytes":14661,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":44,"series":2,"stats":{"get_bytes":15488,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":45,"series":2,"stats":{"get_bytes":12951,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":46,"series":2,"stats":{"get_bytes":14271,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":47,"series":2,"stats":{"get_bytes":15205,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":48,"series":2,"stats":{"get_bytes":14470,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":49,"series":2,"stats":{"get_bytes":13954,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":50,"series":2,"stats":{"get_bytes":14983,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":51,"series":2,"stats":{"get_bytes":12830,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":52,"series":2,"stats":{"get_bytes":13105,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":53,"series":2,"stats":{"get_bytes":14482,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":54,"series":2,"stats":{"get_bytes":14737,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":55,"series":2,"stats":{"get_bytes":12328,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":56,"series":2,"stats":{"get_bytes":13373,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":57,"series":2,"stats":{"get_bytes":15067,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":58,"series":2,"stats":{"get_bytes":15369,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":59,"series":2,"stats":{"get_bytes":14499,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":60,"series":2,"stats":{"get_bytes":14475,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":61,"series":2,"stats":{"get_bytes":15923,"get_count":23,"put_bytes":0,"put_count":0}} -{"i":62,"series":2,"stats":{"get_bytes":14176,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":63,"series":2,"stats":{"get_bytes":15246,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":64,"series":2,"stats":{"get_bytes":13564,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":65,"series":2,"stats":{"get_bytes":13887,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":66,"series":2,"stats":{"get_bytes":12479,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":67,"series":2,"stats":{"get_bytes":14411,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":68,"series":2,"stats":{"get_bytes":13992,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":69,"series":2,"stats":{"get_bytes":12386,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":70,"series":2,"stats":{"get_bytes":15388,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":71,"series":2,"stats":{"get_bytes":11661,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":72,"series":2,"stats":{"get_bytes":13808,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":73,"series":2,"stats":{"get_bytes":14338,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":74,"series":2,"stats":{"get_bytes":14602,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":75,"series":2,"stats":{"get_bytes":12940,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":76,"series":2,"stats":{"get_bytes":14345,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":77,"series":2,"stats":{"get_bytes":15069,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":78,"series":2,"stats":{"get_bytes":14205,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":79,"series":2,"stats":{"get_bytes":14482,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":80,"series":2,"stats":{"get_bytes":13717,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":81,"series":2,"stats":{"get_bytes":13135,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":82,"series":2,"stats":{"get_bytes":13060,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":83,"series":2,"stats":{"get_bytes":15534,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":84,"series":2,"stats":{"get_bytes":14085,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":85,"series":2,"stats":{"get_bytes":13638,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":86,"series":2,"stats":{"get_bytes":13995,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":87,"series":2,"stats":{"get_bytes":13706,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":88,"series":2,"stats":{"get_bytes":15154,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":89,"series":2,"stats":{"get_bytes":15352,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":90,"series":2,"stats":{"get_bytes":14675,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":91,"series":2,"stats":{"get_bytes":14657,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":92,"series":2,"stats":{"get_bytes":12941,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":93,"series":2,"stats":{"get_bytes":14453,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":94,"series":2,"stats":{"get_bytes":13138,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":95,"series":2,"stats":{"get_bytes":12882,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":96,"series":2,"stats":{"get_bytes":12921,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":97,"series":2,"stats":{"get_bytes":13177,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":98,"series":2,"stats":{"get_bytes":14239,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":99,"series":2,"stats":{"get_bytes":14133,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":11811,"get_count":20,"put_bytes":9803,"put_count":19}} +{"i":1,"series":1,"stats":{"get_bytes":12430,"get_count":15,"put_bytes":10422,"put_count":14}} +{"i":2,"series":1,"stats":{"get_bytes":11036,"get_count":14,"put_bytes":9028,"put_count":13}} +{"i":3,"series":1,"stats":{"get_bytes":12469,"get_count":20,"put_bytes":10461,"put_count":19}} +{"i":4,"series":1,"stats":{"get_bytes":11880,"get_count":18,"put_bytes":9872,"put_count":17}} +{"i":5,"series":1,"stats":{"get_bytes":12694,"get_count":16,"put_bytes":10686,"put_count":15}} +{"i":6,"series":1,"stats":{"get_bytes":12810,"get_count":20,"put_bytes":10802,"put_count":19}} +{"i":7,"series":1,"stats":{"get_bytes":12544,"get_count":17,"put_bytes":10536,"put_count":16}} +{"i":8,"series":1,"stats":{"get_bytes":12713,"get_count":21,"put_bytes":10705,"put_count":20}} +{"i":9,"series":1,"stats":{"get_bytes":12243,"get_count":16,"put_bytes":10235,"put_count":15}} +{"i":10,"series":1,"stats":{"get_bytes":13556,"get_count":20,"put_bytes":11548,"put_count":19}} +{"i":11,"series":1,"stats":{"get_bytes":11308,"get_count":14,"put_bytes":9300,"put_count":13}} +{"i":12,"series":1,"stats":{"get_bytes":11710,"get_count":18,"put_bytes":9702,"put_count":17}} +{"i":13,"series":1,"stats":{"get_bytes":13214,"get_count":18,"put_bytes":11206,"put_count":17}} +{"i":14,"series":1,"stats":{"get_bytes":12533,"get_count":18,"put_bytes":10525,"put_count":17}} +{"i":15,"series":1,"stats":{"get_bytes":11848,"get_count":17,"put_bytes":9840,"put_count":16}} +{"i":16,"series":1,"stats":{"get_bytes":10177,"get_count":13,"put_bytes":8169,"put_count":12}} +{"i":17,"series":1,"stats":{"get_bytes":10922,"get_count":17,"put_bytes":8914,"put_count":16}} +{"i":18,"series":1,"stats":{"get_bytes":13253,"get_count":18,"put_bytes":11245,"put_count":17}} +{"i":19,"series":1,"stats":{"get_bytes":12511,"get_count":19,"put_bytes":10503,"put_count":18}} +{"i":20,"series":1,"stats":{"get_bytes":12889,"get_count":20,"put_bytes":10881,"put_count":19}} +{"i":21,"series":1,"stats":{"get_bytes":11597,"get_count":17,"put_bytes":9589,"put_count":16}} +{"i":22,"series":1,"stats":{"get_bytes":13572,"get_count":19,"put_bytes":11564,"put_count":18}} +{"i":23,"series":1,"stats":{"get_bytes":11053,"get_count":19,"put_bytes":9045,"put_count":18}} +{"i":24,"series":1,"stats":{"get_bytes":12683,"get_count":18,"put_bytes":10675,"put_count":17}} +{"i":25,"series":1,"stats":{"get_bytes":12827,"get_count":18,"put_bytes":10819,"put_count":17}} +{"i":26,"series":1,"stats":{"get_bytes":11982,"get_count":18,"put_bytes":9974,"put_count":17}} +{"i":27,"series":1,"stats":{"get_bytes":11531,"get_count":14,"put_bytes":9523,"put_count":13}} +{"i":28,"series":1,"stats":{"get_bytes":13052,"get_count":19,"put_bytes":11044,"put_count":18}} +{"i":29,"series":1,"stats":{"get_bytes":12850,"get_count":18,"put_bytes":10842,"put_count":17}} +{"i":30,"series":1,"stats":{"get_bytes":13602,"get_count":21,"put_bytes":11594,"put_count":20}} +{"i":31,"series":1,"stats":{"get_bytes":11835,"get_count":16,"put_bytes":9827,"put_count":15}} +{"i":32,"series":1,"stats":{"get_bytes":12974,"get_count":19,"put_bytes":10966,"put_count":18}} +{"i":33,"series":1,"stats":{"get_bytes":12459,"get_count":19,"put_bytes":10451,"put_count":18}} +{"i":34,"series":1,"stats":{"get_bytes":11969,"get_count":15,"put_bytes":9961,"put_count":14}} +{"i":35,"series":1,"stats":{"get_bytes":12550,"get_count":19,"put_bytes":10542,"put_count":18}} +{"i":36,"series":1,"stats":{"get_bytes":13420,"get_count":19,"put_bytes":11412,"put_count":18}} +{"i":37,"series":1,"stats":{"get_bytes":11452,"get_count":16,"put_bytes":9444,"put_count":15}} +{"i":38,"series":1,"stats":{"get_bytes":11032,"get_count":16,"put_bytes":9024,"put_count":15}} +{"i":39,"series":1,"stats":{"get_bytes":12573,"get_count":18,"put_bytes":10565,"put_count":17}} +{"i":40,"series":1,"stats":{"get_bytes":12341,"get_count":22,"put_bytes":10333,"put_count":21}} +{"i":41,"series":1,"stats":{"get_bytes":13927,"get_count":20,"put_bytes":11919,"put_count":19}} +{"i":42,"series":1,"stats":{"get_bytes":12774,"get_count":20,"put_bytes":10766,"put_count":19}} +{"i":43,"series":1,"stats":{"get_bytes":12950,"get_count":17,"put_bytes":10942,"put_count":16}} +{"i":44,"series":1,"stats":{"get_bytes":13761,"get_count":21,"put_bytes":11753,"put_count":20}} +{"i":45,"series":1,"stats":{"get_bytes":11526,"get_count":17,"put_bytes":9518,"put_count":16}} +{"i":46,"series":1,"stats":{"get_bytes":12574,"get_count":18,"put_bytes":10566,"put_count":17}} +{"i":47,"series":1,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":11562,"put_count":18}} +{"i":48,"series":1,"stats":{"get_bytes":12760,"get_count":20,"put_bytes":10752,"put_count":19}} +{"i":49,"series":1,"stats":{"get_bytes":12422,"get_count":20,"put_bytes":10414,"put_count":19}} +{"i":50,"series":1,"stats":{"get_bytes":13333,"get_count":19,"put_bytes":11325,"put_count":18}} +{"i":51,"series":1,"stats":{"get_bytes":11525,"get_count":15,"put_bytes":9517,"put_count":14}} +{"i":52,"series":1,"stats":{"get_bytes":11604,"get_count":17,"put_bytes":9596,"put_count":16}} +{"i":53,"series":1,"stats":{"get_bytes":12756,"get_count":18,"put_bytes":10748,"put_count":17}} +{"i":54,"series":1,"stats":{"get_bytes":13101,"get_count":22,"put_bytes":11093,"put_count":21}} +{"i":55,"series":1,"stats":{"get_bytes":10933,"get_count":17,"put_bytes":8925,"put_count":16}} +{"i":56,"series":1,"stats":{"get_bytes":11963,"get_count":17,"put_bytes":9955,"put_count":16}} +{"i":57,"series":1,"stats":{"get_bytes":13221,"get_count":17,"put_bytes":11213,"put_count":16}} +{"i":58,"series":1,"stats":{"get_bytes":13612,"get_count":21,"put_bytes":11604,"put_count":20}} +{"i":59,"series":1,"stats":{"get_bytes":12968,"get_count":16,"put_bytes":10960,"put_count":15}} +{"i":60,"series":1,"stats":{"get_bytes":12674,"get_count":18,"put_bytes":10666,"put_count":17}} +{"i":61,"series":1,"stats":{"get_bytes":14108,"get_count":23,"put_bytes":12100,"put_count":22}} +{"i":62,"series":1,"stats":{"get_bytes":12540,"get_count":18,"put_bytes":10532,"put_count":17}} +{"i":63,"series":1,"stats":{"get_bytes":13371,"get_count":19,"put_bytes":11363,"put_count":18}} +{"i":64,"series":1,"stats":{"get_bytes":11959,"get_count":19,"put_bytes":9951,"put_count":18}} +{"i":65,"series":1,"stats":{"get_bytes":12206,"get_count":19,"put_bytes":10198,"put_count":18}} +{"i":66,"series":1,"stats":{"get_bytes":11009,"get_count":16,"put_bytes":9001,"put_count":15}} +{"i":67,"series":1,"stats":{"get_bytes":12714,"get_count":20,"put_bytes":10706,"put_count":19}} +{"i":68,"series":1,"stats":{"get_bytes":12447,"get_count":17,"put_bytes":10439,"put_count":16}} +{"i":69,"series":1,"stats":{"get_bytes":11005,"get_count":17,"put_bytes":8997,"put_count":16}} +{"i":70,"series":1,"stats":{"get_bytes":13527,"get_count":18,"put_bytes":11519,"put_count":17}} +{"i":71,"series":1,"stats":{"get_bytes":10400,"get_count":16,"put_bytes":8392,"put_count":15}} +{"i":72,"series":1,"stats":{"get_bytes":12082,"get_count":15,"put_bytes":10074,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":12762,"get_count":19,"put_bytes":10754,"put_count":18}} +{"i":74,"series":1,"stats":{"get_bytes":12966,"get_count":20,"put_bytes":10958,"put_count":19}} +{"i":75,"series":1,"stats":{"get_bytes":11605,"get_count":18,"put_bytes":9597,"put_count":17}} +{"i":76,"series":1,"stats":{"get_bytes":12740,"get_count":19,"put_bytes":10732,"put_count":18}} +{"i":77,"series":1,"stats":{"get_bytes":13254,"get_count":18,"put_bytes":11246,"put_count":17}} +{"i":78,"series":1,"stats":{"get_bytes":12525,"get_count":18,"put_bytes":10517,"put_count":17}} +{"i":79,"series":1,"stats":{"get_bytes":12876,"get_count":21,"put_bytes":10868,"put_count":20}} +{"i":80,"series":1,"stats":{"get_bytes":12217,"get_count":18,"put_bytes":10209,"put_count":17}} +{"i":81,"series":1,"stats":{"get_bytes":11588,"get_count":18,"put_bytes":9580,"put_count":17}} +{"i":82,"series":1,"stats":{"get_bytes":11678,"get_count":19,"put_bytes":9670,"put_count":18}} +{"i":83,"series":1,"stats":{"get_bytes":13733,"get_count":17,"put_bytes":11725,"put_count":16}} +{"i":84,"series":1,"stats":{"get_bytes":12403,"get_count":18,"put_bytes":10395,"put_count":17}} +{"i":85,"series":1,"stats":{"get_bytes":12108,"get_count":17,"put_bytes":10100,"put_count":16}} +{"i":86,"series":1,"stats":{"get_bytes":12478,"get_count":18,"put_bytes":10470,"put_count":17}} +{"i":87,"series":1,"stats":{"get_bytes":12101,"get_count":16,"put_bytes":10093,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":13399,"get_count":20,"put_bytes":11391,"put_count":19}} +{"i":89,"series":1,"stats":{"get_bytes":13567,"get_count":18,"put_bytes":11559,"put_count":17}} +{"i":90,"series":1,"stats":{"get_bytes":13068,"get_count":19,"put_bytes":11060,"put_count":18}} +{"i":91,"series":1,"stats":{"get_bytes":12976,"get_count":18,"put_bytes":10968,"put_count":17}} +{"i":92,"series":1,"stats":{"get_bytes":11515,"get_count":16,"put_bytes":9507,"put_count":15}} +{"i":93,"series":1,"stats":{"get_bytes":12802,"get_count":18,"put_bytes":10794,"put_count":17}} +{"i":94,"series":1,"stats":{"get_bytes":11697,"get_count":18,"put_bytes":9689,"put_count":17}} +{"i":95,"series":1,"stats":{"get_bytes":11472,"get_count":17,"put_bytes":9464,"put_count":16}} +{"i":96,"series":1,"stats":{"get_bytes":11570,"get_count":19,"put_bytes":9562,"put_count":18}} +{"i":97,"series":1,"stats":{"get_bytes":11707,"get_count":16,"put_bytes":9699,"put_count":15}} +{"i":98,"series":1,"stats":{"get_bytes":12679,"get_count":20,"put_bytes":10671,"put_count":19}} +{"i":99,"series":1,"stats":{"get_bytes":12512,"get_count":21,"put_bytes":10504,"put_count":20}} +{"i":0,"series":2,"stats":{"get_bytes":11811,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":12430,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":2,"series":2,"stats":{"get_bytes":11036,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":3,"series":2,"stats":{"get_bytes":12469,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":4,"series":2,"stats":{"get_bytes":11880,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":5,"series":2,"stats":{"get_bytes":12694,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":6,"series":2,"stats":{"get_bytes":12810,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":7,"series":2,"stats":{"get_bytes":12544,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":8,"series":2,"stats":{"get_bytes":12713,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":9,"series":2,"stats":{"get_bytes":12243,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":10,"series":2,"stats":{"get_bytes":13556,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":11,"series":2,"stats":{"get_bytes":11308,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":2,"stats":{"get_bytes":11710,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":13,"series":2,"stats":{"get_bytes":13214,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":14,"series":2,"stats":{"get_bytes":12533,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":15,"series":2,"stats":{"get_bytes":11848,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":16,"series":2,"stats":{"get_bytes":10177,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":17,"series":2,"stats":{"get_bytes":10922,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":18,"series":2,"stats":{"get_bytes":13253,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":19,"series":2,"stats":{"get_bytes":12511,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":20,"series":2,"stats":{"get_bytes":12889,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":21,"series":2,"stats":{"get_bytes":11597,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":2,"stats":{"get_bytes":13572,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":23,"series":2,"stats":{"get_bytes":11053,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":24,"series":2,"stats":{"get_bytes":12683,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":25,"series":2,"stats":{"get_bytes":12827,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":26,"series":2,"stats":{"get_bytes":11982,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":27,"series":2,"stats":{"get_bytes":11531,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":28,"series":2,"stats":{"get_bytes":13052,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":29,"series":2,"stats":{"get_bytes":12850,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":30,"series":2,"stats":{"get_bytes":13602,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":31,"series":2,"stats":{"get_bytes":11835,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":32,"series":2,"stats":{"get_bytes":12974,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":33,"series":2,"stats":{"get_bytes":12459,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":34,"series":2,"stats":{"get_bytes":11969,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":35,"series":2,"stats":{"get_bytes":12550,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":36,"series":2,"stats":{"get_bytes":13420,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":37,"series":2,"stats":{"get_bytes":11452,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":38,"series":2,"stats":{"get_bytes":11032,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":2,"stats":{"get_bytes":12573,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":40,"series":2,"stats":{"get_bytes":12341,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":41,"series":2,"stats":{"get_bytes":13927,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":42,"series":2,"stats":{"get_bytes":12774,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":43,"series":2,"stats":{"get_bytes":12950,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":44,"series":2,"stats":{"get_bytes":13761,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":45,"series":2,"stats":{"get_bytes":11526,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":46,"series":2,"stats":{"get_bytes":12574,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":47,"series":2,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":48,"series":2,"stats":{"get_bytes":12760,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":49,"series":2,"stats":{"get_bytes":12422,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":50,"series":2,"stats":{"get_bytes":13333,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":51,"series":2,"stats":{"get_bytes":11525,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":2,"stats":{"get_bytes":11604,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":53,"series":2,"stats":{"get_bytes":12756,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":54,"series":2,"stats":{"get_bytes":13101,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":55,"series":2,"stats":{"get_bytes":10933,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":56,"series":2,"stats":{"get_bytes":11963,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":57,"series":2,"stats":{"get_bytes":13221,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":58,"series":2,"stats":{"get_bytes":13612,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":59,"series":2,"stats":{"get_bytes":12968,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":60,"series":2,"stats":{"get_bytes":12674,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":61,"series":2,"stats":{"get_bytes":14108,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":62,"series":2,"stats":{"get_bytes":12540,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":63,"series":2,"stats":{"get_bytes":13371,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":64,"series":2,"stats":{"get_bytes":11959,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":65,"series":2,"stats":{"get_bytes":12206,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":66,"series":2,"stats":{"get_bytes":11009,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":67,"series":2,"stats":{"get_bytes":12714,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":68,"series":2,"stats":{"get_bytes":12447,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":69,"series":2,"stats":{"get_bytes":11005,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":70,"series":2,"stats":{"get_bytes":13527,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":71,"series":2,"stats":{"get_bytes":10400,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":72,"series":2,"stats":{"get_bytes":12082,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":73,"series":2,"stats":{"get_bytes":12762,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":74,"series":2,"stats":{"get_bytes":12966,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":75,"series":2,"stats":{"get_bytes":11605,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":76,"series":2,"stats":{"get_bytes":12740,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":77,"series":2,"stats":{"get_bytes":13254,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":78,"series":2,"stats":{"get_bytes":12525,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":79,"series":2,"stats":{"get_bytes":12876,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":80,"series":2,"stats":{"get_bytes":12217,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":81,"series":2,"stats":{"get_bytes":11588,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":82,"series":2,"stats":{"get_bytes":11678,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":83,"series":2,"stats":{"get_bytes":13733,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":84,"series":2,"stats":{"get_bytes":12403,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":85,"series":2,"stats":{"get_bytes":12108,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":86,"series":2,"stats":{"get_bytes":12478,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":87,"series":2,"stats":{"get_bytes":12101,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":2,"stats":{"get_bytes":13399,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":89,"series":2,"stats":{"get_bytes":13567,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":90,"series":2,"stats":{"get_bytes":13068,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":91,"series":2,"stats":{"get_bytes":12976,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":92,"series":2,"stats":{"get_bytes":11515,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":93,"series":2,"stats":{"get_bytes":12802,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":94,"series":2,"stats":{"get_bytes":11697,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":95,"series":2,"stats":{"get_bytes":11472,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":96,"series":2,"stats":{"get_bytes":11570,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":97,"series":2,"stats":{"get_bytes":11707,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":98,"series":2,"stats":{"get_bytes":12679,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":99,"series":2,"stats":{"get_bytes":12512,"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 index 32d0de480ac46c4a2a04a26e2a050c5f844a1789..a5949d4507a19e95306d3cabaf1963cf65c74abf 100644 GIT binary patch literal 22423 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

nUHb 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 literal 22805 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 aJ*Le6Y*upR**xn>uGVdRwUV26!~O@E_g--T diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 0e2267a11..a149957d5 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":111147,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":111123,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":105868,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":104488,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":110464,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":110865,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":102853,"get_count":144,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":108169,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":113851,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":109300,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":111910,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":113787,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":113924,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":107260,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":109288,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":109393,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":109613,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":108303,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":103074,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":111344,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":103638,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":106242,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":103465,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":112889,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":111818,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":108993,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":107548,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":107130,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":108386,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":106801,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":112167,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":103982,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":112439,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":107657,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":109317,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":106354,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":107451,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":108700,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":109289,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":103575,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":108045,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":107427,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":109306,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":107437,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":111389,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":110509,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":111173,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":109922,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":112633,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":110165,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":105012,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":106947,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":110335,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":105735,"get_count":143,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":110458,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":105383,"get_count":139,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":106564,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":109976,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":108742,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":111084,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":108269,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":112349,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":104081,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":112114,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":111501,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":104248,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":110096,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":109958,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":111248,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":109970,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":112618,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":106639,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":107945,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":107270,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":105831,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":112476,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":109365,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":107222,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":107974,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":108445,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":108864,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":112180,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":108594,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":103941,"get_count":142,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":113079,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":107886,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":109679,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":107472,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":108065,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":111805,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":114330,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":107387,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":114088,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":107972,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":109341,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":109982,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":106179,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":108027,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":106394,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":108939,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":93326,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":93298,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":89573,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":88148,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":93076,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":93958,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":87041,"get_count":144,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":91321,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":95982,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":92379,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":93983,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":95874,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":96223,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":90861,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":91959,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":92348,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":92566,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":91420,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":87189,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":93370,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":87331,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":89859,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":87323,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":95080,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":94206,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":91758,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":90717,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":90417,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":91586,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":90119,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":94270,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":87612,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":94513,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":90732,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":92062,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":89446,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":90663,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":91749,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":91979,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":87626,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":91210,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":90624,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":91868,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":90451,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":93774,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":93257,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":93877,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":93000,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":94793,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":92612,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":88150,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":89438,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":93141,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":89085,"get_count":143,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":92934,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":88833,"get_count":139,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":89839,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":92782,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":91638,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":93877,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":91092,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":94675,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":87998,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":94097,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":93735,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":88074,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":92798,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":92718,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":93633,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":92177,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":94946,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":89897,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":90960,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":90527,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":89056,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":94684,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":92278,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":90374,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":91126,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":91464,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":91958,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":94461,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":91341,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":87574,"get_count":142,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":95332,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":91175,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":92634,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":90745,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":91219,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":94373,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":96402,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":90715,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":96128,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":91257,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":92103,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":92700,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":89587,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":90971,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":89816,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":91852,"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 index 14763fc6a54c72b615c749c936a016ee59925e6c..83403010a379d8c0804c1f66436d531ed85214cb 100644 GIT binary patch literal 20296 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 Date: Fri, 13 Jan 2023 21:28:09 -0800 Subject: [PATCH 264/339] EVM: Fix precompile 0 send (#1055) * fix send to zero with call_actor_x * Update fvm.rs --- actors/evm/src/interpreter/precompiles/fvm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 1210102ba..ff78d2127 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,7 +1,7 @@ use crate::EVM_MAX_RESERVED_METHOD; use fil_actors_runtime::runtime::{builtins::Type, Runtime}; use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags}; +use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags, METHOD_SEND}; use num_traits::FromPrimitive; use crate::interpreter::{instructions::call::CallKind, precompiles::NativeType, System, U256}; @@ -254,7 +254,7 @@ pub(super) fn call_actor_shared( Address::from_bytes(&addr_bytes).map_err(|_| PrecompileError::InvalidInput)? }; - if method <= EVM_MAX_RESERVED_METHOD { + if method <= EVM_MAX_RESERVED_METHOD && method != METHOD_SEND { return Err(PrecompileError::InvalidInput); } From 757d9e94f8c0cc92be4a6336df028147e68d6b37 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 13 Jan 2023 23:36:09 -0800 Subject: [PATCH 265/339] Test: solidity call_actor tests (#1056) * wip constructor of solidity contract! * MVP call_actor_id precompile testgit add .! * cleanup solidity actor test add one for full address, remove accidental log --- actors/evm/tests/call.rs | 178 +++++++++++++++++- .../tests/contracts/CallActorPrecompile.abi | 1 + .../tests/contracts/CallActorPrecompile.bin | 1 + .../tests/contracts/CallActorPrecompile.hex | 1 + .../contracts/CallActorPrecompile.signatures | 3 + .../tests/contracts/CallActorPrecompile.sol | 19 ++ .../CallActorPrecompile_storage.json | 1 + 7 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 actors/evm/tests/contracts/CallActorPrecompile.abi create mode 100644 actors/evm/tests/contracts/CallActorPrecompile.bin create mode 100644 actors/evm/tests/contracts/CallActorPrecompile.hex create mode 100644 actors/evm/tests/contracts/CallActorPrecompile.signatures create mode 100644 actors/evm/tests/contracts/CallActorPrecompile.sol create mode 100644 actors/evm/tests/contracts/CallActorPrecompile_storage.json diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 40727175b..c6d64c2d3 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -1,18 +1,28 @@ 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::interpreter::address::EthAddress; use evm::interpreter::U256; use evm::EVM_CONTRACT_REVERTED; use fil_actor_evm as evm; -use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_ipld_encoding::{BytesSer, IPLD_RAW}; +use fvm_ipld_encoding::{BytesDe, BytesSer, 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::ExitCode; use fvm_shared::sys::SendFlags; -use fvm_shared::{MethodNum, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum, METHOD_SEND}; +use once_cell::sync::Lazy; mod util; @@ -350,6 +360,11 @@ 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(); @@ -375,7 +390,7 @@ fn test_native_call() { rt.expect_validate_caller_any(); let err = rt.call::(1026, None).unwrap_err(); assert_eq!(err.exit_code().value(), 42); - assert_eq!(err.data(), &[]); + assert!(err.data().is_empty()); rt.expect_validate_caller_any(); let err = rt.call::(1027, None).unwrap_err(); @@ -446,6 +461,23 @@ fn test_callactor_revert() { 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. @@ -459,6 +491,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i // 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); @@ -603,6 +636,141 @@ 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, 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_generalized( + Address::new_id(101), + 0, + None, + TokenAmount::from_atto(0), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + ); + + 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 = FILAddress::new_id(10101); + let evm_del = EthAddress(util::CONTRACT_ADDRESS).try_into().unwrap(); + tester.rt.add_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_generalized( + to_address, + 0, + None, + TokenAmount::from_atto(0), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + ); + + 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)); + } +} + +pub(crate) struct ContractTester { + rt: MockRuntime, + _address: EthAddress, +} + +impl ContractTester { + fn new(addr: EthAddress, 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(), + }; + // 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.add_delegated_address( + Address::new_id(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."); + 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/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 From cc264f7b7fbe5056d5b2aaae7f778eed7a35583f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 14 Jan 2023 11:38:10 -0500 Subject: [PATCH 266/339] Fix: EAM: Successfully create actors over placeholders (#1058) --- actors/eam/src/lib.rs | 12 ++++-- actors/eam/tests/create.rs | 75 +++++++++++++++++++++++++++++++++++++- actors/evm/tests/util.rs | 6 +++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 93e50f2b2..836a9cbac 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -169,13 +169,19 @@ fn create_actor( RawBytes::serialize(EvmConstructorParams { creator, initcode: initcode.into() })?; let value = rt.message().value_received(); - // Try to resurrect it if it already exists. let f4_addr = Address::new_delegated(EAM_ACTOR_ID, &new_addr.0).unwrap(); + if let Some(id) = rt.resolve_address(&f4_addr) { - rt.send(&Address::new_id(id), RESURRECT_METHOD, constructor_params.into(), value)?; - return Ok(Return { actor_id: id, robust_address: None, eth_address: new_addr }); + // Try to resurrect it if it is already an EVM actor (must be "dead") + let caller_code_cid = rt.get_actor_code_cid(&id).expect("failed to lookup actor code"); + if let Some(Type::EVM) = rt.resolve_builtin_actor_type(&caller_code_cid) { + rt.send(&Address::new_id(id), RESURRECT_METHOD, constructor_params.into(), value)?; + return Ok(Return { actor_id: id, robust_address: None, eth_address: new_addr }); + } } + // If the f4 address wasn't resolved, or resolved to something other than an EVM actor, we try + // to construct it "normally". let init_params = Exec4Params { code_cid: rt.get_code_cid_for_type(Type::EVM), constructor_params, diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 7c1300162..7c41df4ca 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -4,10 +4,12 @@ use eam::{ compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, }; use fil_actor_eam as eam; +use fil_actor_eam::CreateExternalParams; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ - expect_empty, MockRuntime, EVM_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, + expect_empty, MockRuntime, ETHACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, + PLACEHOLDER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -17,7 +19,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; #[test] -fn call_create() { +fn call_create_new() { let mut rt = construct_and_verify(); let id_addr = Address::new_id(110); @@ -77,6 +79,74 @@ fn call_create() { rt.verify(); } +#[test] +fn call_create_external_over_placeholder() { + let mut rt = construct_and_verify(); + + let caller_id_addr = Address::new_id(110); + let caller_eth_addr = + eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let caller_f4_eth_addr = Address::new_delegated(10, &caller_eth_addr.0).unwrap(); + rt.add_delegated_address(caller_id_addr, caller_f4_eth_addr); + + // Accounts and EthAccounts are the valid caller types for CreateExternal + rt.set_caller(*ETHACCOUNT_ACTOR_CODE_ID, caller_id_addr); + rt.set_origin(caller_id_addr); + + let target_id_addr = Address::new_id(111); + let target_eth_addr = compute_address_create(&rt, &caller_eth_addr, 0); + let target_f4_eth_addr = Address::new_delegated(10, &target_eth_addr.0).unwrap(); + rt.add_delegated_address(target_id_addr, target_f4_eth_addr); + rt.set_address_actor_type(target_id_addr, *PLACEHOLDER_ACTOR_CODE_ID); + + let initcode = vec![0xff]; + + let create_params = CreateExternalParams(initcode.clone()); + + let evm_params = EvmConstructorParams { creator: caller_eth_addr, initcode: initcode.into() }; + + let params = Exec4Params { + code_cid: *EVM_ACTOR_CODE_ID, + constructor_params: RawBytes::serialize(evm_params).unwrap(), + subaddress: target_eth_addr.0[..].to_owned().into(), + }; + + let send_return = Exec4Return { + id_address: Address::new_id(111), + robust_address: Address::new_actor(&[0xde, 0xad, 0xbe, 0xef]), + }; + let send_return_ser = IpldBlock::serialize_cbor(&send_return).unwrap(); + + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC4_METHOD, + IpldBlock::serialize_cbor(¶ms).unwrap(), + TokenAmount::from_atto(0), + send_return_ser, + ExitCode::OK, + ); + + rt.expect_validate_caller_addr(vec![caller_id_addr]); + let result = rt + .call::( + eam::Method::CreateExternal as u64, + IpldBlock::serialize_cbor(&create_params).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize::() + .unwrap(); + + let expected_return = Return { + actor_id: target_id_addr.id().unwrap(), + robust_address: Some(send_return.robust_address), + eth_address: target_eth_addr, + }; + + assert_eq!(result, expected_return); + rt.verify(); +} + #[test] fn call_resurrect() { let mut rt = construct_and_verify(); @@ -93,6 +163,7 @@ fn call_resurrect() { let target_eth_addr = compute_address_create(&rt, &caller_eth_addr, 0); let target_f4_eth_addr = Address::new_delegated(10, &target_eth_addr.0).unwrap(); rt.add_delegated_address(target_id_addr, target_f4_eth_addr); + rt.set_address_actor_type(target_id_addr, *EVM_ACTOR_CODE_ID); rt.expect_validate_caller_type(vec![Type::EVM]); diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index fa7290ec4..abe4914ec 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -2,10 +2,13 @@ use cid::Cid; use evm::interpreter::U256; use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; +use fil_actor_evm::State; +use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::{ test_utils::{self, *}, ActorError, 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}; @@ -55,6 +58,9 @@ pub fn init_construct_and_verify( ) .unwrap() .is_none()); + let evm_st: State = rt.state().unwrap(); + let evm_code = rt.store.get(&evm_st.bytecode).unwrap().unwrap(); + println!("{:?}", evm_code); rt.verify(); rt From 7381413f13be96e7db049d3ba2dfcd04058e1c69 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jan 2023 08:44:20 -0800 Subject: [PATCH 267/339] EVM: fix sar behavior, and test bit shifts (#1063) fixes https://github.com/filecoin-project/ref-fvm/issues/1465 --- .../src/interpreter/instructions/bitwise.rs | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs index 1b51ea6df..5a9024c74 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -40,7 +40,7 @@ pub fn sar(shift: U256, mut value: U256) -> U256 { U256::MAX } else { // value is >= 0, pushing 0 - U256::ONE + U256::ZERO } } else { let shift = shift.low_u32(); @@ -59,6 +59,70 @@ pub fn sar(shift: U256, mut value: U256) -> U256 { 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::>()); From 231bdc50dbfff4219cefd56e8f5d7139a1721779 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jan 2023 08:45:09 -0800 Subject: [PATCH 268/339] feat: implement push0 (#1061) fixes https://github.com/filecoin-project/ref-fvm/issues/1400 --- actors/evm/src/interpreter/execution.rs | 1 + actors/evm/src/interpreter/instructions/mod.rs | 1 + actors/evm/src/interpreter/instructions/stack.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 3bea4c126..cc98fce3b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -163,6 +163,7 @@ pub mod opcodes { 0x59: MSIZE, 0x5a: GAS, 0x5b: JUMPDEST, + 0x5F: PUSH0, 0x60: PUSH1, 0x61: PUSH2, 0x62: PUSH3, diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 36ef2a27b..f7f2d769e 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -274,6 +274,7 @@ 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> } diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index 77c8ab662..dffa31ae5 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -26,6 +26,7 @@ pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result 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]}), @@ -66,3 +67,15 @@ fn test_push_pad_right() { 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); +} From 23949e94ff040a9b5bdd5b3283f9ac450e305ca6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jan 2023 08:47:02 -0800 Subject: [PATCH 269/339] chore: remove callactor opcode from the test framework (#1060) The opcode itself isn't actually defined, this is just leftover testing code. --- actors/evm/tests/asm.rs | 17 +---------------- actors/evm/tests/opcodes/callactor.hex | 1 - 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 actors/evm/tests/opcodes/callactor.hex diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index b8b000e94..5ee860aaa 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -44,8 +44,7 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a // the contract code let mut body_code = Vec::new(); let mut ingest_body = Ingest::new(&mut body_code); - let body = with_fevm_extensions(body); - let body_with_prelude = PRELUDE.to_owned() + &body; + 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(); @@ -92,17 +91,3 @@ pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_a contract_code.append(&mut body_code); Ok(contract_code) } - -// this is a hack to support mnemonics for the FEVM extension opcodes -// it is really ugly, but the etk assmebler doesn't currently support any way to -// directly embed (otherwise invalid) asm instructions in the stream... sigh. -// Ideally we would just do them as macros like -// %macro methodnum() -// 0xb1 -// %end -// Note that to add insult to injury, macros cannot %include_hex... double sigh. -// So f*ck it, we'll just hack this until there is support. -// See also https://github.com/quilt/etk/issues/110 -fn with_fevm_extensions(body: &str) -> String { - body.to_owned().replace("@callactor", "%include_hex(\"tests/opcodes/callactor.hex\")") -} diff --git a/actors/evm/tests/opcodes/callactor.hex b/actors/evm/tests/opcodes/callactor.hex deleted file mode 100644 index 334536890..000000000 --- a/actors/evm/tests/opcodes/callactor.hex +++ /dev/null @@ -1 +0,0 @@ -b0 \ No newline at end of file From 715e1b8ce84131d96debc02718d9e543cc77313a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jan 2023 08:48:26 -0800 Subject: [PATCH 270/339] fix: return early when trying to deploy over an existing contract (#1059) Now that we have this information, we may as well use it. --- actors/eam/src/lib.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index 836a9cbac..4e1b13920 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -174,9 +174,20 @@ fn create_actor( if let Some(id) = rt.resolve_address(&f4_addr) { // Try to resurrect it if it is already an EVM actor (must be "dead") let caller_code_cid = rt.get_actor_code_cid(&id).expect("failed to lookup actor code"); - if let Some(Type::EVM) = rt.resolve_builtin_actor_type(&caller_code_cid) { - rt.send(&Address::new_id(id), RESURRECT_METHOD, constructor_params.into(), value)?; - return Ok(Return { actor_id: id, robust_address: None, eth_address: new_addr }); + match rt.resolve_builtin_actor_type(&caller_code_cid) { + // If it's an EVM actor, resurrect it. + Some(Type::EVM) => { + rt.send(&Address::new_id(id), RESURRECT_METHOD, constructor_params.into(), value)?; + return Ok(Return { actor_id: id, robust_address: None, eth_address: new_addr }); + } + // If it's a Placeholder, continue on to create it. + Some(Type::Placeholder) => {} + // Otherwise, return an error. + _ => { + return Err( + actor_error!(forbidden; "cannot deploy contract over existing contract at address {new_addr}"), + ) + } } } From 4dd5461a06639bdc3c5bbb81f70ff899282a8c9f Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 17 Jan 2023 13:53:46 -0800 Subject: [PATCH 271/339] EVM: derive copy for precompile enum in tests (#1068) impl Copy for NativePrecompile --- actors/evm/tests/precompile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index f1987c254..8b6cb70bc 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -84,7 +84,7 @@ enum PrecompileExit { } #[repr(u8)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum NativePrecompile { ResolveAddress = 1, LookupDelegatedAddress = 2, From 7063b4be5580e113761611ef5f703be7d000b125 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 17 Jan 2023 17:17:03 -0800 Subject: [PATCH 272/339] EVM: Refactor precompile tests (#1069) * wip * rename assembly contract * fix logging for precompiles * do a lot of things, needs to get split into multiple PRs * cut down changes for new PR * impl copy for NativePrecompile --- actors/evm/tests/call.rs | 197 +++++++-------------- actors/evm/tests/precompile.rs | 183 +++----------------- actors/evm/tests/util.rs | 301 +++++++++++++++++++++++++++++++-- 3 files changed, 375 insertions(+), 306 deletions(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index c6d64c2d3..ed3b42486 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -398,57 +398,6 @@ fn test_native_call() { assert_eq!(err.data(), &b"foobar"[..]); } -#[allow(dead_code)] -pub fn callactor_proxy_contract() -> Vec { - let init = ""; - let body = r#" -# get call payload size -calldatasize -# store payload to mem 0x00 -push1 0x00 -push1 0x00 -calldatacopy - -# prepare the proxy call - -# out size -# out off -push1 0x20 -push1 0xa0 - -# in size -# in off -calldatasize -push1 0x00 - -# dst (callactor precompile) -push20 0xfe00000000000000000000000000000000000003 - -# gas -push4 0xffffffff - -# call_actor must be from delegatecall -delegatecall - -# write exit code memory -push1 0x00 # offset -mstore8 - -returndatasize -push1 0x00 # offset -push1 0x01 # dest offset (we have already written the exit code of the call) -returndatacopy - -returndatasize -push1 0x01 # (add 1 to the returndatasize to accommodate for the exitcode) -add -push1 0x00 -return -"#; - - asm::new_contract("callactor-proxy", init, body).unwrap() -} - #[test] fn test_callactor_success() { // Should work if the called actor succeeds. @@ -485,7 +434,10 @@ fn test_callactor_restrict() { } fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_input: bool) { - let contract = callactor_proxy_contract(); + 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; @@ -534,9 +486,9 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i // expected return data // Test with a codec _other_ than DAG_CBOR, to make sure we are actually passing the returned codec let some_codec = 0x42; - let send_return = IpldBlock { codec: some_codec, data: vec![0xde, 0xad, 0xbe, 0xef] }; + let data = vec![0xde, 0xad, 0xbe, 0xef]; + let send_return = IpldBlock { codec: some_codec, data }; - rt.expect_gas_available(10_000_000_000u64); if valid_call_input { // We only get to the send_generalized if the call params were valid rt.expect_send_generalized( @@ -544,91 +496,70 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i method_num, make_raw_params(proxy_call_input_data), TokenAmount::zero(), - Some(0xffffffff), + Some(0), send_flags, Some(send_return.clone()), exit_code, ); } - /// ensures top bits are zeroed - fn assert_zero_bytes(src: &[u8]) { - assert_eq!(src[..S], [0u8; S]); - } + // output bytes are padded to nearest 32 byte + let mut v = vec![0; 32]; + v[..4].copy_from_slice(&send_return.data); - // invoke - - let mut result = util::invoke_contract(&mut rt, &contract_params); - rt.verify(); - rt.reset(); + let expect = CallActorReturn { + exit_code, + codec: send_return.codec, + data_offset: 96, + data_size: send_return.data.len() as u32, + data: v, + }; - let call_result = result.remove(0); - if !valid_call_input { - // 0 is failure - assert_eq!(call_result, 0); + let (expected_exit, expected_out) = if valid_call_input { + (util::PrecompileExit::Success, expect.into_vec()) } else { - // 1 is success - assert_eq!(call_result, 1); - - // if call succeeded, we should have a return that we assert over - - #[derive(Debug, PartialEq, Eq)] - struct CallActorReturn { - exit_code: ExitCode, - codec: u64, - data_offset: u32, - data_size: u32, - data: Vec, - } - - impl CallActorReturn { - pub fn read(src: &[u8]) -> Self { - assert!( - src.len() >= 4 * 32, - "expected to read at least 4 U256 values, got {:?}", - src - ); - - let bytes = &src[..32]; - let exit_code = { - assert_zero_bytes::<4>(bytes); - ExitCode::new(u32::from_be_bytes(bytes[28..32].try_into().unwrap())) - }; - - let bytes = &src[32..64]; - let codec = { - assert_zero_bytes::<8>(bytes); - u64::from_be_bytes(bytes[24..32].try_into().unwrap()) - }; - - let bytes = &src[64..96]; - let offset = { - assert_zero_bytes::<4>(bytes); - u32::from_be_bytes(bytes[28..32].try_into().unwrap()) - }; - - let bytes = &src[96..128]; - let size = { - assert_zero_bytes::<4>(bytes); - u32::from_be_bytes(bytes[28..32].try_into().unwrap()) - }; - - let data = Vec::from(&src[128..128 + size as usize]); - - Self { exit_code, codec, data_offset: offset, data_size: size, data } - } - } - - let result = CallActorReturn::read(&result); - let expected = CallActorReturn { - exit_code, - codec: send_return.clone().codec, - data_offset: 96, - data_size: send_return.clone().data.len() as u32, - data: send_return.data, - }; + (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, + }; - assert_eq!(result, expected); + // invoke + test.run_test(&mut rt); +} + +#[derive(Debug, PartialEq, Eq)] +struct CallActorReturn { + exit_code: ExitCode, + codec: u64, + data_offset: u32, + data_size: u32, + data: Vec, +} + +impl CallActorReturn { + fn into_vec(self) -> Vec { + assert_eq!(self.data.len() % 32, 0); + + let exit = U256::from(self.exit_code.value()); + let codec = U256::from(self.codec); + let data_offset = U256::from(self.data_offset); + let data_size = U256::from(self.data_size); + + let mut out = [exit, codec, data_offset, data_size] + .iter() + .map(|p| p.to_bytes().to_vec()) + .collect::>>() + .concat(); + out.extend_from_slice(&self.data); + out } } @@ -645,7 +576,7 @@ fn call_actor_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, contract_hex); + let mut tester = ContractTester::new(contract_address, 111, contract_hex); // call_actor_id { @@ -722,7 +653,7 @@ pub(crate) struct ContractTester { } impl ContractTester { - fn new(addr: EthAddress, contract_hex: &str) -> Self { + fn new(addr: EthAddress, id: u64, contract_hex: &str) -> Self { init_logging().ok(); let mut rt = MockRuntime::default(); @@ -730,6 +661,8 @@ impl ContractTester { 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); @@ -755,7 +688,7 @@ impl ContractTester { } fn call(&mut self, call: TestContractCall) -> Returns { - let input = call.calldata().expect("Should have calldata."); + let input = call.calldata().expect("Should have calldata.").to_vec(); let input = IpldBlock::serialize_cbor(&BytesSer(&input)).expect("failed to serialize input data"); diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 8b6cb70bc..53b6c32cf 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -1,7 +1,5 @@ mod asm; -use std::fmt::Debug; - use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::{ @@ -15,7 +13,7 @@ use fil_actors_runtime::{ use fvm_shared::{address::Address as FILAddress, econ::TokenAmount, error::ExitCode, METHOD_SEND}; mod util; -use util::id_to_vec; +use util::{id_to_vec, NativePrecompile, PrecompileExit, PrecompileTest}; #[allow(dead_code)] pub fn magic_precompile_contract() -> Vec { @@ -69,152 +67,16 @@ fn test_precompile_hash() { ); } -fn precompile_address(prefix: u8, index: u8) -> EthAddress { - let mut buf = [0u8; 20]; - buf[0] = prefix; - buf[19] = index; - EthAddress(buf) -} - -#[repr(u8)] -#[derive(Debug, PartialEq, Eq)] -enum PrecompileExit { - Reverted = 0, - Success = 1, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy)] -pub enum NativePrecompile { - ResolveAddress = 1, - LookupDelegatedAddress = 2, - CallActor = 3, - GetActorType = 4, -} - -impl NativePrecompile { - fn as_address(&self) -> EthAddress { - precompile_address(0xfe, *self as u8) - } -} - -struct PrecompileTest { - pub expected_return: Vec, - pub expected_exit_code: PrecompileExit, - pub precompile_address: EthAddress, - pub output_size: u32, - pub input: Vec, - pub gas_avaliable: u64, -} - -impl Debug for PrecompileTest { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PrecompileTest") - .field("expected_exit_code", &self.expected_exit_code) - .field("precompile_address", &self.precompile_address) - .field("input", &hex::encode(&self.input)) - .field("expected_return", &hex::encode(&self.expected_return)) - .field("output_size", &self.output_size) - .field("gas_avaliable", &self.gas_avaliable) - .finish() - } -} - -impl PrecompileTest { - 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 = util::invoke_contract( - rt, - &[ - self.precompile_address.as_evm_word().to_bytes().to_vec(), - U256::from(self.output_size).to_bytes().to_vec(), - self.input.clone(), - ] - .concat(), - ); - log::trace!("returned: {:?}", hex::encode(&result)); - 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(); - } - - fn test_runner_bytecode() -> Vec { - Self::test_runner_bytecode_transfer_value(0) - } - fn test_runner_bytecode_transfer_value(value: u64) -> Vec { - let init = ""; - let body = format!( - r#" -# store entire input to mem 0x00 -calldatasize -push1 0x00 # input offset -push1 0x00 # dst offset -calldatacopy - -# 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 - -# value -%push({value}) - -# precompile address -push1 0x00 # first word of input is precompile -mload - -# gas -push1 0x00 - -call - -# write exit code 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 -"# - ); - - asm::new_contract("precompile_tester", init, &body).unwrap() - } +fn tester_bytecode() -> Vec { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("precompile-tester", &init, &body).unwrap() } #[test] fn test_native_actor_type() { use evm::interpreter::precompiles::NativeType; - let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); + let mut rt = util::construct_and_verify(tester_bytecode()); // 0x88 is an EVM actor let evm_target = FILAddress::new_id(0x88); @@ -246,12 +108,13 @@ fn test_native_actor_type() { fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { let test = PrecompileTest { - precompile_address: NativePrecompile::GetActorType.as_address(), - input: id_to_vec(&id), + precompile_address: NativePrecompile::GetActorType.eth_address(), output_size: 32, expected_exit_code: PrecompileExit::Success, - expected_return: U256::from(expected as u32).to_bytes().to_vec(), gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(0), + input: id_to_vec(&id), + expected_return: U256::from(expected as u32).to_bytes().to_vec(), }; test.run_test(rt); } @@ -268,12 +131,13 @@ fn test_native_actor_type() { // invalid id parameter (over) fn test_type_invalid(rt: &mut MockRuntime, input: Vec) { let test = PrecompileTest { - precompile_address: NativePrecompile::GetActorType.as_address(), - input, + precompile_address: NativePrecompile::GetActorType.eth_address(), output_size: 32, expected_exit_code: PrecompileExit::Reverted, - expected_return: vec![], gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(0), + expected_return: vec![], + input, }; test.run_test(rt); } @@ -349,7 +213,7 @@ return #[test] fn test_native_lookup_delegated_address() { - let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); + let mut rt = util::construct_and_verify(tester_bytecode()); // f0 10101 is an EVM actor let evm_target = FILAddress::new_id(10101); @@ -363,12 +227,13 @@ fn test_native_lookup_delegated_address() { fn test_lookup_address(rt: &mut MockRuntime, id: FILAddress, expected: Vec) { let test = PrecompileTest { - precompile_address: NativePrecompile::LookupDelegatedAddress.as_address(), - input: id_to_vec(&id), + precompile_address: NativePrecompile::LookupDelegatedAddress.eth_address(), output_size: 32, expected_exit_code: PrecompileExit::Success, - expected_return: expected, gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(0), + expected_return: expected, + input: id_to_vec(&id), }; test.run_test(rt); @@ -453,18 +318,22 @@ fn test_resolve_delegated() { #[test] fn test_precompile_transfer() { - let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode_transfer_value(1)); + 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 = precompile_address(prefix, index); + let addr = util::precompile_address(prefix, index); let test = PrecompileTest { precompile_address: addr, - input: vec![0xff; 32], // garbage input should change nothing output_size: 32, expected_exit_code: PrecompileExit::Success, - expected_return: vec![], 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(fil_addr, METHOD_SEND, None, TokenAmount::from_atto(1), None, ExitCode::OK); diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index abe4914ec..b5d7e0abf 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,12 +1,12 @@ use cid::Cid; +use evm::interpreter::address::EthAddress; use evm::interpreter::U256; -use evm::interpreter::{address::EthAddress, StatusCode}; use fil_actor_evm as evm; use fil_actor_evm::State; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::{ test_utils::{self, *}, - ActorError, EAM_ACTOR_ID, INIT_ACTOR_ADDR, + EAM_ACTOR_ID, INIT_ACTOR_ADDR, }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -14,6 +14,8 @@ 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, |_| {}) @@ -60,7 +62,7 @@ pub fn init_construct_and_verify( .is_none()); let evm_st: State = rt.state().unwrap(); let evm_code = rt.store.get(&evm_st.bytecode).unwrap().unwrap(); - println!("{:?}", evm_code); + log::trace!("{:?}", evm_code); rt.verify(); rt @@ -82,20 +84,7 @@ pub fn invoke_contract(rt: &mut MockRuntime, input_data: &[u8]) -> Vec { } #[allow(dead_code)] -pub fn invoke_contract_expect_abort(rt: &mut MockRuntime, input_data: &[u8], expect: StatusCode) { - rt.expect_validate_caller_any(); - let err = rt - .call::( - evm::Method::InvokeContract as u64, - IpldBlock::serialize_cbor(&BytesSer(input_data)).unwrap(), - ) - .expect_err(&format!("expected contract to fail with {:?}", expect)); - rt.verify(); - // REMOVEME so this is jank... (just copies err creation from execute in lib.rs) - assert_eq!(err, ActorError::unspecified(format!("EVM execution error: {expect:?}"))) -} - -#[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; @@ -111,3 +100,281 @@ 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, 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, +} + +#[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) + } +} + +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!("returned: {:?}", hex::encode(&result)); + 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)] + /// 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()) + } +} From b73be3fcfdfcd6b6a36c6a14c188fb6aad4179aa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 17 Jan 2023 19:27:03 -0800 Subject: [PATCH 273/339] fix: correctly encode create2 params (#1065) * fix: correctly encode create2 params We have tests for this, but they're unit tests that use this structure. fixes https://github.com/filecoin-project/ref-fvm/issues/1468 * test: add a test for create2 We need to flesh these tests out, but this is a start. --- actors/evm/Makefile | 2 +- .../src/interpreter/instructions/lifecycle.rs | 1 + actors/evm/tests/contracts/Factory.abi | 1 + actors/evm/tests/contracts/Factory.bin | 1 + actors/evm/tests/contracts/Factory.signatures | 3 + actors/evm/tests/contracts/FactoryChild.abi | 1 + actors/evm/tests/contracts/FactoryChild.bin | 1 + .../tests/contracts/FactoryChild.signatures | 3 + .../tests/contracts/FactoryChild_storage.json | 1 + .../evm/tests/contracts/Factory_storage.json | 1 + actors/evm/tests/contracts/Lifecycle.hex | 1 + actors/evm/tests/contracts/Lifecycle.sol | 25 +++++ .../tests/contracts/SelfDestruct_storage.json | 1 + .../evm/tests/contracts/StorageFootprint.bin | 2 +- .../evm/tests/contracts/StorageFootprint.hex | 2 +- test_vm/tests/evm_test.rs | 91 ++++++++++++++++++- 16 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 actors/evm/tests/contracts/Factory.abi create mode 100644 actors/evm/tests/contracts/Factory.bin create mode 100644 actors/evm/tests/contracts/Factory.signatures create mode 100644 actors/evm/tests/contracts/FactoryChild.abi create mode 100644 actors/evm/tests/contracts/FactoryChild.bin create mode 100644 actors/evm/tests/contracts/FactoryChild.signatures create mode 100644 actors/evm/tests/contracts/FactoryChild_storage.json create mode 100644 actors/evm/tests/contracts/Factory_storage.json create mode 100644 actors/evm/tests/contracts/Lifecycle.hex create mode 100644 actors/evm/tests/contracts/Lifecycle.sol create mode 100644 actors/evm/tests/contracts/SelfDestruct_storage.json diff --git a/actors/evm/Makefile b/actors/evm/Makefile index 3a5b6dad3..abca4fd7d 100644 --- a/actors/evm/Makefile +++ b/actors/evm/Makefile @@ -21,7 +21,7 @@ test-contracts: $(TEST_CONTRACTS_HEX) $(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 $< | tail -n +4 | tr -d '\n' > $@ + solc --bin $< | sed '4q;d' | tr -d '\n' > $@ solc --bin --abi --storage-layout --hashes --overwrite $< -o $(TEST_CONTRACTS_DIR) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 1afb5b311..5a0d281e0 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -33,6 +33,7 @@ pub struct CreateParams { pub struct Create2Params { #[serde(with = "strict_bytes")] pub code: Vec, + #[serde(with = "strict_bytes")] pub salt: [u8; 32], } 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/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/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/StorageFootprint.bin b/actors/evm/tests/contracts/StorageFootprint.bin index 2eabe041f..c382c241d 100644 --- a/actors/evm/tests/contracts/StorageFootprint.bin +++ b/actors/evm/tests/contracts/StorageFootprint.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b506107d8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610773565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b92508263ffffffff03821115610768576107676106de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212204627e855ec638124e86ac435a22bc2e64114895f4cfa3ad1db43962f0273c67a64736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506107d6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610771565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b9250828201905063ffffffff81111561076b5761076a6106de565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212209ead5577e72317eb390c1db842de5179a6cf2a8df9cc840fb0a80f11d1fcf07064736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.hex b/actors/evm/tests/contracts/StorageFootprint.hex index 2eabe041f..c382c241d 100644 --- a/actors/evm/tests/contracts/StorageFootprint.hex +++ b/actors/evm/tests/contracts/StorageFootprint.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506107d8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610773565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b92508263ffffffff03821115610768576107676106de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212204627e855ec638124e86ac435a22bc2e64114895f4cfa3ad1db43962f0273c67a64736f6c634300080f0033 \ No newline at end of file +608060405234801561001057600080fd5b506107d6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610771565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b9250828201905063ffffffff81111561076b5761076a6106de565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212209ead5577e72317eb390c1db842de5179a6cf2a8df9cc840fb0a80f11d1fcf07064736f6c63430008110033 \ No newline at end of file diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index be537c5b9..9b61134da 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -19,8 +19,10 @@ use test_vm::{ TEST_FAUCET_ADDR, VM, }; -// Generate a statically typed interface for the contract. +// 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]; @@ -34,7 +36,7 @@ fn id_to_eth(id: ActorID) -> EthAddress { struct ContractParams(#[serde(with = "strict_bytes")] pub Vec); #[test] -fn test_evm_lifecycle() { +fn test_evm_call() { let store = MemoryBlockstore::new(); let v = VM::new_with_singletons(&store); @@ -85,6 +87,91 @@ fn test_evm_lifecycle() { 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 child_addr: EthAddress = { + let func = factory.create_2([0; 32], 42); + let call_params = 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(&func.function.name, &return_value).expect("failed to decode return") + }; + + let child_addr = Address::new_delegated(EAM_ACTOR_ID, &child_addr.0[..]).unwrap(); + + { + 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); + }; +} + #[test] fn test_evm_eth_create_external() { let store = MemoryBlockstore::new(); From a05defff260c86a4365b6cfbe4fb9b91dd3e13ac Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 17 Jan 2023 22:03:58 -0800 Subject: [PATCH 274/339] fix: correctly decode EAM return on resurrect (#1072) * fix: correctly decode EAM return on resurrect And improve the lifecycle tests. * fix create test --- .../src/interpreter/instructions/lifecycle.rs | 2 +- actors/evm/tests/create.rs | 2 +- test_vm/tests/evm_test.rs | 150 ++++++++++++------ 3 files changed, 106 insertions(+), 48 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 5a0d281e0..871f56a29 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -40,7 +40,7 @@ pub struct Create2Params { #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Copy, PartialEq, Eq)] pub struct EamReturn { pub actor_id: u64, - pub robust_address: Address, + pub robust_address: Option

, pub eth_address: EthAddress, } diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index 6e1c203e4..ce44e988e 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -58,7 +58,7 @@ fn test_create() { let fake_ret = EamReturn { actor_id: 12345, eth_address: fake_eth_addr, - robust_address: (&fake_eth_addr).try_into().unwrap(), + robust_address: Some((&fake_eth_addr).try_into().unwrap()), }; let salt = diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index 9b61134da..ccbd5bb5b 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -1,8 +1,8 @@ use std::sync::Arc; -use ethers::core::types::Address as EthAddress; use ethers::prelude::abigen; use ethers::providers::Provider; +use ethers::{core::types::Address as EthAddress, prelude::builders::ContractCall}; use fil_actor_evm::interpreter::U256; use fil_actors_runtime::{ test_utils::{ETHACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID}, @@ -122,54 +122,112 @@ fn test_evm_create() { let create_return: fil_actor_eam::CreateExternalReturn = create_result.ret.unwrap().deserialize().expect("failed to decode results"); - let child_addr: EthAddress = { - let func = factory.create_2([0; 32], 42); - let call_params = 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(&func.function.name, &return_value).expect("failed to decode return") + 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 }; - let child_addr = Address::new_delegated(EAM_ACTOR_ID, &child_addr.0[..]).unwrap(); + // 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); - { - 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); - }; + // 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] From 0592571a7ec881ca073266e57ad233c90b7e2a3b Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 17 Jan 2023 22:10:43 -0800 Subject: [PATCH 275/339] EVM: update measurements (#1073) * update measurements * update measurement images --- .../tests/measurements/array_push_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/array_push_n1.png | Bin 18027 -> 18027 bytes .../measurements/array_push_n100.jsonline | 400 +++++++++--------- .../tests/measurements/array_push_n100.png | Bin 27680 -> 27586 bytes .../tests/measurements/array_read_n1.jsonline | 200 ++++----- .../evm/tests/measurements/array_read_n1.png | Bin 13481 -> 13483 bytes .../measurements/array_read_n100.jsonline | 200 ++++----- .../tests/measurements/array_read_n100.png | Bin 14532 -> 14530 bytes .../measurements/inc_after_fill.jsonline | 400 +++++++++--------- .../evm/tests/measurements/inc_after_fill.png | Bin 14273 -> 14276 bytes .../measurements/inc_one_vs_all.jsonline | 40 +- .../evm/tests/measurements/inc_one_vs_all.png | Bin 14346 -> 14371 bytes .../measurements/mapping_add_n1.jsonline | 400 +++++++++--------- .../evm/tests/measurements/mapping_add_n1.png | Bin 20987 -> 21163 bytes .../measurements/mapping_add_n100.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_add_n100.png | Bin 22527 -> 22529 bytes .../measurements/mapping_overwrite.jsonline | 400 +++++++++--------- .../tests/measurements/mapping_overwrite.png | Bin 22423 -> 22406 bytes .../measurements/mapping_read_n1.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n1.png | Bin 17960 -> 17958 bytes .../measurements/mapping_read_n100.jsonline | 200 ++++----- .../tests/measurements/mapping_read_n100.png | Bin 20296 -> 20282 bytes 22 files changed, 1620 insertions(+), 1620 deletions(-) diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline index 6e7e9c866..eb6aa57bb 100644 --- a/actors/evm/tests/measurements/array_push_n1.jsonline +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2313,"put_count":5}} -{"i":1,"series":1,"stats":{"get_bytes":2187,"get_count":3,"put_bytes":183,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2191,"get_count":3,"put_bytes":187,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2195,"get_count":3,"put_bytes":191,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2199,"get_count":3,"put_bytes":195,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2203,"get_count":3,"put_bytes":199,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2207,"get_count":3,"put_bytes":204,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2212,"get_count":3,"put_bytes":208,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2216,"get_count":3,"put_bytes":336,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2344,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":344,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2352,"get_count":4,"put_bytes":348,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2356,"get_count":4,"put_bytes":352,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2360,"get_count":4,"put_bytes":356,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2364,"get_count":4,"put_bytes":361,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2369,"get_count":4,"put_bytes":365,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2373,"get_count":4,"put_bytes":317,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2325,"get_count":4,"put_bytes":321,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2329,"get_count":4,"put_bytes":325,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2333,"get_count":4,"put_bytes":329,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2337,"get_count":4,"put_bytes":333,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2341,"get_count":4,"put_bytes":337,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2345,"get_count":4,"put_bytes":342,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":346,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2354,"get_count":4,"put_bytes":440,"put_count":4}} -{"i":25,"series":1,"stats":{"get_bytes":2448,"get_count":5,"put_bytes":444,"put_count":4}} -{"i":26,"series":1,"stats":{"get_bytes":2452,"get_count":5,"put_bytes":448,"put_count":4}} -{"i":27,"series":1,"stats":{"get_bytes":2456,"get_count":5,"put_bytes":452,"put_count":4}} -{"i":28,"series":1,"stats":{"get_bytes":2460,"get_count":5,"put_bytes":456,"put_count":4}} -{"i":29,"series":1,"stats":{"get_bytes":2464,"get_count":5,"put_bytes":460,"put_count":4}} -{"i":30,"series":1,"stats":{"get_bytes":2468,"get_count":5,"put_bytes":465,"put_count":4}} -{"i":31,"series":1,"stats":{"get_bytes":2473,"get_count":5,"put_bytes":469,"put_count":4}} -{"i":32,"series":1,"stats":{"get_bytes":2333,"get_count":4,"put_bytes":366,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2374,"get_count":4,"put_bytes":370,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2378,"get_count":4,"put_bytes":374,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":378,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2386,"get_count":4,"put_bytes":382,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":386,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2394,"get_count":4,"put_bytes":391,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2399,"get_count":4,"put_bytes":395,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2403,"get_count":4,"put_bytes":489,"put_count":4}} -{"i":41,"series":1,"stats":{"get_bytes":2497,"get_count":5,"put_bytes":493,"put_count":4}} -{"i":42,"series":1,"stats":{"get_bytes":2501,"get_count":5,"put_bytes":497,"put_count":4}} -{"i":43,"series":1,"stats":{"get_bytes":2505,"get_count":5,"put_bytes":501,"put_count":4}} -{"i":44,"series":1,"stats":{"get_bytes":2509,"get_count":5,"put_bytes":505,"put_count":4}} -{"i":45,"series":1,"stats":{"get_bytes":2513,"get_count":5,"put_bytes":509,"put_count":4}} -{"i":46,"series":1,"stats":{"get_bytes":2517,"get_count":5,"put_bytes":514,"put_count":4}} -{"i":47,"series":1,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":518,"put_count":4}} -{"i":48,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":415,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2423,"get_count":4,"put_bytes":419,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2427,"get_count":4,"put_bytes":423,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2431,"get_count":4,"put_bytes":427,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2435,"get_count":4,"put_bytes":431,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2439,"get_count":4,"put_bytes":435,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2443,"get_count":4,"put_bytes":440,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2448,"get_count":4,"put_bytes":444,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2452,"get_count":4,"put_bytes":538,"put_count":4}} -{"i":57,"series":1,"stats":{"get_bytes":2546,"get_count":5,"put_bytes":542,"put_count":4}} -{"i":58,"series":1,"stats":{"get_bytes":2550,"get_count":5,"put_bytes":546,"put_count":4}} -{"i":59,"series":1,"stats":{"get_bytes":2554,"get_count":5,"put_bytes":550,"put_count":4}} -{"i":60,"series":1,"stats":{"get_bytes":2558,"get_count":5,"put_bytes":554,"put_count":4}} -{"i":61,"series":1,"stats":{"get_bytes":2562,"get_count":5,"put_bytes":558,"put_count":4}} -{"i":62,"series":1,"stats":{"get_bytes":2566,"get_count":5,"put_bytes":563,"put_count":4}} -{"i":63,"series":1,"stats":{"get_bytes":2571,"get_count":5,"put_bytes":567,"put_count":4}} -{"i":64,"series":1,"stats":{"get_bytes":2431,"get_count":4,"put_bytes":464,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2472,"get_count":4,"put_bytes":468,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2476,"get_count":4,"put_bytes":472,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":476,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2484,"get_count":4,"put_bytes":480,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2488,"get_count":4,"put_bytes":484,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2492,"get_count":4,"put_bytes":489,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2497,"get_count":4,"put_bytes":493,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2501,"get_count":4,"put_bytes":587,"put_count":4}} -{"i":73,"series":1,"stats":{"get_bytes":2595,"get_count":5,"put_bytes":591,"put_count":4}} -{"i":74,"series":1,"stats":{"get_bytes":2599,"get_count":5,"put_bytes":595,"put_count":4}} -{"i":75,"series":1,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":599,"put_count":4}} -{"i":76,"series":1,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":603,"put_count":4}} -{"i":77,"series":1,"stats":{"get_bytes":2611,"get_count":5,"put_bytes":607,"put_count":4}} -{"i":78,"series":1,"stats":{"get_bytes":2615,"get_count":5,"put_bytes":612,"put_count":4}} -{"i":79,"series":1,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":616,"put_count":4}} -{"i":80,"series":1,"stats":{"get_bytes":2480,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":319,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2327,"get_count":4,"put_bytes":323,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2331,"get_count":4,"put_bytes":327,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2335,"get_count":4,"put_bytes":331,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2339,"get_count":4,"put_bytes":335,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2343,"get_count":4,"put_bytes":340,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":344,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2352,"get_count":4,"put_bytes":439,"put_count":4}} -{"i":89,"series":1,"stats":{"get_bytes":2447,"get_count":5,"put_bytes":443,"put_count":4}} -{"i":90,"series":1,"stats":{"get_bytes":2451,"get_count":5,"put_bytes":447,"put_count":4}} -{"i":91,"series":1,"stats":{"get_bytes":2455,"get_count":5,"put_bytes":451,"put_count":4}} -{"i":92,"series":1,"stats":{"get_bytes":2459,"get_count":5,"put_bytes":455,"put_count":4}} -{"i":93,"series":1,"stats":{"get_bytes":2463,"get_count":5,"put_bytes":459,"put_count":4}} -{"i":94,"series":1,"stats":{"get_bytes":2467,"get_count":5,"put_bytes":464,"put_count":4}} -{"i":95,"series":1,"stats":{"get_bytes":2472,"get_count":5,"put_bytes":468,"put_count":4}} -{"i":96,"series":1,"stats":{"get_bytes":2476,"get_count":5,"put_bytes":417,"put_count":4}} -{"i":97,"series":1,"stats":{"get_bytes":2425,"get_count":5,"put_bytes":421,"put_count":4}} -{"i":98,"series":1,"stats":{"get_bytes":2429,"get_count":5,"put_bytes":425,"put_count":4}} -{"i":99,"series":1,"stats":{"get_bytes":2433,"get_count":5,"put_bytes":429,"put_count":4}} -{"i":0,"series":2,"stats":{"get_bytes":2227,"get_count":3,"put_bytes":355,"put_count":3}} -{"i":1,"series":2,"stats":{"get_bytes":2363,"get_count":4,"put_bytes":359,"put_count":3}} -{"i":2,"series":2,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":363,"put_count":3}} -{"i":3,"series":2,"stats":{"get_bytes":2371,"get_count":4,"put_bytes":367,"put_count":3}} -{"i":4,"series":2,"stats":{"get_bytes":2375,"get_count":4,"put_bytes":371,"put_count":3}} -{"i":5,"series":2,"stats":{"get_bytes":2379,"get_count":4,"put_bytes":375,"put_count":3}} -{"i":6,"series":2,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":380,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":2388,"get_count":4,"put_bytes":384,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":2392,"get_count":4,"put_bytes":512,"put_count":4}} -{"i":9,"series":2,"stats":{"get_bytes":2520,"get_count":5,"put_bytes":516,"put_count":4}} -{"i":10,"series":2,"stats":{"get_bytes":2524,"get_count":5,"put_bytes":520,"put_count":4}} -{"i":11,"series":2,"stats":{"get_bytes":2528,"get_count":5,"put_bytes":524,"put_count":4}} -{"i":12,"series":2,"stats":{"get_bytes":2532,"get_count":5,"put_bytes":528,"put_count":4}} -{"i":13,"series":2,"stats":{"get_bytes":2536,"get_count":5,"put_bytes":532,"put_count":4}} -{"i":14,"series":2,"stats":{"get_bytes":2540,"get_count":5,"put_bytes":537,"put_count":4}} -{"i":15,"series":2,"stats":{"get_bytes":2545,"get_count":5,"put_bytes":541,"put_count":4}} -{"i":16,"series":2,"stats":{"get_bytes":2549,"get_count":5,"put_bytes":491,"put_count":4}} -{"i":17,"series":2,"stats":{"get_bytes":2499,"get_count":5,"put_bytes":495,"put_count":4}} -{"i":18,"series":2,"stats":{"get_bytes":2503,"get_count":5,"put_bytes":499,"put_count":4}} -{"i":19,"series":2,"stats":{"get_bytes":2507,"get_count":5,"put_bytes":503,"put_count":4}} -{"i":20,"series":2,"stats":{"get_bytes":2511,"get_count":5,"put_bytes":507,"put_count":4}} -{"i":21,"series":2,"stats":{"get_bytes":2515,"get_count":5,"put_bytes":511,"put_count":4}} -{"i":22,"series":2,"stats":{"get_bytes":2519,"get_count":5,"put_bytes":516,"put_count":4}} -{"i":23,"series":2,"stats":{"get_bytes":2524,"get_count":5,"put_bytes":520,"put_count":4}} -{"i":24,"series":2,"stats":{"get_bytes":2528,"get_count":5,"put_bytes":614,"put_count":5}} -{"i":25,"series":2,"stats":{"get_bytes":2622,"get_count":6,"put_bytes":618,"put_count":5}} -{"i":26,"series":2,"stats":{"get_bytes":2626,"get_count":6,"put_bytes":622,"put_count":5}} -{"i":27,"series":2,"stats":{"get_bytes":2630,"get_count":6,"put_bytes":626,"put_count":5}} -{"i":28,"series":2,"stats":{"get_bytes":2634,"get_count":6,"put_bytes":630,"put_count":5}} -{"i":29,"series":2,"stats":{"get_bytes":2638,"get_count":6,"put_bytes":634,"put_count":5}} -{"i":30,"series":2,"stats":{"get_bytes":2642,"get_count":6,"put_bytes":639,"put_count":5}} -{"i":31,"series":2,"stats":{"get_bytes":2647,"get_count":6,"put_bytes":643,"put_count":5}} -{"i":32,"series":2,"stats":{"get_bytes":2507,"get_count":5,"put_bytes":540,"put_count":4}} -{"i":33,"series":2,"stats":{"get_bytes":2548,"get_count":5,"put_bytes":544,"put_count":4}} -{"i":34,"series":2,"stats":{"get_bytes":2552,"get_count":5,"put_bytes":548,"put_count":4}} -{"i":35,"series":2,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":552,"put_count":4}} -{"i":36,"series":2,"stats":{"get_bytes":2560,"get_count":5,"put_bytes":556,"put_count":4}} -{"i":37,"series":2,"stats":{"get_bytes":2564,"get_count":5,"put_bytes":560,"put_count":4}} -{"i":38,"series":2,"stats":{"get_bytes":2568,"get_count":5,"put_bytes":565,"put_count":4}} -{"i":39,"series":2,"stats":{"get_bytes":2573,"get_count":5,"put_bytes":569,"put_count":4}} -{"i":40,"series":2,"stats":{"get_bytes":2577,"get_count":5,"put_bytes":663,"put_count":5}} -{"i":41,"series":2,"stats":{"get_bytes":2671,"get_count":6,"put_bytes":667,"put_count":5}} -{"i":42,"series":2,"stats":{"get_bytes":2675,"get_count":6,"put_bytes":671,"put_count":5}} -{"i":43,"series":2,"stats":{"get_bytes":2679,"get_count":6,"put_bytes":675,"put_count":5}} -{"i":44,"series":2,"stats":{"get_bytes":2683,"get_count":6,"put_bytes":679,"put_count":5}} -{"i":45,"series":2,"stats":{"get_bytes":2687,"get_count":6,"put_bytes":683,"put_count":5}} -{"i":46,"series":2,"stats":{"get_bytes":2691,"get_count":6,"put_bytes":688,"put_count":5}} -{"i":47,"series":2,"stats":{"get_bytes":2696,"get_count":6,"put_bytes":692,"put_count":5}} -{"i":48,"series":2,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":589,"put_count":4}} -{"i":49,"series":2,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":593,"put_count":4}} -{"i":50,"series":2,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":597,"put_count":4}} -{"i":51,"series":2,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":601,"put_count":4}} -{"i":52,"series":2,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":605,"put_count":4}} -{"i":53,"series":2,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":609,"put_count":4}} -{"i":54,"series":2,"stats":{"get_bytes":2617,"get_count":5,"put_bytes":614,"put_count":4}} -{"i":55,"series":2,"stats":{"get_bytes":2622,"get_count":5,"put_bytes":618,"put_count":4}} -{"i":56,"series":2,"stats":{"get_bytes":2626,"get_count":5,"put_bytes":712,"put_count":5}} -{"i":57,"series":2,"stats":{"get_bytes":2720,"get_count":6,"put_bytes":716,"put_count":5}} -{"i":58,"series":2,"stats":{"get_bytes":2724,"get_count":6,"put_bytes":720,"put_count":5}} -{"i":59,"series":2,"stats":{"get_bytes":2728,"get_count":6,"put_bytes":724,"put_count":5}} -{"i":60,"series":2,"stats":{"get_bytes":2732,"get_count":6,"put_bytes":728,"put_count":5}} -{"i":61,"series":2,"stats":{"get_bytes":2736,"get_count":6,"put_bytes":732,"put_count":5}} -{"i":62,"series":2,"stats":{"get_bytes":2740,"get_count":6,"put_bytes":737,"put_count":5}} -{"i":63,"series":2,"stats":{"get_bytes":2745,"get_count":6,"put_bytes":741,"put_count":5}} -{"i":64,"series":2,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":638,"put_count":4}} -{"i":65,"series":2,"stats":{"get_bytes":2646,"get_count":5,"put_bytes":642,"put_count":4}} -{"i":66,"series":2,"stats":{"get_bytes":2650,"get_count":5,"put_bytes":646,"put_count":4}} -{"i":67,"series":2,"stats":{"get_bytes":2654,"get_count":5,"put_bytes":650,"put_count":4}} -{"i":68,"series":2,"stats":{"get_bytes":2658,"get_count":5,"put_bytes":654,"put_count":4}} -{"i":69,"series":2,"stats":{"get_bytes":2662,"get_count":5,"put_bytes":658,"put_count":4}} -{"i":70,"series":2,"stats":{"get_bytes":2666,"get_count":5,"put_bytes":663,"put_count":4}} -{"i":71,"series":2,"stats":{"get_bytes":2671,"get_count":5,"put_bytes":667,"put_count":4}} -{"i":72,"series":2,"stats":{"get_bytes":2675,"get_count":5,"put_bytes":761,"put_count":5}} -{"i":73,"series":2,"stats":{"get_bytes":2769,"get_count":6,"put_bytes":765,"put_count":5}} -{"i":74,"series":2,"stats":{"get_bytes":2773,"get_count":6,"put_bytes":769,"put_count":5}} -{"i":75,"series":2,"stats":{"get_bytes":2777,"get_count":6,"put_bytes":773,"put_count":5}} -{"i":76,"series":2,"stats":{"get_bytes":2781,"get_count":6,"put_bytes":777,"put_count":5}} -{"i":77,"series":2,"stats":{"get_bytes":2785,"get_count":6,"put_bytes":781,"put_count":5}} -{"i":78,"series":2,"stats":{"get_bytes":2789,"get_count":6,"put_bytes":786,"put_count":5}} -{"i":79,"series":2,"stats":{"get_bytes":2794,"get_count":6,"put_bytes":790,"put_count":5}} -{"i":80,"series":2,"stats":{"get_bytes":2654,"get_count":5,"put_bytes":687,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":2695,"get_count":5,"put_bytes":691,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":2699,"get_count":5,"put_bytes":695,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":699,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":2707,"get_count":5,"put_bytes":703,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":2711,"get_count":5,"put_bytes":707,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":2715,"get_count":5,"put_bytes":712,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":2720,"get_count":5,"put_bytes":716,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":2724,"get_count":5,"put_bytes":810,"put_count":5}} -{"i":89,"series":2,"stats":{"get_bytes":2818,"get_count":6,"put_bytes":814,"put_count":5}} -{"i":90,"series":2,"stats":{"get_bytes":2822,"get_count":6,"put_bytes":818,"put_count":5}} -{"i":91,"series":2,"stats":{"get_bytes":2826,"get_count":6,"put_bytes":822,"put_count":5}} -{"i":92,"series":2,"stats":{"get_bytes":2830,"get_count":6,"put_bytes":826,"put_count":5}} -{"i":93,"series":2,"stats":{"get_bytes":2834,"get_count":6,"put_bytes":830,"put_count":5}} -{"i":94,"series":2,"stats":{"get_bytes":2838,"get_count":6,"put_bytes":835,"put_count":5}} -{"i":95,"series":2,"stats":{"get_bytes":2843,"get_count":6,"put_bytes":839,"put_count":5}} -{"i":96,"series":2,"stats":{"get_bytes":2703,"get_count":5,"put_bytes":736,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":2744,"get_count":5,"put_bytes":740,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":2748,"get_count":5,"put_bytes":744,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":2752,"get_count":5,"put_bytes":748,"put_count":4}} +{"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 index 0317912c6f5eb39f365af15710cc36ee0e8b493a..9762c18e76279071dfaa82bdba2d0ba1ed14c75f 100644 GIT binary patch literal 18027 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%0DvryB1Q9h3kaz85*R8z%@lPc@$(c{-Mz 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 VVyNyoYmx0(>yF;-@>{T{{{>k!6%_yg delta 9733 zcmcI}cT`hPyROnABA_7B!9uT6qzVWsNS78u4`NVyN2%Kgh^ThDv zYNt`WTM{NyZ0mtHQ(fSTrv}R~A~H3C9+Ei8{V}hzF!Ps-dv+U&%725fa*Pv7Un+Q9ebS4gcVsY z_N8#K5qc|6l)VQh5Qn=gGP%D;QSJpKZnM8Jq}3G+TkQ!TIDW8;#%N?~ozWI#*sjxeOzEWA1Bl<;laN*GAw{(D z8-d$!dxfxtp%BU;mG%##;KGflJ%04Sr59|wu1xo2+i0N8N0TDxq$o=N#9GsgCC-WV z*MZf8AwKo;{ZU4g>tojI`k!?7Sy_%RWxMG)rLhcb$W9wyz8y?_D{-ERrQv%Jocl-@ zPulD3AU0j#8JV7b1o6?$vny}F z?XS+$B#CrVSNzcewWn*4NG(Szm10l1-VjzNXSI2ALmK<_qKVDp>cQsUWgn>heR*M8 z(LO3vcIoANvkyi**;G^Sm5xNb)Q<5P&^=Wa@ia#xwt*2fSA*QWmZVcuo6H2os*kG z7TuIaAhandzh#DO;JQ|Rn@hye2GLO?BN*s8V64dtQ;jG`GWsa((|j#rbXEIGPz@5M zR2P(Fo#Ff~t{=G!nb9mVYaFnT*4A11N{ee6A%HL_9nA&fPjz>MjKs7@o0eE+2|Iqb zuKw+5^R|K?-Jgr3Eb|}f*|=!Hg_cY#q8cvPh`@Y;(=ebKR%_jAN;aZ8#mhqI#p1CY z0+&8Zd%{b+hx-N40tgSg=Mfe$z4Xi-bJ4}|>A4rcPEwXjO#k2LCoYN5N zzg|=yagp)ywm+9dxxQjYT>pp}+JIlK2#u4c{qS`;0(3IXAOL5nrLR^}ukK|K3&7`} zhiwH;i6<|?`Q~YcD=VCh%SP2R6=vMX=DNZOX7I#O=9TE`_agV&+&SUeDTVR z5`fzL!q7N<^@Q(dA6)7(*xeUJ;X(+mn6`P-ve z1W%)?nh?4BwhQ-V$denvSACiY$wRAdz1;`XVq`6Z&@I7Mo>IaN4sdGR-W{q7M4voN zrBVi*7aFKG>gya98pQW6Q4Mg-#3_6l4*j#7tP+6kMx(&)wP?~=n4P0>yh-ocJpFXQ z3#ShQ{s1|@vyH&~9+hNxCZ5bhBi>}2nd)s5gL5`kA~_}EO_aXPhmtcL97WD_;1^-h zk$$>)@F-5#=di%M{$c%TferEVph_~XaDGyvk9yA|$gp9p@R`Bj68pd@pfoe$1?Zk< z3}U!0Y9G5$%2r-?6ShGS>Ll#=fI9K_97OiQ2_!h zY7KTNcE2S@4(y0Q2gl>B0eKaI4X~(ZhKD1ImXm5;qqHbH1N2grrr=S}eg|;5-|?^Bjo=_X!H7eW>{;}m5;$NBRXruNL8el>mWYC(4C27;xl@D zbns|YY=YFo9HR2ALq|4fu2o{hc{{2RQ#sw_wqN;k-$s~iP`BCyN$cL_TUlS+{KrFDr)zLqjm3a@a)$P-=hAAtuzrv zNY#8P8i|e20W~qdj|i;w@IuZ(C8wefy)mqO|xl zRMpY}vnV1-DF(*{Q?p6K#pZ`QsFKwTzBiE{PO3$I@Xf~{J~rb_?y9#2wQ2aD%Tv1o zWJlu1`?5JDW%LLsx9Jec&>6WEo%L?;B;`2GBRS@KMm8c6;4`G zj$wlRb{9N@s>rf0l&|Rp9#YWRt;ix-)(Cmqw)aM=&^vo@J*o>0K53ZB#VLkC504#B z%fIgpY%BsUf<1+bWW_UaRL};s%s}4*_yh>`OlE3mOMZQOU1klTGSzTBn_H%tiKXXMAvIXS2R7-~HNecuL{eg@ z{X*2DWbHf=voEI+bNZ@=xdWWu1*k5%ddtR%q{G`zF&shEp`e>&mqRyIYC4&yrpP}C21{s) zE&@B=E4522_Uir&W?*={ver2ZRv4Xzn0o@bS9!p-CROl*ul)|V1;{q0C3OONN_-U# z*hG+eEe74{bWIEUTDYJzwQ76#HH|`ux|>1r1bsRX0;1*q0i@v=fytAYM@NcDCK<3?dvOQbV7xOd*voolX>U zr_^|sysE{eraLzhD6V~ zoA6xuE07Bu`SIH9HE>cXMSN zsG#rNa>l%(^g~i&&bsq-WWF}!>d}Jq1k(r$#@u7O0)cMU$=Y32;x6AW(|V8P1o;qJ zlkB$u)}W2HnUUBz@-9s!;|tG?zsP10&&UP^h|*BoZ#S3(%6F^9Trx@3zs@yU!0vkN zMqre<^UJH84N%&vzl+U=4ZF%OoL-A=dBqKruwFhLFn2f5ElU9&Ne%?AvI|(|NJCqa zqkve!fU&b;axorGMfhJe1aoM?^z>kXiECl0#b3$^4(akaS=(K@qX`&Xb@ED8n0ewO z(0ALxvJm6lRSZciqr)eH1yI@-ZX*xD%G!5HrU)`9FeT`uN0i(zxeIdtP*c`5QIPv1 z7BD+q9EJJ>_*BL(FdH2t62(Hr4lzEzp@H3bH!g+ysrTx1+lci-Ug2mz|zqQ$`PDr36@dYDX37xz>ObUc%ge zk8odSs4IPFway;Io5(KWHD>HMS=EDj7^8FV?od=4$X>ZFWh*pn5V2o-qw?80E+Wlb zIfeSDyY6=tsSXZ0&pol3LP_+bmUR{?vbFak7ZZR~Nonna_pc0!S;ZAQ1ai+%(1oxo zq1Or(13H5;6vCw|E~D>*tO)x9WK!UXO9MW863^S8*y{OdsBV9fn$ByIEtWUlCo)pI-DMRNxxdSxC@m8l0M#V4AF;e#rfFWy--#tBYiLC9a z?dO(XNsNa`&1LSas*_1N|NkC{8`}K~Gq5-i+TaVVzei5#B&w{Ni2b-;_<|Y*0`{;7 zud;BlO!4z;-cw?3&Ei32JJXJ|s1hnx|9bxA;sk4|Vf0_!2uO^Mjd=})zS1Y4a5*O} zq4~fR)noE4`1Mo$PhOE+_pf!b?J%c@Cl9zMe-G*E_Mxg$m5$}Cx2|`_vY!DtJ>}!J zBK!_C$I9bqF=UfD0RN;+OVG8%WJ*=9lYX7zgjfBCu3tCO8w?H0RNLQyRYHFmU`xKP z?m2N_wXmyjzu?2h>r30eujV-nH)h|VUwyO}-%{SuS3I?L+p)XR&exv-c=}WQ zW^vo0&v~wW(gnqBMho}WE5uDh9lKkRX9z#ylDVp;j&8Ys2|g5^1UPQ(EYCMFlL+6f znxDT4IDXkXyB~YDWjo>nF4ZS)o@bhU*-W{1`rM2ehtZ*O1>I%xvR_CuIKmh2o4q45 zPTxKxG!J2qB$Xb$CWId_PfsTSF>~|pJlG}wXG}kfD-NgmZW&__}UoNN3W6EFK^g9T8?5CUQ3l5!?hVo^k4?X@f>7* z_aF(4W}aV!k6lpMo%(H7gGmk%hrtTf_oauhDvz`SoJfwo51AKmuo2ej+t@4)pxt;Q9MZ=YgF-HW*c zfnKP+$4801!?n?2vA8^rWn`fnOn8BUjppvk76cXJ1TNIF;%=xwC)*0jEG zBmzKAgQ|aWZN`m_-+*rb`{o3Y*< zsPvS$R?S7Iq_M-)VV=))QS4dO$)3)VU)A-QJqKGJZ*Vn%2i&6`YST2@Kluzle`C4&{ORaHg+0~4?d&?#s_{-8u3Si&_V?v*8P*bauq@$ay7MjW?LbZO6XK#-S z1h7XRB6#PLml9~-th^SN=&^9eUzSKKk-fC0Id%L{}7E?B%}mIa0Abq3VV&;U(A<2xOqkU`UV&X<_fh?%4F`Rb7P zd!k1cJe4Ag29u2m+39o846{9AfRk>Yp=I)~>a3>pD-YGsJbh@CH1@Oo)nvSR6mLiI z9Hd2``^2lunxDB8%Vwl}p!I2I3H4kWeyS{XvXQ-O0M7VA53ZnD|5J*>o)O%}HN!Zy z$)QCxEsJqgv@r6k=*-4FuI>`Ao|pty;YVw7mZ80i{pd9%x-?!3H>RS5w4KNxE!yRt zFAyY4>*w?G(nlibPh2pkozFCvJe^l%DGIx}WO3Pxxz`(CoSa{Z9q@5ir2RO;+B3^S z>4Gl}126-4IGi5Eb%W?0*$|7X20wS24QRD&s8MgVpPmCOxbJnQ3^(NXd zZRq8Ik%kX1%#l1D4SyElR-dsXBx`QC;ooLo7ze!(eq_JQhVE+I-Xc%=?{piI0$o?P%6$dHACD9H{D`c6&H^&UopV%0~Y&JD2KGAfrPt3ftMSQ;5!p+)(g&F{>skQUc>UrBVmt&&Ef4#)61nznFxpp%8ESx z?{KxH685C5rkA|q8Jer&BKmfPX9GFNq>Me)?odPL){C=+rh%SFOMyPH0y z)@<@nK`r%09X|0Nvr=LgGjx33DI7Nh$nWUVi&YSVvM0Sl?+fz&F*o+B7A;QpEzNL- zRIT!)iAG_6*Ju(&*@h(!@c?RzN958ZHSaoR@Q!|ka6dF--*rd|fh9K_0AIJLpsDs9 zE8b3vOYBL8J9JILG9~lu!$9k|mGn&W^2Hep;ZPqgu5HExgFw@Q3PCa91sRSVRTN@t z0rwy=wzq+iPNAZe7NvVPzACpj5kzVcyGf`L6|X#1`J;eR#E2HHNsUb(mvRq?&L)x& zdMDKk|EkmsUb1){KoPx*@xeUM!2YSsNIw;y4cTS;lgmS1x<`*Ka&5ItW39qf%vsrt zYQ-0^0lA%W>_hWZWz%aE4r?C@QK!}VQlO3Hf%R5x(25~y8vMtkz@;te*1_3LM9nF& z8{Y*f*P|M-*JlG;Q@7kDeIhXww?bsmJ9dl}A)%k}p)!-er0o&|lKMF@7_QRPsMq{P zkEl%jEqKx=xVh$6?u;bv$T6;J%9ci%bHSDE5#Nv zT;Wc>bpf{9$a)sXGO4yS>ZS7c>~reB$6rK;1_Y%7$EsQ8R2MF;;||agj9U_e?$7WZ zd!$FdM8U>2LyGmEThcAh3`ZjqO|-Wm!`tqXZD;7%rn0Gqe@!B~cl??0LiUzd$1$g| z<%gkKK}n(y;qt4fEO9xMc{2|t-vBaW{-^6Xspt<`0r z6$5h0^>vT>L+j=f`hebeW5m-j(`XQ-ZkB72du^GD6Gz5U+^F>LrT4*@I}aMGRr7AS ziYDoo#WBI2yoDeW^>-((j4Q6L<+68B-ctF&XRQ)WIo=$aY6F4(`o%)o(&a8np1yc6 zz}a5$U79Z92OpJXy<+*S{lToQiVq;}t{jBmQl3OUV=&;FbW__98lfr343R)doWDax z=Ep+C#?8oSGK~M7asMY!!2bZI{+atfGd1u(1MB~^3c&xxsrY-k^#sZ7h2aIxb6}8J zt(*z66&=t5T~*RD3LxNU!LaCxBVI|T4u2{eWGz1I5Gl0H(jo&Q*|Lj+#c{e>o2>|Y zI~{x8miq|^rsf~UlC?kY&QXc#R~};PZ-OX%SOU2R(G@RQAt-xoH~DlyxnPKAG6 ztuuQ#%11@~xh9Y+XClxjm+7UmT5a~w?(al_mTix=-71>)w=-PHV77G?J|fm@UwLmS z3`j`=Rpi3dkwn~a``A(NDjLbsT}FBKj|p9Co=-zyW4*<4E9Q`kAebuLnJ(ms4g%?H zP}bfy3ZlBM%AX5{J!TX%FyzjgHHtiU%e9*MUbjCt4G-jl%PT`Id2jH8vg1h&>vdPK zh2iS87G<`2A_0L~HU@H4N<9hW%&}T~Hy@Lbel2+4VE;?~a^I&N#kI{+@Bdr3mX?qCDd+9%BcD@I2l_CR4hoy7&%E z2iu|Sri*^MKP4K9o00anrRI<29JlV?NOJLMZsIJ_}C z+-^N4^{h=_?;oKAj_?69^^m3hfPSxH)=&b8S;@wi?VVKT8JE`(5^)`esbZYS8ulFY zihaC4Iau~Zh$YmAW3$J}b)$`A;h%LRvlWQrCL&skub@i=nfG$6rw`|aX4x1I@gvIs z@&RvMQ?OHSfxtS7Kx)@XEwoitONw^6@xAPkONz-R0sAy^u{h^x=&xrH6}TgFB5V=vI1zkoX}P)b8hD z0;4|sWXR*fLJbwW-45N2XJ?_d++k#02aKWO%I^g_8gHrHB4>cJaQ>(Jn+5f<%Mh}E zJ|tBt*=YYR8;4{w{WZ1ec+N+&<1DP|oY0Mv@lOO8fvNl8m6-*Dh}4K+iNwXVnnIpE z-mO1(;fLy1C3Z$0kUgibtFomw@y+D0Eqn;#LY}&)kY}IQoxru_Uve2qUH!qWgqO%z zo3TUa3ot0vyPKXzt`=_KZUWR@d(dcix+u&q-qP&V-<*gfwUxVltaLInrU2;9;)T$a zQAOj+1LwYPCin&%2Bth6JDKOKt1<|{eyyHb^Dnk@96_1|2X8*9@E+0W+vVL_@lCUY zF_bOou*=aRq3(t*kADDlCWh88(|c77j7C=(WPN?-R@{$gZR=$(Zm#>-`(@tH038H= zPaoY>J0v?Juh25D;fYWcK@diEgpI~6_G{&8uEsVr+z3;eBTSCGc%+@D(plI|2BrQ| z>6I}X>?|v)`Tux-bP9FpFN57XN3l*HZ!RSS9A%m4q*mX6(<`prDC^3p3QKA;Y4YhU zj!4H)TwIGxYTLf6GL!$XKDndcJl+Rr=D=9=dvBk@6$~BY15XI5WB(j+E@GSu%U7N+ z+H%BQG4w(?Jy*X{UI@wDf66v%WOMeI)LeSt(r~O@sGhly^QiUrPBZz2CSm5>D0>S10uc9ppNK)0r$Pt$;<;e+a3!>$0qqxiJB?F4NH zTKd4_my%Of&#RE84$KQQsQrQ0T=g;h3UB+4e-}>0l_Sla^hVY3)wX9jt}({P)aV{F zJm$atkQ5O|(q2>#JIA?8Vzq0ky$T$=cL1kvL3-5W32y`^;r(X|AoM2IMziX=@Ti!v z`~7_Qw<|gPXg)}0XIjES&bZy7K(n8}L^k)_{pE7PvVfZR;mF;`{nfwb!XeR4i4?m2 zcM(gI`k?BF=mAZVU?QiPZ@=(y0_tb2T4xk1m+!&L>4Mr`_g-N7BD!4*$eQc3d)%LM zkM#>f+B0n=e`AqS_rXs-ExeAEM+8KFNAvYYj%G;!Ytl>(u3A6fqk3)YZ~egajnA|w z?dw)dti^13VMUpjcaVu4J*=Y`>76ivRS3WG!>QGG5&1&}Hgjnh4(!$RaKdAQu%X0B zfjZcxB)uH1j9_D_D&*x)ASZbMz2m~snP@Yp;3iWUR;m4CLP&WsIlnD0Y{{i@&LeRl z$5D~d$@wl!w6XL{;;K+cy>Jr%#TWTsJ!;br@#&d3~o5>@6isxQ}L=jz#hx#!D-4^Cc;> z?~_=wnYFB5w*+65t&?lJRzK60u9>{FfMIHRUeqzfLod-IJa}nVJ>5Eu894lirEkE! n>?>6K=Ug7I$a{uuMvTNbJ`}m<8UJLQ+@XEj;MO}0_|yLY__y~0 diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline index 0466b2203..2a9ce7132 100644 --- a/actors/evm/tests/measurements/array_read_n100.jsonline +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":5810,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":5558,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":5565,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":7148,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":5572,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":7544,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":6900,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":5324,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":5917,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":4341,"get_count":13,"put_bytes":0,"put_count":0}} +{"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 index 5be6a77250f0e84fc0de846fcfa701deeda111d8..23c0d00261b562e530d80710809ce0e612492818 100644 GIT binary patch delta 6721 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 a747qv=1E_3oXq@ldD6XOq*Zbo{`@~>7fWUU delta 6713 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 SE#CMN%Q_GAHOuZpU;YpDie>iz diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index d117a64d0..703a6aa6e 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2229,"get_count":3,"put_bytes":316,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":2,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":3,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":4,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":5,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":6,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":8,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2322,"get_count":4,"put_bytes":314,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":30,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":31,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":32,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":45,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":57,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":58,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":61,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":62,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":63,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":74,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":315,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":2482,"get_count":3,"put_bytes":482,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2769,"get_count":3,"put_bytes":761,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2981,"get_count":3,"put_bytes":973,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3212,"get_count":3,"put_bytes":1204,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":3309,"get_count":3,"put_bytes":1301,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":3481,"get_count":3,"put_bytes":1473,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":3615,"get_count":4,"put_bytes":1607,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":3680,"get_count":4,"put_bytes":1672,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":3712,"get_count":4,"put_bytes":1704,"put_count":3}} -{"i":9,"series":2,"stats":{"get_bytes":3728,"get_count":4,"put_bytes":1720,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":3744,"get_count":4,"put_bytes":1736,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":3784,"get_count":4,"put_bytes":1776,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":3784,"get_count":4,"put_bytes":1776,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":3784,"get_count":4,"put_bytes":1776,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":3792,"get_count":4,"put_bytes":1784,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":3792,"get_count":4,"put_bytes":1784,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1877,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1877,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1877,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":28,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":3926,"get_count":4,"put_bytes":1918,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":38,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":43,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":3967,"get_count":4,"put_bytes":1959,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":52,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":55,"series":2,"stats":{"get_bytes":4008,"get_count":4,"put_bytes":2000,"put_count":3}} -{"i":56,"series":2,"stats":{"get_bytes":4049,"get_count":4,"put_bytes":2041,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4090,"get_count":4,"put_bytes":2082,"put_count":3}} -{"i":58,"series":2,"stats":{"get_bytes":4090,"get_count":4,"put_bytes":2082,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4090,"get_count":4,"put_bytes":2082,"put_count":3}} -{"i":60,"series":2,"stats":{"get_bytes":4090,"get_count":4,"put_bytes":2082,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":63,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":64,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":65,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":67,"series":2,"stats":{"get_bytes":4131,"get_count":4,"put_bytes":2123,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4172,"get_count":4,"put_bytes":2164,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2213,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2213,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2213,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2213,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4221,"get_count":4,"put_bytes":2213,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4229,"get_count":4,"put_bytes":2221,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4270,"get_count":4,"put_bytes":2262,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4270,"get_count":4,"put_bytes":2262,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4270,"get_count":4,"put_bytes":2262,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4278,"get_count":4,"put_bytes":2270,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4278,"get_count":4,"put_bytes":2270,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4371,"get_count":5,"put_bytes":2363,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":4371,"get_count":5,"put_bytes":2363,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":4412,"get_count":5,"put_bytes":2404,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":4412,"get_count":5,"put_bytes":2404,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":4412,"get_count":5,"put_bytes":2404,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":4412,"get_count":5,"put_bytes":2404,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":4412,"get_count":5,"put_bytes":2404,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":4453,"get_count":5,"put_bytes":2445,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":4453,"get_count":5,"put_bytes":2445,"put_count":4}} -{"i":89,"series":2,"stats":{"get_bytes":4453,"get_count":5,"put_bytes":2445,"put_count":4}} -{"i":90,"series":2,"stats":{"get_bytes":4453,"get_count":5,"put_bytes":2445,"put_count":4}} -{"i":91,"series":2,"stats":{"get_bytes":4494,"get_count":5,"put_bytes":2486,"put_count":4}} -{"i":92,"series":2,"stats":{"get_bytes":4494,"get_count":5,"put_bytes":2486,"put_count":4}} -{"i":93,"series":2,"stats":{"get_bytes":4494,"get_count":5,"put_bytes":2486,"put_count":4}} -{"i":94,"series":2,"stats":{"get_bytes":4494,"get_count":5,"put_bytes":2486,"put_count":4}} -{"i":95,"series":2,"stats":{"get_bytes":4494,"get_count":5,"put_bytes":2486,"put_count":4}} -{"i":96,"series":2,"stats":{"get_bytes":4534,"get_count":5,"put_bytes":2526,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":4534,"get_count":5,"put_bytes":2526,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":4582,"get_count":5,"put_bytes":2574,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":4590,"get_count":5,"put_bytes":2582,"put_count":4}} +{"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 index 202f378c2f94a1b053ee2fd7bb3d220082371b00..ad6267388844b5f129d28800d8d40ad90af9fd5e 100644 GIT binary patch delta 10761 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 JKppkye*j&RTD$-N diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index 7b3c508dd..1bf5929c9 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2269,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2143,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"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 index de6cd86c625153774c916e12ee513107d102017d..b845f568af6007cd947cb06b0fecaa7f4ed2a12c 100644 GIT binary patch literal 14371 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 OBkXMUS^sM3pY?BP@Jy5d literal 20987 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 Mt(CoHiG>&CUvJAn%m4rY diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index fa60e24c9..82ead2085 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2134,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":6059,"put_count":35}} -{"i":2,"series":1,"stats":{"get_bytes":7282,"get_count":28,"put_bytes":10271,"put_count":43}} -{"i":3,"series":1,"stats":{"get_bytes":10592,"get_count":32,"put_bytes":13517,"put_count":46}} -{"i":4,"series":1,"stats":{"get_bytes":14965,"get_count":37,"put_bytes":18822,"put_count":68}} -{"i":5,"series":1,"stats":{"get_bytes":17977,"get_count":43,"put_bytes":21287,"put_count":64}} -{"i":6,"series":1,"stats":{"get_bytes":19376,"get_count":38,"put_bytes":23461,"put_count":73}} -{"i":7,"series":1,"stats":{"get_bytes":23549,"get_count":47,"put_bytes":27579,"put_count":81}} -{"i":8,"series":1,"stats":{"get_bytes":27385,"get_count":48,"put_bytes":31406,"put_count":82}} -{"i":9,"series":1,"stats":{"get_bytes":29030,"get_count":55,"put_bytes":33209,"put_count":93}} -{"i":10,"series":1,"stats":{"get_bytes":31809,"get_count":49,"put_bytes":35728,"put_count":81}} -{"i":11,"series":1,"stats":{"get_bytes":34524,"get_count":54,"put_bytes":38862,"put_count":94}} -{"i":12,"series":1,"stats":{"get_bytes":37024,"get_count":70,"put_bytes":41207,"put_count":108}} -{"i":13,"series":1,"stats":{"get_bytes":36349,"get_count":68,"put_bytes":40355,"put_count":102}} -{"i":14,"series":1,"stats":{"get_bytes":39934,"get_count":67,"put_bytes":44368,"put_count":109}} -{"i":15,"series":1,"stats":{"get_bytes":40959,"get_count":71,"put_bytes":45103,"put_count":107}} -{"i":16,"series":1,"stats":{"get_bytes":42959,"get_count":72,"put_bytes":47345,"put_count":113}} -{"i":17,"series":1,"stats":{"get_bytes":43707,"get_count":74,"put_bytes":48073,"put_count":114}} -{"i":18,"series":1,"stats":{"get_bytes":42205,"get_count":75,"put_bytes":46485,"put_count":114}} -{"i":19,"series":1,"stats":{"get_bytes":50340,"get_count":92,"put_bytes":54039,"put_count":120}} -{"i":20,"series":1,"stats":{"get_bytes":45837,"get_count":81,"put_bytes":49764,"put_count":113}} -{"i":21,"series":1,"stats":{"get_bytes":47965,"get_count":80,"put_bytes":52393,"put_count":121}} -{"i":22,"series":1,"stats":{"get_bytes":48089,"get_count":83,"put_bytes":52126,"put_count":117}} -{"i":23,"series":1,"stats":{"get_bytes":55277,"get_count":108,"put_bytes":58095,"put_count":121}} -{"i":24,"series":1,"stats":{"get_bytes":54445,"get_count":104,"put_bytes":57787,"put_count":126}} -{"i":25,"series":1,"stats":{"get_bytes":54687,"get_count":102,"put_bytes":58422,"put_count":131}} -{"i":26,"series":1,"stats":{"get_bytes":53703,"get_count":94,"put_bytes":57798,"put_count":129}} -{"i":27,"series":1,"stats":{"get_bytes":56313,"get_count":107,"put_bytes":59471,"put_count":125}} -{"i":28,"series":1,"stats":{"get_bytes":56493,"get_count":106,"put_bytes":60200,"put_count":134}} -{"i":29,"series":1,"stats":{"get_bytes":56898,"get_count":106,"put_bytes":60003,"put_count":123}} -{"i":30,"series":1,"stats":{"get_bytes":61064,"get_count":113,"put_bytes":64690,"put_count":140}} -{"i":31,"series":1,"stats":{"get_bytes":56141,"get_count":107,"put_bytes":59588,"put_count":131}} -{"i":32,"series":1,"stats":{"get_bytes":61820,"get_count":111,"put_bytes":65267,"put_count":135}} -{"i":33,"series":1,"stats":{"get_bytes":59943,"get_count":114,"put_bytes":63552,"put_count":141}} -{"i":34,"series":1,"stats":{"get_bytes":61479,"get_count":113,"put_bytes":64497,"put_count":130}} -{"i":35,"series":1,"stats":{"get_bytes":61304,"get_count":116,"put_bytes":64224,"put_count":131}} -{"i":36,"series":1,"stats":{"get_bytes":62205,"get_count":113,"put_bytes":65210,"put_count":129}} -{"i":37,"series":1,"stats":{"get_bytes":63059,"get_count":117,"put_bytes":66386,"put_count":138}} -{"i":38,"series":1,"stats":{"get_bytes":64628,"get_count":120,"put_bytes":68083,"put_count":144}} -{"i":39,"series":1,"stats":{"get_bytes":60348,"get_count":115,"put_bytes":63342,"put_count":130}} -{"i":40,"series":1,"stats":{"get_bytes":64881,"get_count":121,"put_bytes":67750,"put_count":134}} -{"i":41,"series":1,"stats":{"get_bytes":64290,"get_count":118,"put_bytes":67341,"put_count":134}} -{"i":42,"series":1,"stats":{"get_bytes":66194,"get_count":118,"put_bytes":69621,"put_count":141}} -{"i":43,"series":1,"stats":{"get_bytes":64767,"get_count":121,"put_bytes":67779,"put_count":137}} -{"i":44,"series":1,"stats":{"get_bytes":69588,"get_count":127,"put_bytes":72362,"put_count":138}} -{"i":45,"series":1,"stats":{"get_bytes":67390,"get_count":123,"put_bytes":70665,"put_count":144}} -{"i":46,"series":1,"stats":{"get_bytes":69075,"get_count":128,"put_bytes":72275,"put_count":147}} -{"i":47,"series":1,"stats":{"get_bytes":69169,"get_count":128,"put_bytes":72266,"put_count":145}} -{"i":48,"series":1,"stats":{"get_bytes":71822,"get_count":125,"put_bytes":74761,"put_count":139}} -{"i":49,"series":1,"stats":{"get_bytes":70013,"get_count":127,"put_bytes":73164,"put_count":145}} -{"i":50,"series":1,"stats":{"get_bytes":67056,"get_count":117,"put_bytes":69983,"put_count":131}} -{"i":51,"series":1,"stats":{"get_bytes":69735,"get_count":119,"put_bytes":72610,"put_count":132}} -{"i":52,"series":1,"stats":{"get_bytes":70906,"get_count":128,"put_bytes":74050,"put_count":146}} -{"i":53,"series":1,"stats":{"get_bytes":67923,"get_count":122,"put_bytes":70628,"put_count":132}} -{"i":54,"series":1,"stats":{"get_bytes":72422,"get_count":130,"put_bytes":75467,"put_count":146}} -{"i":55,"series":1,"stats":{"get_bytes":70213,"get_count":120,"put_bytes":73048,"put_count":133}} -{"i":56,"series":1,"stats":{"get_bytes":70600,"get_count":127,"put_bytes":73586,"put_count":142}} -{"i":57,"series":1,"stats":{"get_bytes":74392,"get_count":132,"put_bytes":77130,"put_count":143}} -{"i":58,"series":1,"stats":{"get_bytes":71984,"get_count":129,"put_bytes":74939,"put_count":144}} -{"i":59,"series":1,"stats":{"get_bytes":73941,"get_count":135,"put_bytes":76533,"put_count":143}} -{"i":60,"series":1,"stats":{"get_bytes":72541,"get_count":129,"put_bytes":75580,"put_count":145}} -{"i":61,"series":1,"stats":{"get_bytes":76572,"get_count":131,"put_bytes":79500,"put_count":145}} -{"i":62,"series":1,"stats":{"get_bytes":70675,"get_count":127,"put_bytes":73448,"put_count":138}} -{"i":63,"series":1,"stats":{"get_bytes":77306,"get_count":134,"put_bytes":80243,"put_count":150}} -{"i":64,"series":1,"stats":{"get_bytes":77142,"get_count":131,"put_bytes":80132,"put_count":146}} -{"i":65,"series":1,"stats":{"get_bytes":71319,"get_count":126,"put_bytes":74467,"put_count":144}} -{"i":66,"series":1,"stats":{"get_bytes":76162,"get_count":128,"put_bytes":79363,"put_count":147}} -{"i":67,"series":1,"stats":{"get_bytes":76992,"get_count":134,"put_bytes":79756,"put_count":145}} -{"i":68,"series":1,"stats":{"get_bytes":78036,"get_count":130,"put_bytes":80965,"put_count":144}} -{"i":69,"series":1,"stats":{"get_bytes":77909,"get_count":131,"put_bytes":80941,"put_count":147}} -{"i":70,"series":1,"stats":{"get_bytes":79204,"get_count":131,"put_bytes":82341,"put_count":149}} -{"i":71,"series":1,"stats":{"get_bytes":76227,"get_count":126,"put_bytes":79429,"put_count":145}} -{"i":72,"series":1,"stats":{"get_bytes":76114,"get_count":131,"put_bytes":79148,"put_count":147}} -{"i":73,"series":1,"stats":{"get_bytes":77667,"get_count":132,"put_bytes":80367,"put_count":142}} -{"i":74,"series":1,"stats":{"get_bytes":74918,"get_count":127,"put_bytes":78011,"put_count":144}} -{"i":75,"series":1,"stats":{"get_bytes":81086,"get_count":134,"put_bytes":84235,"put_count":152}} -{"i":76,"series":1,"stats":{"get_bytes":79892,"get_count":131,"put_bytes":82929,"put_count":147}} -{"i":77,"series":1,"stats":{"get_bytes":77938,"get_count":132,"put_bytes":80533,"put_count":140}} -{"i":78,"series":1,"stats":{"get_bytes":79481,"get_count":130,"put_bytes":82349,"put_count":143}} -{"i":79,"series":1,"stats":{"get_bytes":79364,"get_count":132,"put_bytes":82785,"put_count":155}} -{"i":80,"series":1,"stats":{"get_bytes":80127,"get_count":133,"put_bytes":83269,"put_count":151}} -{"i":81,"series":1,"stats":{"get_bytes":82896,"get_count":138,"put_bytes":86039,"put_count":156}} -{"i":82,"series":1,"stats":{"get_bytes":79516,"get_count":127,"put_bytes":82994,"put_count":151}} -{"i":83,"series":1,"stats":{"get_bytes":77208,"get_count":123,"put_bytes":80133,"put_count":137}} -{"i":84,"series":1,"stats":{"get_bytes":85058,"get_count":134,"put_bytes":88247,"put_count":153}} -{"i":85,"series":1,"stats":{"get_bytes":81271,"get_count":129,"put_bytes":84138,"put_count":142}} -{"i":86,"series":1,"stats":{"get_bytes":83145,"get_count":131,"put_bytes":86073,"put_count":145}} -{"i":87,"series":1,"stats":{"get_bytes":81449,"get_count":129,"put_bytes":84594,"put_count":147}} -{"i":88,"series":1,"stats":{"get_bytes":82196,"get_count":131,"put_bytes":85665,"put_count":155}} -{"i":89,"series":1,"stats":{"get_bytes":85098,"get_count":133,"put_bytes":88621,"put_count":158}} -{"i":90,"series":1,"stats":{"get_bytes":87905,"get_count":135,"put_bytes":91270,"put_count":157}} -{"i":91,"series":1,"stats":{"get_bytes":82797,"get_count":135,"put_bytes":85996,"put_count":154}} -{"i":92,"series":1,"stats":{"get_bytes":87779,"get_count":132,"put_bytes":91254,"put_count":156}} -{"i":93,"series":1,"stats":{"get_bytes":83264,"get_count":130,"put_bytes":87177,"put_count":162}} -{"i":94,"series":1,"stats":{"get_bytes":85490,"get_count":130,"put_bytes":88736,"put_count":150}} -{"i":95,"series":1,"stats":{"get_bytes":85700,"get_count":132,"put_bytes":89180,"put_count":156}} -{"i":96,"series":1,"stats":{"get_bytes":83630,"get_count":134,"put_bytes":86498,"put_count":147}} -{"i":97,"series":1,"stats":{"get_bytes":84325,"get_count":135,"put_bytes":87697,"put_count":157}} -{"i":98,"series":1,"stats":{"get_bytes":83690,"get_count":133,"put_bytes":86883,"put_count":152}} -{"i":99,"series":1,"stats":{"get_bytes":86498,"get_count":135,"put_bytes":89589,"put_count":152}} -{"i":0,"series":2,"stats":{"get_bytes":88973,"get_count":140,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":86991,"get_count":131,"put_bytes":90297,"put_count":152}} -{"i":2,"series":2,"stats":{"get_bytes":86667,"get_count":132,"put_bytes":90249,"put_count":158}} -{"i":3,"series":2,"stats":{"get_bytes":88119,"get_count":133,"put_bytes":91763,"put_count":160}} -{"i":4,"series":2,"stats":{"get_bytes":83224,"get_count":130,"put_bytes":86748,"put_count":155}} -{"i":5,"series":2,"stats":{"get_bytes":88113,"get_count":131,"put_bytes":91754,"put_count":158}} -{"i":6,"series":2,"stats":{"get_bytes":90632,"get_count":133,"put_bytes":94167,"put_count":158}} -{"i":7,"series":2,"stats":{"get_bytes":90522,"get_count":134,"put_bytes":94169,"put_count":161}} -{"i":8,"series":2,"stats":{"get_bytes":92697,"get_count":138,"put_bytes":96226,"put_count":163}} -{"i":9,"series":2,"stats":{"get_bytes":91637,"get_count":134,"put_bytes":95058,"put_count":157}} -{"i":10,"series":2,"stats":{"get_bytes":88288,"get_count":131,"put_bytes":91708,"put_count":154}} -{"i":11,"series":2,"stats":{"get_bytes":91071,"get_count":137,"put_bytes":94324,"put_count":157}} -{"i":12,"series":2,"stats":{"get_bytes":88847,"get_count":130,"put_bytes":92307,"put_count":154}} -{"i":13,"series":2,"stats":{"get_bytes":88802,"get_count":132,"put_bytes":92279,"put_count":156}} -{"i":14,"series":2,"stats":{"get_bytes":92333,"get_count":135,"put_bytes":95957,"put_count":162}} -{"i":15,"series":2,"stats":{"get_bytes":82488,"get_count":127,"put_bytes":85574,"put_count":144}} -{"i":16,"series":2,"stats":{"get_bytes":92260,"get_count":134,"put_bytes":95630,"put_count":156}} -{"i":17,"series":2,"stats":{"get_bytes":93132,"get_count":136,"put_bytes":96383,"put_count":156}} -{"i":18,"series":2,"stats":{"get_bytes":93814,"get_count":135,"put_bytes":97513,"put_count":163}} -{"i":19,"series":2,"stats":{"get_bytes":90552,"get_count":129,"put_bytes":93590,"put_count":145}} -{"i":20,"series":2,"stats":{"get_bytes":91607,"get_count":132,"put_bytes":95079,"put_count":156}} -{"i":21,"series":2,"stats":{"get_bytes":97157,"get_count":139,"put_bytes":100745,"put_count":165}} -{"i":22,"series":2,"stats":{"get_bytes":93937,"get_count":135,"put_bytes":97466,"put_count":160}} -{"i":23,"series":2,"stats":{"get_bytes":92035,"get_count":131,"put_bytes":95401,"put_count":153}} -{"i":24,"series":2,"stats":{"get_bytes":93463,"get_count":131,"put_bytes":97039,"put_count":157}} -{"i":25,"series":2,"stats":{"get_bytes":94600,"get_count":129,"put_bytes":98040,"put_count":153}} -{"i":26,"series":2,"stats":{"get_bytes":96794,"get_count":139,"put_bytes":99999,"put_count":158}} -{"i":27,"series":2,"stats":{"get_bytes":97667,"get_count":138,"put_bytes":101048,"put_count":161}} -{"i":28,"series":2,"stats":{"get_bytes":96496,"get_count":135,"put_bytes":100291,"put_count":165}} -{"i":29,"series":2,"stats":{"get_bytes":90707,"get_count":133,"put_bytes":93913,"put_count":152}} -{"i":30,"series":2,"stats":{"get_bytes":94478,"get_count":136,"put_bytes":98173,"put_count":164}} -{"i":31,"series":2,"stats":{"get_bytes":97256,"get_count":137,"put_bytes":101003,"put_count":166}} -{"i":32,"series":2,"stats":{"get_bytes":97930,"get_count":139,"put_bytes":101899,"put_count":172}} -{"i":33,"series":2,"stats":{"get_bytes":92900,"get_count":131,"put_bytes":96677,"put_count":161}} -{"i":34,"series":2,"stats":{"get_bytes":97465,"get_count":136,"put_bytes":101043,"put_count":162}} -{"i":35,"series":2,"stats":{"get_bytes":98818,"get_count":133,"put_bytes":102423,"put_count":160}} -{"i":36,"series":2,"stats":{"get_bytes":98278,"get_count":134,"put_bytes":102415,"put_count":170}} -{"i":37,"series":2,"stats":{"get_bytes":97044,"get_count":132,"put_bytes":100576,"put_count":157}} -{"i":38,"series":2,"stats":{"get_bytes":94969,"get_count":131,"put_bytes":99060,"put_count":166}} -{"i":39,"series":2,"stats":{"get_bytes":93266,"get_count":131,"put_bytes":96512,"put_count":151}} -{"i":40,"series":2,"stats":{"get_bytes":93837,"get_count":131,"put_bytes":97471,"put_count":158}} -{"i":41,"series":2,"stats":{"get_bytes":98112,"get_count":143,"put_bytes":101585,"put_count":167}} -{"i":42,"series":2,"stats":{"get_bytes":93678,"get_count":127,"put_bytes":97484,"put_count":157}} -{"i":43,"series":2,"stats":{"get_bytes":98980,"get_count":131,"put_bytes":102225,"put_count":151}} -{"i":44,"series":2,"stats":{"get_bytes":99369,"get_count":137,"put_bytes":102791,"put_count":160}} -{"i":45,"series":2,"stats":{"get_bytes":102316,"get_count":142,"put_bytes":106022,"put_count":170}} -{"i":46,"series":2,"stats":{"get_bytes":101498,"get_count":140,"put_bytes":104803,"put_count":161}} -{"i":47,"series":2,"stats":{"get_bytes":99667,"get_count":135,"put_bytes":103634,"put_count":168}} -{"i":48,"series":2,"stats":{"get_bytes":100277,"get_count":137,"put_bytes":104247,"put_count":170}} -{"i":49,"series":2,"stats":{"get_bytes":101446,"get_count":136,"put_bytes":105081,"put_count":163}} -{"i":50,"series":2,"stats":{"get_bytes":99251,"get_count":136,"put_bytes":102673,"put_count":159}} -{"i":51,"series":2,"stats":{"get_bytes":101481,"get_count":139,"put_bytes":104565,"put_count":156}} -{"i":52,"series":2,"stats":{"get_bytes":103598,"get_count":144,"put_bytes":107563,"put_count":177}} -{"i":53,"series":2,"stats":{"get_bytes":97680,"get_count":129,"put_bytes":101260,"put_count":155}} -{"i":54,"series":2,"stats":{"get_bytes":100734,"get_count":142,"put_bytes":104434,"put_count":170}} -{"i":55,"series":2,"stats":{"get_bytes":99908,"get_count":130,"put_bytes":103490,"put_count":156}} -{"i":56,"series":2,"stats":{"get_bytes":101669,"get_count":140,"put_bytes":105581,"put_count":172}} -{"i":57,"series":2,"stats":{"get_bytes":101452,"get_count":139,"put_bytes":105222,"put_count":169}} -{"i":58,"series":2,"stats":{"get_bytes":107314,"get_count":139,"put_bytes":111346,"put_count":173}} -{"i":59,"series":2,"stats":{"get_bytes":106208,"get_count":133,"put_bytes":110666,"put_count":175}} -{"i":60,"series":2,"stats":{"get_bytes":107512,"get_count":136,"put_bytes":111587,"put_count":171}} -{"i":61,"series":2,"stats":{"get_bytes":106970,"get_count":134,"put_bytes":111225,"put_count":172}} -{"i":62,"series":2,"stats":{"get_bytes":101583,"get_count":131,"put_bytes":105447,"put_count":162}} -{"i":63,"series":2,"stats":{"get_bytes":99593,"get_count":138,"put_bytes":103591,"put_count":172}} -{"i":64,"series":2,"stats":{"get_bytes":105299,"get_count":139,"put_bytes":109214,"put_count":171}} -{"i":65,"series":2,"stats":{"get_bytes":106401,"get_count":137,"put_bytes":110370,"put_count":170}} -{"i":66,"series":2,"stats":{"get_bytes":105792,"get_count":138,"put_bytes":109548,"put_count":167}} -{"i":67,"series":2,"stats":{"get_bytes":107865,"get_count":144,"put_bytes":111557,"put_count":172}} -{"i":68,"series":2,"stats":{"get_bytes":108228,"get_count":140,"put_bytes":112023,"put_count":170}} -{"i":69,"series":2,"stats":{"get_bytes":105412,"get_count":140,"put_bytes":109412,"put_count":174}} -{"i":70,"series":2,"stats":{"get_bytes":108242,"get_count":142,"put_bytes":111660,"put_count":165}} -{"i":71,"series":2,"stats":{"get_bytes":105940,"get_count":134,"put_bytes":109740,"put_count":164}} -{"i":72,"series":2,"stats":{"get_bytes":104966,"get_count":135,"put_bytes":108772,"put_count":165}} -{"i":73,"series":2,"stats":{"get_bytes":105967,"get_count":140,"put_bytes":110270,"put_count":179}} -{"i":74,"series":2,"stats":{"get_bytes":103449,"get_count":134,"put_bytes":107424,"put_count":167}} -{"i":75,"series":2,"stats":{"get_bytes":105512,"get_count":141,"put_bytes":109151,"put_count":168}} -{"i":76,"series":2,"stats":{"get_bytes":107464,"get_count":142,"put_bytes":111549,"put_count":177}} -{"i":77,"series":2,"stats":{"get_bytes":110884,"get_count":135,"put_bytes":114843,"put_count":168}} -{"i":78,"series":2,"stats":{"get_bytes":108559,"get_count":140,"put_bytes":112429,"put_count":171}} -{"i":79,"series":2,"stats":{"get_bytes":108811,"get_count":134,"put_bytes":112903,"put_count":169}} -{"i":80,"series":2,"stats":{"get_bytes":110954,"get_count":143,"put_bytes":115082,"put_count":179}} -{"i":81,"series":2,"stats":{"get_bytes":106133,"get_count":137,"put_bytes":109889,"put_count":166}} -{"i":82,"series":2,"stats":{"get_bytes":112044,"get_count":145,"put_bytes":115906,"put_count":176}} -{"i":83,"series":2,"stats":{"get_bytes":109102,"get_count":136,"put_bytes":113411,"put_count":175}} -{"i":84,"series":2,"stats":{"get_bytes":107996,"get_count":136,"put_bytes":111714,"put_count":165}} -{"i":85,"series":2,"stats":{"get_bytes":108645,"get_count":137,"put_bytes":112617,"put_count":170}} -{"i":86,"series":2,"stats":{"get_bytes":106875,"get_count":142,"put_bytes":110964,"put_count":177}} -{"i":87,"series":2,"stats":{"get_bytes":113455,"get_count":140,"put_bytes":117489,"put_count":174}} -{"i":88,"series":2,"stats":{"get_bytes":109122,"get_count":138,"put_bytes":113031,"put_count":170}} -{"i":89,"series":2,"stats":{"get_bytes":106679,"get_count":140,"put_bytes":110326,"put_count":167}} -{"i":90,"series":2,"stats":{"get_bytes":108678,"get_count":141,"put_bytes":112317,"put_count":168}} -{"i":91,"series":2,"stats":{"get_bytes":113650,"get_count":143,"put_bytes":117213,"put_count":170}} -{"i":92,"series":2,"stats":{"get_bytes":109183,"get_count":140,"put_bytes":113010,"put_count":171}} -{"i":93,"series":2,"stats":{"get_bytes":109740,"get_count":140,"put_bytes":113978,"put_count":179}} -{"i":94,"series":2,"stats":{"get_bytes":111827,"get_count":143,"put_bytes":115576,"put_count":172}} -{"i":95,"series":2,"stats":{"get_bytes":109814,"get_count":142,"put_bytes":113561,"put_count":171}} -{"i":96,"series":2,"stats":{"get_bytes":113342,"get_count":142,"put_bytes":117333,"put_count":176}} -{"i":97,"series":2,"stats":{"get_bytes":113106,"get_count":141,"put_bytes":117137,"put_count":175}} -{"i":98,"series":2,"stats":{"get_bytes":115775,"get_count":146,"put_bytes":119253,"put_count":170}} -{"i":99,"series":2,"stats":{"get_bytes":107141,"get_count":139,"put_bytes":111111,"put_count":172}} +{"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 index 3eb3a947901f298249ebaaa8803a95449f23ec59..a1559a5892decfa5f6afd5e873c983581949fe89 100644 GIT binary patch delta 14675 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 YrW4OSzuU-yoFYV1T~Dp*?&I+P0m`RLV*mgE delta 14678 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�Ow{NP<0OJPet%Tijr6j;5{S|3nhg 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 diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index a0ad60348..87e726107 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":11811,"get_count":20,"put_bytes":9803,"put_count":19}} -{"i":1,"series":1,"stats":{"get_bytes":12430,"get_count":15,"put_bytes":10422,"put_count":14}} -{"i":2,"series":1,"stats":{"get_bytes":11036,"get_count":14,"put_bytes":9028,"put_count":13}} -{"i":3,"series":1,"stats":{"get_bytes":12469,"get_count":20,"put_bytes":10461,"put_count":19}} -{"i":4,"series":1,"stats":{"get_bytes":11880,"get_count":18,"put_bytes":9872,"put_count":17}} -{"i":5,"series":1,"stats":{"get_bytes":12694,"get_count":16,"put_bytes":10686,"put_count":15}} -{"i":6,"series":1,"stats":{"get_bytes":12810,"get_count":20,"put_bytes":10802,"put_count":19}} -{"i":7,"series":1,"stats":{"get_bytes":12544,"get_count":17,"put_bytes":10536,"put_count":16}} -{"i":8,"series":1,"stats":{"get_bytes":12713,"get_count":21,"put_bytes":10705,"put_count":20}} -{"i":9,"series":1,"stats":{"get_bytes":12243,"get_count":16,"put_bytes":10235,"put_count":15}} -{"i":10,"series":1,"stats":{"get_bytes":13556,"get_count":20,"put_bytes":11548,"put_count":19}} -{"i":11,"series":1,"stats":{"get_bytes":11308,"get_count":14,"put_bytes":9300,"put_count":13}} -{"i":12,"series":1,"stats":{"get_bytes":11710,"get_count":18,"put_bytes":9702,"put_count":17}} -{"i":13,"series":1,"stats":{"get_bytes":13214,"get_count":18,"put_bytes":11206,"put_count":17}} -{"i":14,"series":1,"stats":{"get_bytes":12533,"get_count":18,"put_bytes":10525,"put_count":17}} -{"i":15,"series":1,"stats":{"get_bytes":11848,"get_count":17,"put_bytes":9840,"put_count":16}} -{"i":16,"series":1,"stats":{"get_bytes":10177,"get_count":13,"put_bytes":8169,"put_count":12}} -{"i":17,"series":1,"stats":{"get_bytes":10922,"get_count":17,"put_bytes":8914,"put_count":16}} -{"i":18,"series":1,"stats":{"get_bytes":13253,"get_count":18,"put_bytes":11245,"put_count":17}} -{"i":19,"series":1,"stats":{"get_bytes":12511,"get_count":19,"put_bytes":10503,"put_count":18}} -{"i":20,"series":1,"stats":{"get_bytes":12889,"get_count":20,"put_bytes":10881,"put_count":19}} -{"i":21,"series":1,"stats":{"get_bytes":11597,"get_count":17,"put_bytes":9589,"put_count":16}} -{"i":22,"series":1,"stats":{"get_bytes":13572,"get_count":19,"put_bytes":11564,"put_count":18}} -{"i":23,"series":1,"stats":{"get_bytes":11053,"get_count":19,"put_bytes":9045,"put_count":18}} -{"i":24,"series":1,"stats":{"get_bytes":12683,"get_count":18,"put_bytes":10675,"put_count":17}} -{"i":25,"series":1,"stats":{"get_bytes":12827,"get_count":18,"put_bytes":10819,"put_count":17}} -{"i":26,"series":1,"stats":{"get_bytes":11982,"get_count":18,"put_bytes":9974,"put_count":17}} -{"i":27,"series":1,"stats":{"get_bytes":11531,"get_count":14,"put_bytes":9523,"put_count":13}} -{"i":28,"series":1,"stats":{"get_bytes":13052,"get_count":19,"put_bytes":11044,"put_count":18}} -{"i":29,"series":1,"stats":{"get_bytes":12850,"get_count":18,"put_bytes":10842,"put_count":17}} -{"i":30,"series":1,"stats":{"get_bytes":13602,"get_count":21,"put_bytes":11594,"put_count":20}} -{"i":31,"series":1,"stats":{"get_bytes":11835,"get_count":16,"put_bytes":9827,"put_count":15}} -{"i":32,"series":1,"stats":{"get_bytes":12974,"get_count":19,"put_bytes":10966,"put_count":18}} -{"i":33,"series":1,"stats":{"get_bytes":12459,"get_count":19,"put_bytes":10451,"put_count":18}} -{"i":34,"series":1,"stats":{"get_bytes":11969,"get_count":15,"put_bytes":9961,"put_count":14}} -{"i":35,"series":1,"stats":{"get_bytes":12550,"get_count":19,"put_bytes":10542,"put_count":18}} -{"i":36,"series":1,"stats":{"get_bytes":13420,"get_count":19,"put_bytes":11412,"put_count":18}} -{"i":37,"series":1,"stats":{"get_bytes":11452,"get_count":16,"put_bytes":9444,"put_count":15}} -{"i":38,"series":1,"stats":{"get_bytes":11032,"get_count":16,"put_bytes":9024,"put_count":15}} -{"i":39,"series":1,"stats":{"get_bytes":12573,"get_count":18,"put_bytes":10565,"put_count":17}} -{"i":40,"series":1,"stats":{"get_bytes":12341,"get_count":22,"put_bytes":10333,"put_count":21}} -{"i":41,"series":1,"stats":{"get_bytes":13927,"get_count":20,"put_bytes":11919,"put_count":19}} -{"i":42,"series":1,"stats":{"get_bytes":12774,"get_count":20,"put_bytes":10766,"put_count":19}} -{"i":43,"series":1,"stats":{"get_bytes":12950,"get_count":17,"put_bytes":10942,"put_count":16}} -{"i":44,"series":1,"stats":{"get_bytes":13761,"get_count":21,"put_bytes":11753,"put_count":20}} -{"i":45,"series":1,"stats":{"get_bytes":11526,"get_count":17,"put_bytes":9518,"put_count":16}} -{"i":46,"series":1,"stats":{"get_bytes":12574,"get_count":18,"put_bytes":10566,"put_count":17}} -{"i":47,"series":1,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":11562,"put_count":18}} -{"i":48,"series":1,"stats":{"get_bytes":12760,"get_count":20,"put_bytes":10752,"put_count":19}} -{"i":49,"series":1,"stats":{"get_bytes":12422,"get_count":20,"put_bytes":10414,"put_count":19}} -{"i":50,"series":1,"stats":{"get_bytes":13333,"get_count":19,"put_bytes":11325,"put_count":18}} -{"i":51,"series":1,"stats":{"get_bytes":11525,"get_count":15,"put_bytes":9517,"put_count":14}} -{"i":52,"series":1,"stats":{"get_bytes":11604,"get_count":17,"put_bytes":9596,"put_count":16}} -{"i":53,"series":1,"stats":{"get_bytes":12756,"get_count":18,"put_bytes":10748,"put_count":17}} -{"i":54,"series":1,"stats":{"get_bytes":13101,"get_count":22,"put_bytes":11093,"put_count":21}} -{"i":55,"series":1,"stats":{"get_bytes":10933,"get_count":17,"put_bytes":8925,"put_count":16}} -{"i":56,"series":1,"stats":{"get_bytes":11963,"get_count":17,"put_bytes":9955,"put_count":16}} -{"i":57,"series":1,"stats":{"get_bytes":13221,"get_count":17,"put_bytes":11213,"put_count":16}} -{"i":58,"series":1,"stats":{"get_bytes":13612,"get_count":21,"put_bytes":11604,"put_count":20}} -{"i":59,"series":1,"stats":{"get_bytes":12968,"get_count":16,"put_bytes":10960,"put_count":15}} -{"i":60,"series":1,"stats":{"get_bytes":12674,"get_count":18,"put_bytes":10666,"put_count":17}} -{"i":61,"series":1,"stats":{"get_bytes":14108,"get_count":23,"put_bytes":12100,"put_count":22}} -{"i":62,"series":1,"stats":{"get_bytes":12540,"get_count":18,"put_bytes":10532,"put_count":17}} -{"i":63,"series":1,"stats":{"get_bytes":13371,"get_count":19,"put_bytes":11363,"put_count":18}} -{"i":64,"series":1,"stats":{"get_bytes":11959,"get_count":19,"put_bytes":9951,"put_count":18}} -{"i":65,"series":1,"stats":{"get_bytes":12206,"get_count":19,"put_bytes":10198,"put_count":18}} -{"i":66,"series":1,"stats":{"get_bytes":11009,"get_count":16,"put_bytes":9001,"put_count":15}} -{"i":67,"series":1,"stats":{"get_bytes":12714,"get_count":20,"put_bytes":10706,"put_count":19}} -{"i":68,"series":1,"stats":{"get_bytes":12447,"get_count":17,"put_bytes":10439,"put_count":16}} -{"i":69,"series":1,"stats":{"get_bytes":11005,"get_count":17,"put_bytes":8997,"put_count":16}} -{"i":70,"series":1,"stats":{"get_bytes":13527,"get_count":18,"put_bytes":11519,"put_count":17}} -{"i":71,"series":1,"stats":{"get_bytes":10400,"get_count":16,"put_bytes":8392,"put_count":15}} -{"i":72,"series":1,"stats":{"get_bytes":12082,"get_count":15,"put_bytes":10074,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":12762,"get_count":19,"put_bytes":10754,"put_count":18}} -{"i":74,"series":1,"stats":{"get_bytes":12966,"get_count":20,"put_bytes":10958,"put_count":19}} -{"i":75,"series":1,"stats":{"get_bytes":11605,"get_count":18,"put_bytes":9597,"put_count":17}} -{"i":76,"series":1,"stats":{"get_bytes":12740,"get_count":19,"put_bytes":10732,"put_count":18}} -{"i":77,"series":1,"stats":{"get_bytes":13254,"get_count":18,"put_bytes":11246,"put_count":17}} -{"i":78,"series":1,"stats":{"get_bytes":12525,"get_count":18,"put_bytes":10517,"put_count":17}} -{"i":79,"series":1,"stats":{"get_bytes":12876,"get_count":21,"put_bytes":10868,"put_count":20}} -{"i":80,"series":1,"stats":{"get_bytes":12217,"get_count":18,"put_bytes":10209,"put_count":17}} -{"i":81,"series":1,"stats":{"get_bytes":11588,"get_count":18,"put_bytes":9580,"put_count":17}} -{"i":82,"series":1,"stats":{"get_bytes":11678,"get_count":19,"put_bytes":9670,"put_count":18}} -{"i":83,"series":1,"stats":{"get_bytes":13733,"get_count":17,"put_bytes":11725,"put_count":16}} -{"i":84,"series":1,"stats":{"get_bytes":12403,"get_count":18,"put_bytes":10395,"put_count":17}} -{"i":85,"series":1,"stats":{"get_bytes":12108,"get_count":17,"put_bytes":10100,"put_count":16}} -{"i":86,"series":1,"stats":{"get_bytes":12478,"get_count":18,"put_bytes":10470,"put_count":17}} -{"i":87,"series":1,"stats":{"get_bytes":12101,"get_count":16,"put_bytes":10093,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":13399,"get_count":20,"put_bytes":11391,"put_count":19}} -{"i":89,"series":1,"stats":{"get_bytes":13567,"get_count":18,"put_bytes":11559,"put_count":17}} -{"i":90,"series":1,"stats":{"get_bytes":13068,"get_count":19,"put_bytes":11060,"put_count":18}} -{"i":91,"series":1,"stats":{"get_bytes":12976,"get_count":18,"put_bytes":10968,"put_count":17}} -{"i":92,"series":1,"stats":{"get_bytes":11515,"get_count":16,"put_bytes":9507,"put_count":15}} -{"i":93,"series":1,"stats":{"get_bytes":12802,"get_count":18,"put_bytes":10794,"put_count":17}} -{"i":94,"series":1,"stats":{"get_bytes":11697,"get_count":18,"put_bytes":9689,"put_count":17}} -{"i":95,"series":1,"stats":{"get_bytes":11472,"get_count":17,"put_bytes":9464,"put_count":16}} -{"i":96,"series":1,"stats":{"get_bytes":11570,"get_count":19,"put_bytes":9562,"put_count":18}} -{"i":97,"series":1,"stats":{"get_bytes":11707,"get_count":16,"put_bytes":9699,"put_count":15}} -{"i":98,"series":1,"stats":{"get_bytes":12679,"get_count":20,"put_bytes":10671,"put_count":19}} -{"i":99,"series":1,"stats":{"get_bytes":12512,"get_count":21,"put_bytes":10504,"put_count":20}} -{"i":0,"series":2,"stats":{"get_bytes":11811,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":12430,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":2,"series":2,"stats":{"get_bytes":11036,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":3,"series":2,"stats":{"get_bytes":12469,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":4,"series":2,"stats":{"get_bytes":11880,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":5,"series":2,"stats":{"get_bytes":12694,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":6,"series":2,"stats":{"get_bytes":12810,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":7,"series":2,"stats":{"get_bytes":12544,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":8,"series":2,"stats":{"get_bytes":12713,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":9,"series":2,"stats":{"get_bytes":12243,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":10,"series":2,"stats":{"get_bytes":13556,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":11,"series":2,"stats":{"get_bytes":11308,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":2,"stats":{"get_bytes":11710,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":13,"series":2,"stats":{"get_bytes":13214,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":14,"series":2,"stats":{"get_bytes":12533,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":15,"series":2,"stats":{"get_bytes":11848,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":16,"series":2,"stats":{"get_bytes":10177,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":17,"series":2,"stats":{"get_bytes":10922,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":18,"series":2,"stats":{"get_bytes":13253,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":19,"series":2,"stats":{"get_bytes":12511,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":20,"series":2,"stats":{"get_bytes":12889,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":21,"series":2,"stats":{"get_bytes":11597,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":22,"series":2,"stats":{"get_bytes":13572,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":23,"series":2,"stats":{"get_bytes":11053,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":24,"series":2,"stats":{"get_bytes":12683,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":25,"series":2,"stats":{"get_bytes":12827,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":26,"series":2,"stats":{"get_bytes":11982,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":27,"series":2,"stats":{"get_bytes":11531,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":28,"series":2,"stats":{"get_bytes":13052,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":29,"series":2,"stats":{"get_bytes":12850,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":30,"series":2,"stats":{"get_bytes":13602,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":31,"series":2,"stats":{"get_bytes":11835,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":32,"series":2,"stats":{"get_bytes":12974,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":33,"series":2,"stats":{"get_bytes":12459,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":34,"series":2,"stats":{"get_bytes":11969,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":35,"series":2,"stats":{"get_bytes":12550,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":36,"series":2,"stats":{"get_bytes":13420,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":37,"series":2,"stats":{"get_bytes":11452,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":38,"series":2,"stats":{"get_bytes":11032,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":39,"series":2,"stats":{"get_bytes":12573,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":40,"series":2,"stats":{"get_bytes":12341,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":41,"series":2,"stats":{"get_bytes":13927,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":42,"series":2,"stats":{"get_bytes":12774,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":43,"series":2,"stats":{"get_bytes":12950,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":44,"series":2,"stats":{"get_bytes":13761,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":45,"series":2,"stats":{"get_bytes":11526,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":46,"series":2,"stats":{"get_bytes":12574,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":47,"series":2,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":48,"series":2,"stats":{"get_bytes":12760,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":49,"series":2,"stats":{"get_bytes":12422,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":50,"series":2,"stats":{"get_bytes":13333,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":51,"series":2,"stats":{"get_bytes":11525,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":52,"series":2,"stats":{"get_bytes":11604,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":53,"series":2,"stats":{"get_bytes":12756,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":54,"series":2,"stats":{"get_bytes":13101,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":55,"series":2,"stats":{"get_bytes":10933,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":56,"series":2,"stats":{"get_bytes":11963,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":57,"series":2,"stats":{"get_bytes":13221,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":58,"series":2,"stats":{"get_bytes":13612,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":59,"series":2,"stats":{"get_bytes":12968,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":60,"series":2,"stats":{"get_bytes":12674,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":61,"series":2,"stats":{"get_bytes":14108,"get_count":23,"put_bytes":0,"put_count":0}} -{"i":62,"series":2,"stats":{"get_bytes":12540,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":63,"series":2,"stats":{"get_bytes":13371,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":64,"series":2,"stats":{"get_bytes":11959,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":65,"series":2,"stats":{"get_bytes":12206,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":66,"series":2,"stats":{"get_bytes":11009,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":67,"series":2,"stats":{"get_bytes":12714,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":68,"series":2,"stats":{"get_bytes":12447,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":69,"series":2,"stats":{"get_bytes":11005,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":70,"series":2,"stats":{"get_bytes":13527,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":71,"series":2,"stats":{"get_bytes":10400,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":72,"series":2,"stats":{"get_bytes":12082,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":73,"series":2,"stats":{"get_bytes":12762,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":74,"series":2,"stats":{"get_bytes":12966,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":75,"series":2,"stats":{"get_bytes":11605,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":76,"series":2,"stats":{"get_bytes":12740,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":77,"series":2,"stats":{"get_bytes":13254,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":78,"series":2,"stats":{"get_bytes":12525,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":79,"series":2,"stats":{"get_bytes":12876,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":80,"series":2,"stats":{"get_bytes":12217,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":81,"series":2,"stats":{"get_bytes":11588,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":82,"series":2,"stats":{"get_bytes":11678,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":83,"series":2,"stats":{"get_bytes":13733,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":84,"series":2,"stats":{"get_bytes":12403,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":85,"series":2,"stats":{"get_bytes":12108,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":86,"series":2,"stats":{"get_bytes":12478,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":87,"series":2,"stats":{"get_bytes":12101,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":88,"series":2,"stats":{"get_bytes":13399,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":89,"series":2,"stats":{"get_bytes":13567,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":90,"series":2,"stats":{"get_bytes":13068,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":91,"series":2,"stats":{"get_bytes":12976,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":92,"series":2,"stats":{"get_bytes":11515,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":93,"series":2,"stats":{"get_bytes":12802,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":94,"series":2,"stats":{"get_bytes":11697,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":95,"series":2,"stats":{"get_bytes":11472,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":96,"series":2,"stats":{"get_bytes":11570,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":97,"series":2,"stats":{"get_bytes":11707,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":98,"series":2,"stats":{"get_bytes":12679,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":99,"series":2,"stats":{"get_bytes":12512,"get_count":21,"put_bytes":0,"put_count":0}} +{"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 index a5949d4507a19e95306d3cabaf1963cf65c74abf..1d993452750fb9bf624074b74932acfc9ff508fa 100644 GIT binary patch delta 17024 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 delta 14037 zcmcI~cT^Njvp0&!0)h(?L<9)}0!t8Fkt_%zNhK{K(NO_mNs@ywD!ck12$GhJN)8JQ zNRB!NBqv!UiwX!!lBmRQd7k(E?)~T9^Zs-9oINws-PK)PU0wC7>duKckQZ?PE@OhW z%u`oBDn7W7Yj1L8QQk-@|8DbI3J-4h7R$Wt<@;Z&Udg*Q3@HVe&L1W)2)=bn;_R(6 ze9uj3diqWy({8c$a6f6DY0jhbrnOcJ>nTR??Ik7YSgpW>vqkrl@xavs>zn;%z;H3B za~I4nKT-P5JFsZlP&ece8vd!e~6o^&JW@f}weg!*F_-0Jcrt)dE2-bo1kt6XxnfN?au5{!rZyi|Ec8C_f3 zG=Zh-ZZVocKkX`NGpZiXnppC_L1tTQLfogSDK8fKXKX=rrys0z_ZH&yt|4E>#A7x) zFOsHm6sM<-Mhav6B{y2LbSK<|t8>l!>Sx{I6t6`K8Y8OlLH51)8jD2s|9Z_ukef?K zX#JE-Lbl*;3W^zgt59tr)<6m(@`7(oYyS7wVB*I7z`uK?Son{JH2j<3KdjOaCG+f+ z`;2XHK$HggnTip~cBl^%GJ0?H*S8myY{(-US_z7(Ac*<$p$JjsqHIM{BmbtTy6qmX zw>XId*HP3DPCH|E+;snLEmiN)G$t84)>edUp9LE>a*LLcoxnoFK<@s;zkI8iRGwx~ z-!%AZ^R%F-|7x2~gs#qi?f=M@W_c%{-!p?h=-XK6bwP5;(!VI(~``W}Lm1FGr-*S*8PhD*g?NEr7h^V;oYWA+^pKm%F zkELcWOf4#^N7|+K%kDZ+>urJ1!{$CtVj307*N`MhX?g%q8|7%3UW%LJ&QJ~NXu)x zoToF&Uq5-T>>4FY%@0{c`C}Qu;Kxo@o4+C~fsV95-2%=8_|8SZ;vrmIOu(cY>dyu& zWT;dz>HN2{0=L#ZC_;q~vk>RpHF?!o8+b9HgIHE~*^cLz2#B;M53hS1BKUBjsI&T? zukJ)~lg{`(6-Hvttg_eJ0HbMvLHq`FcwlBE89qR3YR64-yrz*3idFc{juQQB zJJ{(#8wRAdmb+|3BG$G&4T*r+68=2DD}?#dl7?(6L15TkwJsCYzHa?$((0`RP6vWd zeY+!p1$=vTfpaP!9=lIH1*&Bhzxo&b92cdHjmkz?i>Q699)Uq^Na*p<1aU~oZ=r7|6yeyt zXYqu_p}-K&yWt+?Atg8A{?Rf<1Or_QF`4b>JsyohYPVU{2n<1qJS0bJWG5v|gsfSL z!h1!dsKRK9+dus!E5$$}8sSZM!{O6UwE>=oux}rYfg`e?+w+HTmoEdc-{-&ndP1wL zm$ROL5So^js`0Zu;gOscBo9A;Z7cEYl(Vg`n~bM7CQtU5BENq5uAXEE^*H|=0(YM6 z1~Z_j$BF%YH~n8Bk}jS-!6DBx>~p%d1=fE^xW`YkhBN1Ca zNQ9>}fp};!gM+n48 z*)7BKb5aBbdNIVg`P$l%tXWxMYaq1p-FM>J2G7{_FJ=qX*T=imfJT?FE$0XDO%=)h z{_MijT_H)5A@e9`?d_$07G|k#IzKcnNb)b(m-3YZ>^PN&qO$*}&3kOLldW%_0ME^p z1@vDnZ_`KNmHwokCOdH*GPn$c>g4tXRYkyK7c*vQ*3pUuBtC>ADReXw(fV2=bB_K< zcGh|tN9WwauMtINgZnAD%v6rs^PHrMHtZCsGjaLve?aY6sHXv}UOx=39X(E{xGs;J zz|3yJ?rkXQ#24IW*^0mdE1+paUFUDh?9WoD@qG}n{3E3M_xQo5)?EW8hCYNvU!+-S z$&P~Xg&~`F`c)biYQ0y5$Z4V?z zNYtP1K$f>7tC*#JJPFnWMz`wMd(Ckn-!kfsI5OeqyZ@BK+A+AY`W>!|0N4Ch{eq=R z$<$w^XE5Wtmw`+u`!vC4NxV~c2ZQF8)p$XVEaN675am(83t_&SsmnzPdw3DZeE`>+ zU$6q2Lqw3|(IZG8uOd6if_s`->g%14O`WqDFStm1ucD3*EIJ%DBdp1zNma}sA8UIU z8sy)MMzMfuNjWQWrW!pCfumu>)eD=`NtVeKtJ27_>f@VUH{VqX8hl4~!0dc|{y%j@`*8!w)>u_Y#dByF98l=GW3AP-X%JB1NjGu@ zu+q&|F$KoV(G>6X+E|#2A2Q`3U~gR32ex9V4Tz-$kyNj6h zyjT$ecNj`8G=+RU@%EW60c(q>+|K`0Gbtd2iJcu0vPwyL6?>*VeS#}6BzktP1ga@W z^_q|X?}My7TP${cbo)zh^xfPNfTYvq*(tpd zzuNtgn26r1;NF4q{ntjurU=!e4C848{%*=QysJQKs4ozph+?Yqz#`Qy*A(EeMn2SJ&g@j8aVrT%RKW zD2IE*B3VOc)3Ft;@=hao4rQe`a#?QgT- z^CMAIrqhxwW;m3$%O1_Wb<(3L`aQo0Ytz#FAZwnT=;uXsQOq-Y_Uh{c5HntE=TTJ9 z;z30;N!I>R=PNX&m|2@0cX7>+*`O0pA@+MymKMSnGzJFZwa-p7N&V>h@%6g^$l7(p z5x34H^;v0*LCgLLeBY~4nw-2kio{TJU=`6-$G#QfWZQGJT9K=FK9`>gC_Tb<36MW| zjMApE6idzVkR*R_85e`dT=&Q>_6e{m<0VJ|NpWk!{B*lSDmIxu#^g_|;>UR?(lPc9e&*SrUh)MBl9cle!=m=(Zi!QGX36giy&LAbC>hl|dM) z(CUXfx~v~ zuG@NB=tXnQY(2A2%Z}7IrGh58C#cy0`0TEtMhhV2s#c|o=dgaJq?!Tn7Sa%qCD*m`HF;cI*_fHQS%WYFmqJ{o?4rMY3*aO`lZfC!7 zA!&OY7^o)FqDoR%xz(jhO)};Y2s@_4GX@WO=}phFAJ(hQADZUZJ||$SHT| zM^Jl&+Is61*WZr_a$e)1eB)3dR_Hw9VaDHPxmC-FY<4tkHW5d2mBpmSb_GeL^7?Bd zw^>7{viY8Nca$d74TU|X1;z%B=iIp{n@5L5$QiZ8XNP(lE`Gy^V3%IM&{WYsK9Yi@A+u}7JPDc4o_71yoP@Y-UwYy3>6(XOzSj|rj(UZQMj(x)?4w0Oc zovw_lrWZ8aO|M>RA7Sb6={b^RnYL?gRM2ZPuU&^hlI_$mQI!v1VP-r&cIGgmKYS=; z#YStu!L!Lbf1pI)slU4m)wVSgf(*TW=tZcoCTxfxmf3aS-jw+0JA!%Ij`?F{Z~f}P zsdH7UnxN~@9L+dfW8-1b0jOxMRh=o&Hhq`2 z-vE9uraev&Dk|Po1-X1eb~!}*8b%dbXd&@4r*H7G^VK6@04eRpC{*vpPHElHLX;N@ zt(ftb=+}J_OIMX~D_}C<`PHYwEloSlo!sY{@bAXIJwf3I1rO9zMjH?+D5|wD;Dc86 zyJcV8$i16hw==v^^M?*of zYno`U@gxn4`Yrhg=B89CK1Oi~gb3~G z762$so@TU?auuQbKrnglnl9vc&$;X z3r+dvJ|;+ZLso!GqhUGJxC3Cz%}Xaz7g5wRi^nKr?M7iTbT|qw*p-Kf^DsZy0_y;v zo=Pi+oo1mIJLPU*2p3^;%ij<<5L9QSg*D_UQ0^f=(Kny{Fk1PdnU#Qz)ve+nJuk^= za`6psf-M=#InlW5#uMY43CEcW2f}iK8wL}9bbSAodgVFHEn03r*vuq#xBRV^bC*lFG|7;7B(omR`GflD^9!iBtEHxK_361qi(_Rex~d6HXOJP!!>^41 zmB_{oCj75+?jd*--iO7DPYYmo8oMoDa3!8E3{Luf>W`A(lLSknMtd66UyvIYB|~42 zM0L7>thYQeV2Zqt4Il)`xXF^S?jmIRbBkwr<9Tx64P1(n`EaWsnda+p8p)k_OhwM`r zR{#r>joX;PBG>z1z9^a!Mq9^Qs60$-LuFEC=^z2W+8A5X&te>AVx#c)!%8M35z}xom)9B9<04OskA1?QEw5k8qxT10g)D*O{No*6JkIn#M}*yI86#erD;pFKMZ{k?5V5jZ6^kVtq%n zPb*?aMH)?L!aKx*9~jShF3V2wf4In(v%-G_F}GF4?R9NdDp(RG zcRZfLBiL$G=8qdkAB`XiJeDGWa$66`s#35Fl*( zx3y5;;x|-cg)Nbf3EwDSho=106($3D<+a}SX3>%=-p(po4UTz%wEi}@Dc7Z0)&P%g zO;B2vM-Wr`Q;6*K$!FrR5mr(*g8xbdEid0u=$m1)PD6C3WZ(85ZAmiyT>w);Xj3^K zrgp%vFwBanZKK)GRfxcKn+i3`(Q-wbEa@0b6s3XhBG7!MYq!-DasGzOC7|%Z55tL* zcr$;47*1UGWp)Z)LSIor(XatKfav&{-?QyNQ-%(*7t%uR2XN=x%8U>v-it+TVW; zC5+(rPJ&G5*}1Dit^+K&#p)o#_T2Ml!epM9nW>!=TBO)nGKNK%x1^wh1GKoxcZ+~+ zTG{=m@SeccU4I#nor*>*mAt16v;hz~5r8kw3ny{~&doW}M)jdzkR-S0m#|ZmPjOK^ zhVcePvS%PVq?SL5owV1dLiD*hp$@X{adF~4xzn&q$Zkp>imF_U@_OUb_rd2;mw*^m z5TgeY?cOot&k17y25|H!fveJ(u%?+VjDeUIIdNvEX=%r27R>lR@TL81NaN$GG&!c9 z_%evvwiVxjzwgne^+hEW_8 zp(b5!O0hvAhYK2~_&Hs)#ayNP;K^?>O)SlehKowKH36*p@U<(sdrG)Aq~CH~B@BFk&J4VTWQRbPtA z`RQ9P7l4d5Vw1!{}V8@qFMM?i8&g|jcr>BWMD85*=(M7-O z*1FXmj$#5l6ZPh=2ayiiN%xMuN;LGp9MYKe7>W(!7p0{b1D6h|(C|3A@G; zei-+}-8zMK1e3h(SqeYiUh4tKoitw5Fe%d2to$Z77H-VBVAkqPaD1ZD;^wD!=r}SI%Oc8@m)dp^x zkI|$_b79FW@9uYGp8Goeb^NG=+b(>PRwMhRN&?iOB7iSE18tEMB_ z4ME}h?T_H%sUf-Xt)JVRC@I=e7M8$!(vO|eaN{qIByVQg$Nw+=Z!drSQ5n9|D}i?K z5F`ttDIfl~7s$b5DcuZ>qTWI&TtMUg*B7aSL#&~(>teO((e8aZ@*_0={{DW{l{>v| z<$S!ut71c?;%*8kh7nd}ah8K%ORV@i7W-ov z2*h7;p0w^aEEoAaZl4!eeF`OoB8Xlq4yT=pI%NFYl#=8wF*GtC1a*XsM-L&7%=by9 zIQj?eux}-V$dBMiO)>t=Sb9nAgBPu1QzbF6QIbluKQ~m@<7O6CzWIDQRLPN+%#4#i zW!fW~@F(#pTieMWf|$%ca@L7+;>H0`r$lhAMs|C%*=n1;dQOH2xaNt*dA$2`ClX*~zS&<3b=7PU3-uwfvX$;W!7aD>jN1%~FZ=55AW?*VS&G!jMwLv8dr~7Iaw>#@q0(o{3 zc=A=_0JIYmddEJK)6#EB1em(tbdm8> z`Kr6(T3WEzUfP|u_Csx{CzrlSF!0BYIk3e{)(%0B7;+1i#wJv8cWv(wy%cGd+TdP zIk_t|-`l!Y|Mhg#^&3S*mLlbqXpC2*-OHeOr_vjemJTAAw_1f69HPL~(u1gIlWw_r z*QpC`dtp4*yGEQced%xSlo2Uuf3A>}#$~(RA|y>9`*D9XTb_aWg#Qq~*{9v#t>S%E z&VCB2UvFn|o{La%fBt4(W9CupvK(5*v{0T)o5l6-F>~x*iX5j!R?nsTzFiHy(`Fc7 zAOp5=Syl|iMN4`4ziGZAao7JT%;8jZp{y)P=*Hc?q$qzKwVi|fZ>m!w%j_-Z)MKRk zY`mc^`;O!x{5K^eQ0%CZsJEYwuo-T({p*15l!&ddUDL&_vz?eS``zTYcue0pX3nC! zgU$vzZ9n3cRMYSEG&JgAEOUaA)KgZgya?qX+!E|jv*mU#FP-BRv+A1tn0P1Y#){wU zSd~Zh&k*77V-AP%7MEMOtj|G3huYecQ*<6dMBlVOD4cht-Anpv6BpC#-irep0-{h* zP>e*txj}w+sw8ge=Yy!{Cf&k2aVZih@AbsCep@f3r*VdyzE;PmYx)ayy6k9V=Q@iw zfs8TReRacJZB)80b`FafOpBXZUx&ZMR`=1TGwNmvZwovmL6=0rmnl#5zSk$mqMDLV zDD=s8_h?N*b@@b}Eeo$!{*q@vQaXLQg;#8ktWs?EfkV)kCU$7kp?5^#8f%-UNcOby z>)4@=vjtbojTCOy@-2=@AEJW|LEobDTsRW76kaB47m{T$rk#>IarSGz3h@pfrOmQJ z16kD%(9JnYk~4lA5hizAeCz%eF%Lope8Qq0L~t+g?iEW}zj3!?7T}9leuI zw<_Wn(d&wR%q%~r4fYeh77i4o2 z`>#@1%Who-d=KR2Rp$5vwPiiM%>4$U&akv4eX{9ZwtH8Qrcm>^pA&RyJ5Gbp95~m4 zDf#>27}JtWDbNR7F#wM!KNaJCL**@p&2GuNUTfR-W8#~RSmeO1gnQah^K&95Oy*nd zLk&c4oNq7yj1tDVYTcw+1@N$xeJwe^LG#P-)AM`zkLn0?FK&9U7JiDC35H_!mKS~% zj(Gt^UgfRf>u>Wmi}-{1TV%eF&fd2yd~M@Z+9rjuh*%gcagp#2PLXR{u{D~{eKZZz zH5zVBz1-l^y3MB$82FHh?(0Dk(f-;xvy3PG8#TZQ%vQbpFRT1tk0s#a_aeHBhM%hG z4V$eFJn)B}-+Pi6S#^-^m*DqEt>1K=gb0;G6Z993$uFP@N59e~loxvG< z&^+_Jxk1(t<;)gVB-n9hBIbk+RsEO{3>Hq9aGn!{VCa1P4n#TJ`Y5aXo_K`TCmpOo620zNMUlb z&iP$(BH^*&6nsBY`%Y;haeA&Sif8j>fyqxNGx5~*5jRc1x3{)Aau~nY#EF|ywI8;& z-zuzoL$O{cAlkcKU>o)F9c*R^z7FkkG(yYZA8{s{D>>Zn89mXe-&RA*oY#~)#EkiS zRm>}cqwa#W|80by>yoCyfAW`5g-&4nY^%#;0XM0%n!#|WJZs7dfUN#n_kyM`^L(YZ z79kIrbwo76t0lFM*9^*oGTz#}D;U1|v8ez4<}4QTV9^tL28URR-gq@!*}E$9%Jpnb zNyYieu$!R#z-P%#Kc=;ky&qp37dQ^yPY5SWrjwmtkA>Z{a56QPDSm+y~9+m2`gbQ!4<~#X7bP|hH5_^4r(^|4HMBvWy$pSXY}Sa`kn09((_3fFlBsR{ zuAd}|cmC+z6ldkzMK98E+B9&>43&8dQ(e8TRQHZ1PnM z(Jl*pT(IBWQ5m(M5tCL*z#Z3%Ug#br@`e#N4r>2kuJ zB~Jz7xzhpAczI`|^?+yTM;r}5K8X7D@8{3Izuh%ozfxy85c^yM1D(m{?mL>pSs)kI zT5yz>F*4kcrisr!(WGsL(R4X$`&vQJsm=X4J(j3Q4Uv0$p^nFglb!pb^1b(2iB`U_#sB(U+D`ATYkII-y zDO#1OgcV9z2|zw?qE)j!$L(MgQD4Ks`a$$s>69~uoeY&qe5h8=7>e&w8U zVE_-jj#lS85DTk2LS+isWwWPZTl#-I+i&8VW7d$|GU@E8YxrmH2IyN7MO%x(coU}r z6Wv`m(7tt>Is5a1@9BMsSm<-*_j1ur$3M3$Cge70FG|ya2mYdUSiXOKV(?AbGtBE@ z71{!Gnm9i+!RZC`hQoAjNN1dUekXrfMGdcV%QCb~hP#7UF49CWrzYCo_kRCYM6^Ww z@N49j`+kIG(Z2=QpPiW!(D>I zhR*o11h%u5b?o>A$%Pab-eow zfci%AZjm{!AN$#P%eIs)<-!rB!96Sz`*hDu`Xa1^f~(;3zNUBW;wty%xpistJ!0aV zd=HYUucvQ%Hp+?1Q8fC!TXS&NXK!nRZzqbkD)HXhdJ?)zhhmPOBQTSQPtb>LPzD#cjOQRusR)YyexZ8tmR z&$T;-EL0(d;xTmvEysvc_~Z5?9AZI!D+Q;`_1yy^d{>Y24Zs5O-)H9)M7%#PFzQyR zoBS~=J7?kaI~EA#Vc5Nwu*CZ>@@?Oy)mo4x%HE1P%pR)md*c6;CG?y2o>koBy% z79VVMV}PppR^E+o$U8YZIz{lBezxnurav;T)Y!>p6-@)Q8=-=&>2b5RV*x9%3n-Sa zzH@r{3&N`@K__Xuz3O`+Y&(jS(OcC)dp~Hk0&{v2qw2D?D_nKDFN5Z!>Gx+Zp3Q7- z_g(Qz9;J@PqtNqpZ8%o$1Cw%u;eJ5EzaP7_Hn zZJz(+0sZGq|4&*%|1qckC&QaS|EZw*r5yhn|H#wlw5r{sq&0lQ+zdrxhg0 zFEp8_m}OhCK6*@Bb>5uM`nzuvTjz7wfZ}kBal>Ci|9GAlNY3H7oNn(wCPoj4BxJ3` z4yxq66LV5)sx#7@qtkXWl{~%o@=2-R^(Q<#Cw1%q*4zQMujYm(5~ufWYPp>v$wZ7aVLShP@&u?> z_BUSLGNgT9=hPsqefkHKD(ESFIgA9}d3Cfvk{C(5&P^{!*5&>iBKor-%5?u@)zH

hVxs!Qr2-!e)^)kq?HcP(~POV4eMnQ|I z=?|o7oT6ahHKP`vWEgl&bnxpJGgdh+uL62spe6gcZZB3EoT9(X#52owW=3t2xj!Fx)kklqTJ=)7vQ>j2gIG&T z#d*f)o-u>)Gg>|-IJ4^R%Rol;zF;Pm={`LaRdZ^~K8TR)#SKd(gJE#lX^O1KKH;p^$anhqOa2+{u#W zHV6l%+__Rh1o*l1CMw3tZdwV^e?NMwrB&?l-B;@wf7Qb$CuhRRu*n}SXE7=<1S`=SsMT{?AW$3Y}8{0?Q7TNy@IT%z7}H)KO_x z30=R+l(`ZYzOXh5_GpdP*-RRX-neV2@gzzQb22d3wZa#c>5dC}9-BF(|uFB|`y;r+aR z+H^IstZp2KEAH-jcuJ&7J?i&6S690eF`|ys3vcyk5()A+7ayf5yz@2Bcyf^k@;EPL z@NDTTxgdR>G-|qjg0z&b?-djaEPx|354%D$&pr}{_vjy2yrekR`>gM)vYyUW?P9H) GkNzLi3FABf diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index a149957d5..aebd44e18 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":93326,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":93298,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":89573,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":88148,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":93076,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":93958,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":87041,"get_count":144,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":91321,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":95982,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":92379,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":93983,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":95874,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":96223,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":90861,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":91959,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":92348,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":92566,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":91420,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":87189,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":93370,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":87331,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":89859,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":87323,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":95080,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":94206,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":91758,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":90717,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":90417,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":91586,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":90119,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":94270,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":87612,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":94513,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":90732,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":92062,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":89446,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":90663,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":91749,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":91979,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":87626,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":91210,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":90624,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":91868,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":90451,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":93774,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":93257,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":93877,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":93000,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":94793,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":92612,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":88150,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":89438,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":93141,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":89085,"get_count":143,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":92934,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":88833,"get_count":139,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":89839,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":92782,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":91638,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":93877,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":91092,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":94675,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":87998,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":94097,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":93735,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":88074,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":92798,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":92718,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":93633,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":92177,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":94946,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":89897,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":90960,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":90527,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":89056,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":94684,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":92278,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":90374,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":91126,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":91464,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":91958,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":94461,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":91341,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":87574,"get_count":142,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":95332,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":91175,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":92634,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":90745,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":91219,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":94373,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":96402,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":90715,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":96128,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":91257,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":92103,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":92700,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":89587,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":90971,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":89816,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":91852,"get_count":153,"put_bytes":0,"put_count":0}} +{"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 index 83403010a379d8c0804c1f66436d531ed85214cb..91de2c30a7600c332bf810f629388503faeeeef9 100644 GIT binary patch delta 15489 zcmY*=2|QHa`#+TolBJL(`w|8v`&yP{E!(*EZlV~a?E5Xv5JF7Gnie4pZe-tTlwv~2 z$o`Ri-?A_Lug~}U`~ClauNULaJ?FX4Ip;a==Q;1^x!A#y0Ng zLQ^eFWN8 z{?0BHoo$b(#VHHTIO^cmY1&z-bWiCX!daJ$z0L z$H1+1GdR~02Rh-mr>Fzf49jQD*f5Tpl)XM_n@1=VQ~2Yu9as&tMXpX9URmOi#(rCR zH?GQ}htBS53%1Uz4Y6eK+^+Lt3DWO);a%NhOunhJlms$k@0Wb(nkM@wO|CDPDD$bewv) ziWgy7(YabmK@*TjgPR$t(H(ak=C~8F6O)ff0heTA{w{23hMJO}3AEP^TJ_G{D{`Qd zzlx%4mqEkXk#vT?W-kQvz;0gyWgQ>!5M3suScU50>81z;PZR~}iCVY&18xTox$MFP zrB-SD;fFK9X}bz>{~wuOciPjey97A9JTqUn(C-cB|N8J3 z@0fW$t~~VkkxS{>31!Tk?(!$FOHs z$>(a7b7&vAbJNE+>?+tW3i!qA9j0ez6(RILdLVNp4r0Tw-%}!Z^gGPk9`CVkV(j?d znuU_i(#gL$|1w>R^S%cG=JgFohr`=|&Gf=y9eFMT{JPUg(e<$oC9+y1zXSH2L;@Yj zuVy#vJj%6CbY1{^36QMn^SBi4^WZu#nr1t23e>m6a?iB;cb`N596y|ahdk8!SCQ+F zc&Jdltf?J-DISbSlIl>T=77{ z`?EmXgAuXN0ri~AH@m*=-tVi$z2@L(ahTtpaU}ni9X)HPaFPk!=N#E$i)V~|00iji z0UI6ha5xi=Zt5X?i3a(&4}qE#KzUyts^p-KW{Qw}E=+{ImGiBHi4y8mwvfx<*&Q95 zU3qM1!?ev}k1)ykt@P5-fQ-%F!v?;EyAfoyKX}PtlZqs1#<_1?YU{aEa#WaPiu03;-vISd#-%KCNo2L3B7qWM z*be+=@Bu4sJ(jQD_)Mqh^E%^ct^EL1|Bm%5dRH1B$u{&_Z60A*+##aEhjq zgJ*Bv9(wTj45)b=SAKPlrB;xnDWMNE8nAq&r<4l&e+@5>x+Tc(>?97V!%n+^%6iN`(dg4RqcDO!K|zpg zd>LpLiD#tvJP;-=$$xv-^}=?i(I)?UX+gmU<>qWw>J&4M;{`i8VklJ{d@YqND?=(4 z?`bGit`Rvf2@Rvxe3KCEFCAlXceFTzM&exguPZ#>uG0@ zI-+%F$PMRb&N%cstCHNc4_qdDKz@GTY|Oyw;)m}FZ-xlhHEzORU*IP8Z?bI`=}9o1 zAi2nPR8L_pFIAlFTP$-wyRyA-w0$P{5!_(<+Nx73zr7X%UL`DFJG*La2hy1+?)ABB zazhR0&?8*2ti);G@2}&2e^C{|abzvL->A^g(RR2@Sl;;X#-rZ#o09s_z)Odjzu4bq zDHgy$0?uT!DiN-8M!zu(ytDnRN*Hdhb_Uc-HLY5R2J^<`^&?(^kAaH~6H{DYYvy#Ck{2Fj zhwQ+a+N3$zA)6ieo*24=D>nL-E+tBjk=>-j7GMxO*U7=8)Qy8CX9YQmV;wFDPZXUV@>wPyr%KeQ(>b zaoMqrX*vFcb&myCtE=SBEKM^vLRl7R>=Vt*{289uK#EL}1@$w-8)1L(#l}Ayy>J&} z;r9$DK)=tzRO!;OAh25Qp`Q5@ShxcZC!q?9Ye%0AUA$k$%sLu?ccow6Ca1q#tKYKU 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 Gr~eOF7JKRd From 11cd3fc502453ab1d0ce44b01e75d48ed2fb4511 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 17 Jan 2023 22:12:29 -0800 Subject: [PATCH 276/339] EVM: various call actor tests (#1070) * wip * fix logging for precompiles * do a lot of things, needs to get split into multiple PRs * cut down changes for new PR * impl copy for NativePrecompile * re-implement some tests * log 512 bytes instead of just 256, add indicator that it was truncated * add tests for input offsets out in the middle of nowhere * fix rebase mistake * remove log --- .../evm/src/interpreter/instructions/call.rs | 7 +- actors/evm/tests/call.rs | 197 ++++++++++++++++++ actors/evm/tests/precompile.rs | 24 +++ actors/evm/tests/util.rs | 14 +- 4 files changed, 239 insertions(+), 3 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index c2104354d..2f647d81c 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -186,14 +186,17 @@ pub fn call_generic( 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(512); + 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::error!(target: "evm", "Precompile failed: error {:?}", err); + log::warn!(target: "evm", "Precompile failed: error {:?}", err); // precompile failed, exit with reverted and no output (0, vec![]) } diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index ed3b42486..291fc3459 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -466,6 +466,21 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i 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: method_num, + value: U256::from(0), + flags: SendFlags::empty(), + codec: 0, + param_offset: data_off.as_u32(), + addr_offset: target_off.as_u32(), + param_len: data_size.as_u32(), + params: proxy_call_input_data.clone(), + addr_len: target_size.as_u32(), + 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()); @@ -477,6 +492,14 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i 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(), @@ -535,6 +558,118 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i 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: 0, + value: U256::from(0), + flags: SendFlags::empty(), + codec: 0, + param_offset: 200, + addr_offset: 300, + param_len: 0, + params: vec![], + addr_len: addr_bytes.len() as u32, + 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_generalized( + addr, + 0, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + ); + + let precompile_return = CallActorReturn::default(); + + test.run_test_expecting(&mut rt, precompile_return, util::PrecompileExit::Success); +} + +#[derive(Debug)] +struct CallActorParams { + method: MethodNum, + value: U256, + flags: SendFlags, + codec: u64, + param_offset: u32, + addr_offset: u32, + param_len: u32, + params: Vec, + addr_len: u32, + addr: Vec, +} + +impl From for Vec { + fn from(src: CallActorParams) -> Self { + let method = U256::from(src.method); + let value = src.value; + let flags = U256::from(src.flags.bits()); + let codec = U256::from(src.codec); + let param_offset = U256::from(src.param_offset); + let data_offset = U256::from(src.addr_offset); + + let mut out = [method, value, flags, codec, param_offset, data_offset] + .iter() + .map(|p| p.to_bytes().to_vec()) + .collect::>>() + .concat(); + + assert!(src.param_offset >= out.len() as u32); + assert!(src.addr_offset >= src.param_offset + src.param_len); + + let addr_len_offset = src.addr_offset as usize; + let addr_begin = (addr_len_offset + 32) as usize; + let addr_end = addr_begin + src.addr_len as usize; + + let param_len_offset = src.param_offset as usize; + let param_begin = (param_len_offset + 32) as usize; + let param_end = param_begin + src.param_len as usize; + + out.resize_with(addr_end, || 0); + + let param_len = U256::from(src.param_len).to_bytes(); + let addr_len = U256::from(src.addr_len).to_bytes(); + + out[param_len_offset as usize..param_len_offset + 32].copy_from_slice(¶m_len); + out[addr_len_offset as usize..addr_len_offset + 32].copy_from_slice(&addr_len); + + out[param_begin..param_end].copy_from_slice(&src.params); + out[addr_begin..addr_end].copy_from_slice(&src.addr); + + out + } +} + +impl Default for CallActorReturn { + fn default() -> Self { + Self { exit_code: ExitCode::OK, codec: 0, data_offset: 3 * 32, data_size: 0, data: vec![] } + } +} + #[derive(Debug, PartialEq, Eq)] struct CallActorReturn { exit_code: ExitCode, @@ -544,6 +679,25 @@ struct CallActorReturn { data: Vec, } +impl From for Vec { + fn from(src: CallActorReturn) -> Self { + let exit_code = U256::from(src.exit_code.value()); + 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(); + + assert_eq!(src.data.len(), src.data_size as usize); + out.extend_from_slice(&src.data); + out + } +} + impl CallActorReturn { fn into_vec(self) -> Vec { assert_eq!(self.data.len() % 32, 0); @@ -647,6 +801,49 @@ fn call_actor_solidity() { } } +#[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_generalized( + Address::new_id(101), + 0, + None, + TokenAmount::from_atto(1), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + ); + + 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, diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 53b6c32cf..00aa25871 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -342,6 +342,30 @@ fn test_precompile_transfer() { 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() { // TODO: refactor these to be more clear diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index b5d7e0abf..0f8f9b2bf 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -201,7 +201,7 @@ impl PrecompileTest { ] .concat(), ); - log::trace!("returned: {:?}", hex::encode(&result)); + log::trace!("exit [{}] returned: {:?}", result[0], hex::encode(&result[1..])); rt.verify(); let returned_exit = match result[0] { @@ -215,6 +215,18 @@ impl PrecompileTest { 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) { From d865ac3f4b18811645c3337781453a4d12cb4bd5 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 18 Jan 2023 13:21:34 -0500 Subject: [PATCH 277/339] Ethaccount: Implement FIP 0044 (#1071) --- Cargo.lock | 42 ++++- actors/ethaccount/Cargo.toml | 2 + actors/ethaccount/src/lib.rs | 141 +++++++++------ actors/ethaccount/src/types.rs | 10 + .../ethaccount/tests/authenticate_message.rs | 39 ++++ actors/ethaccount/tests/ethaccount_test.rs | 48 +++++ actors/ethaccount/tests/util.rs | 35 ++++ test_vm/Cargo.toml | 1 + test_vm/src/lib.rs | 10 +- .../account_authenticate_message_test.rs | 51 ------ test_vm/tests/authenticate_message_test.rs | 171 ++++++++++++++++++ 11 files changed, 435 insertions(+), 115 deletions(-) create mode 100644 actors/ethaccount/src/types.rs create mode 100644 actors/ethaccount/tests/authenticate_message.rs create mode 100644 actors/ethaccount/tests/ethaccount_test.rs create mode 100644 actors/ethaccount/tests/util.rs delete mode 100644 test_vm/tests/account_authenticate_message_test.rs create mode 100644 test_vm/tests/authenticate_message_test.rs diff --git a/Cargo.lock b/Cargo.lock index eba0ab900..3ef85218a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,7 +705,7 @@ dependencies = [ "coins-core", "digest 0.10.6", "getrandom", - "hmac", + "hmac 0.12.1", "k256", "lazy_static", "serde", @@ -723,7 +723,7 @@ dependencies = [ "coins-bip32", "getrandom", "hex", - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "rand", "sha2 0.10.6", @@ -1127,7 +1127,7 @@ dependencies = [ "ctr 0.8.0", "digest 0.10.6", "hex", - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "rand", "scrypt 0.8.1", @@ -1149,7 +1149,7 @@ dependencies = [ "ctr 0.9.2", "digest 0.10.6", "hex", - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "rand", "scrypt 0.10.0", @@ -1853,6 +1853,8 @@ name = "fil_actor_ethaccount" version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", + "frc42_dispatch", + "fvm_actor_utils", "fvm_ipld_encoding", "fvm_shared", "hex-literal", @@ -2741,6 +2743,16 @@ 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" @@ -2750,6 +2762,17 @@ 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" @@ -3056,12 +3079,14 @@ dependencies = [ "arrayref", "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]] @@ -3442,7 +3467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.6", - "hmac", + "hmac 0.12.1", "password-hash 0.4.2", "sha2 0.10.6", ] @@ -3816,7 +3841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ "crypto-bigint", - "hmac", + "hmac 0.12.1", "zeroize", ] @@ -4019,7 +4044,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e73d6d7c6311ebdbd9184ad6c4447b2f36337e327bda107d3ba9e3c374f9d325" dependencies = [ - "hmac", + "hmac 0.12.1", "password-hash 0.3.2", "pbkdf2 0.10.1", "salsa20 0.9.0", @@ -4032,7 +4057,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20 0.10.2", "sha2 0.10.6", @@ -4478,6 +4503,7 @@ dependencies = [ "indexmap", "integer-encoding", "lazy_static", + "libsecp256k1", "log", "multihash", "num-derive", diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 04852332e..4bad5f5dd 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/Cargo.toml @@ -15,6 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +frc42_dispatch = "3.0.0" +fvm_actor_utils = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } fvm_ipld_encoding = "0.3.2" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } diff --git a/actors/ethaccount/src/lib.rs b/actors/ethaccount/src/lib.rs index bc2872287..2690ba82d 100644 --- a/actors/ethaccount/src/lib.rs +++ b/actors/ethaccount/src/lib.rs @@ -1,11 +1,18 @@ -use fvm_shared::address::Payload; +pub mod types; + +use fvm_actor_utils::receiver::UniversalReceiverParams; +use fvm_shared::address::{Payload, Protocol}; +use fvm_shared::crypto::hash::SupportedHashes::Keccak256; +use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; +use crate::types::AuthenticateMessageParams; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ - actor_dispatch, actor_error, restrict_internal_api, ActorError, EAM_ACTOR_ID, SYSTEM_ACTOR_ADDR, + actor_dispatch, actor_error, restrict_internal_api, ActorDowncast, ActorError, AsActorError, + EAM_ACTOR_ID, SYSTEM_ACTOR_ADDR, }; #[cfg(feature = "fil-actor")] @@ -16,6 +23,8 @@ fil_actors_runtime::wasm_trampoline!(EthAccountActor); #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, + AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), + UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } /// Ethereum Account actor. @@ -46,67 +55,93 @@ impl EthAccountActor { Ok(()) } -} -impl ActorCode for EthAccountActor { - type Methods = Method; - actor_dispatch! { - Constructor => constructor, - } -} + /// Authenticates whether the provided signature is valid for the provided message. + /// Should be called with the raw bytes of a signature, NOT a serialized Signature object that includes a SignatureType. + /// Errors with USR_ILLEGAL_ARGUMENT if the authentication is invalid. + pub fn authenticate_message( + rt: &mut impl Runtime, + params: AuthenticateMessageParams, + ) -> Result<(), ActorError> { + rt.validate_immediate_caller_accept_any()?; + let msg_hash = rt.hash_blake2b(¶ms.message); + + let signer_pk = rt + .recover_secp_public_key( + &msg_hash, + params + .signature + .as_slice() + .try_into() + .map_err(|_| actor_error!(illegal_argument; "invalid signature length"))?, + ) + .map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_ARGUMENT, + "failed to recover signer public key", + ) + })?; + + // 0x04 to indicate uncompressed point + if signer_pk[0] != 0x04 { + return Err(actor_error!(assertion_failed; "pubkey should start with 0x04, not {}", + signer_pk[0])); + } + + // The subaddress is the last 20 bytes of the keccak hash of the public key + let signer_pk_hash = rt.hash(Keccak256, &signer_pk[1..]); + if signer_pk_hash.len() < 20 { + return Err( + actor_error!(assertion_failed; "invalid keccak hash length {}", signer_pk_hash.len()), + ); + } -#[cfg(test)] -mod tests { - use fil_actors_runtime::EAM_ACTOR_ID; - use fvm_shared::address::Address; - use fvm_shared::error::ExitCode; - use fvm_shared::MethodNum; + let signer_subaddress_bytes = &signer_pk_hash[signer_pk_hash.len() - 20..]; - use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, MockRuntime, SYSTEM_ACTOR_CODE_ID, - }; - use fil_actors_runtime::SYSTEM_ACTOR_ADDR; + let self_address = rt + .lookup_delegated_address( + rt.message().receiver().id().expect("receiver must be ID address"), + ) + .context_code( + ExitCode::USR_ILLEGAL_STATE, + "ethaccount should always have delegated address", + )?; - use crate::{EthAccountActor, Method}; + let self_address_bytes = self_address.to_bytes(); + if self_address_bytes[0] != Protocol::Delegated as u8 + || self_address_bytes[1] != EAM_ACTOR_ID as u8 + { + return Err(actor_error!(illegal_state; + "first 2 bytes of f4 address payload weren't Delegated protocol {} and EAM address {}", + self_address_bytes[0], + self_address_bytes[1])); + } - const EOA: Address = Address::new_id(1000); + // drop the first 2 bytes (protocol and EAM namespace) + let self_subaddress_bytes = &self_address_bytes[2..]; - pub fn new_runtime() -> MockRuntime { - MockRuntime { - receiver: EOA, - caller: SYSTEM_ACTOR_ADDR, - caller_type: *SYSTEM_ACTOR_CODE_ID, - ..Default::default() + if self_subaddress_bytes != signer_subaddress_bytes { + return Err(actor_error!(illegal_argument; "invalid signature for {}", self_address)); } + + Ok(()) } - #[test] - fn construct_from_system() { - let mut rt = new_runtime(); - rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); - rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); - rt.add_delegated_address( - EOA, - Address::new_delegated( - EAM_ACTOR_ID, - &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"), - ) - .unwrap(), - ); - rt.call::(Method::Constructor as MethodNum, None).unwrap(); - rt.verify(); + // Always succeeds, accepting any transfers. + pub fn universal_receiver_hook( + rt: &mut impl Runtime, + _params: UniversalReceiverParams, + ) -> Result<(), ActorError> { + rt.validate_immediate_caller_accept_any()?; + Ok(()) } +} - #[test] - fn no_delegated_cant_deploy() { - let mut rt = new_runtime(); - rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); - rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); - expect_abort_contains_message( - ExitCode::USR_ILLEGAL_ARGUMENT, - "receiver must have a predictable address", - rt.call::(Method::Constructor as MethodNum, None), - ); - rt.verify(); +impl ActorCode for EthAccountActor { + type Methods = Method; + actor_dispatch! { + Constructor => constructor, + AuthenticateMessageExported => authenticate_message, + UniversalReceiverHook => universal_receiver_hook, } } diff --git a/actors/ethaccount/src/types.rs b/actors/ethaccount/src/types.rs new file mode 100644 index 000000000..8b16662d6 --- /dev/null +++ b/actors/ethaccount/src/types.rs @@ -0,0 +1,10 @@ +use fvm_ipld_encoding::serde_bytes; +use fvm_ipld_encoding::tuple::*; + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct AuthenticateMessageParams { + #[serde(with = "serde_bytes")] + pub signature: Vec, + #[serde(with = "serde_bytes")] + pub message: Vec, +} diff --git a/actors/ethaccount/tests/authenticate_message.rs b/actors/ethaccount/tests/authenticate_message.rs new file mode 100644 index 000000000..408992d1c --- /dev/null +++ b/actors/ethaccount/tests/authenticate_message.rs @@ -0,0 +1,39 @@ +mod util; + +use crate::util::*; +use fil_actor_ethaccount::types::AuthenticateMessageParams; +use fil_actor_ethaccount::{EthAccountActor, Method}; +use fil_actors_runtime::test_utils::expect_abort_contains_message; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::error::ExitCode; +use fvm_shared::MethodNum; + +#[test] +fn must_have_params() { + let mut rt = setup(); + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "method expects arguments", + rt.call::(Method::AuthenticateMessageExported as MethodNum, None), + ); + rt.verify(); +} + +#[test] +fn signature_bad_length_fails() { + let mut rt = setup(); + rt.expect_validate_caller_any(); + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "invalid signature length", + rt.call::( + Method::AuthenticateMessageExported as MethodNum, + IpldBlock::serialize_cbor(&AuthenticateMessageParams { + signature: vec![0xde, 0xad, 0xbe, 0xef], + message: vec![0xfa, 0xce], + }) + .unwrap(), + ), + ); + rt.verify(); +} diff --git a/actors/ethaccount/tests/ethaccount_test.rs b/actors/ethaccount/tests/ethaccount_test.rs new file mode 100644 index 000000000..3c30bb300 --- /dev/null +++ b/actors/ethaccount/tests/ethaccount_test.rs @@ -0,0 +1,48 @@ +mod util; + +use crate::util::*; +use fvm_actor_utils::receiver::UniversalReceiverParams; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; + +use fil_actor_ethaccount::{EthAccountActor, Method}; +use fvm_shared::error::ExitCode; +use fvm_shared::MethodNum; + +use fil_actors_runtime::test_utils::{ + expect_abort_contains_message, EVM_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, +}; +use fil_actors_runtime::SYSTEM_ACTOR_ADDR; + +#[test] +fn no_delegated_cant_deploy() { + let mut rt = new_runtime(); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "receiver must have a predictable address", + rt.call::(Method::Constructor as MethodNum, None), + ); + rt.verify(); +} + +#[test] +fn token_receiver() { + let mut rt = setup(); + + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); + rt.expect_validate_caller_any(); + let ret = rt + .call::( + Method::UniversalReceiverHook as MethodNum, + IpldBlock::serialize_cbor(&UniversalReceiverParams { + type_: 0, + payload: RawBytes::new(vec![1, 2, 3]), + }) + .unwrap(), + ) + .unwrap(); + assert!(ret.is_none()); +} diff --git a/actors/ethaccount/tests/util.rs b/actors/ethaccount/tests/util.rs new file mode 100644 index 000000000..9bdfe23d5 --- /dev/null +++ b/actors/ethaccount/tests/util.rs @@ -0,0 +1,35 @@ +use fil_actor_ethaccount::{EthAccountActor, Method}; +use fil_actors_runtime::test_utils::{MockRuntime, SYSTEM_ACTOR_CODE_ID}; +use fil_actors_runtime::EAM_ACTOR_ID; +use fil_actors_runtime::SYSTEM_ACTOR_ADDR; +use fvm_shared::address::Address; +use fvm_shared::MethodNum; + +pub const EOA: Address = Address::new_id(1000); + +pub fn new_runtime() -> MockRuntime { + MockRuntime { + receiver: EOA, + caller: SYSTEM_ACTOR_ADDR, + caller_type: *SYSTEM_ACTOR_CODE_ID, + ..Default::default() + } +} + +#[allow(dead_code)] +pub fn setup() -> MockRuntime { + let mut rt = new_runtime(); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.add_delegated_address( + EOA, + Address::new_delegated( + EAM_ACTOR_ID, + &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"), + ) + .unwrap(), + ); + rt.call::(Method::Constructor as MethodNum, None).unwrap(); + rt.verify(); + rt +} diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 2f3110d97..b8d40522f 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -51,6 +51,7 @@ rand_chacha = "0.3.1" regex = "1" serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0.30" +libsecp256k1 = { version = "0.7.1"} [dev-dependencies] cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index f06da32c4..62f0d10b0 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -433,6 +433,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(); @@ -628,9 +632,9 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { // Validate that there's an actor at the target ID (we don't care what is there, // just that something is there). if self.v.get_actor(Address::new_id(da.namespace())).is_some() => - { - false - } + { + false + } _ => { return Err(ActorError::unchecked( ExitCode::SYS_INVALID_RECEIVER, 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) +} From 008bac85d5dae40507eb1627e11e3f61ceff39c5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Jan 2023 10:48:09 -0800 Subject: [PATCH 278/339] EVM: infallible address conversion (#1074) We now allow conversion of all addresses, even precompile ones. Nothing is reserved. This lets us, e.g., check the balance of a precompile which is technically legal in the EVM. --- actors/evm/src/interpreter/address.rs | 55 +++---------------- .../evm/src/interpreter/instructions/call.rs | 5 +- .../evm/src/interpreter/instructions/ext.rs | 5 +- .../src/interpreter/instructions/lifecycle.rs | 8 +-- .../evm/src/interpreter/instructions/state.rs | 10 ++-- actors/evm/src/interpreter/precompiles/mod.rs | 7 +-- 6 files changed, 22 insertions(+), 68 deletions(-) diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/src/interpreter/address.rs index acd6bc290..d92869fbd 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/src/interpreter/address.rs @@ -1,12 +1,9 @@ -use crate::StatusCode; use crate::U256; use fil_actors_runtime::EAM_ACTOR_ID; use fvm_ipld_encoding::{serde, strict_bytes}; use fvm_shared::address::Address; use fvm_shared::ActorID; -use super::precompiles::is_reserved_precompile_address; - /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). /// /// TODO this type will eventually handle f4 address detection. @@ -30,30 +27,19 @@ impl std::fmt::Debug for EthAddress { } } -impl TryFrom for Address { - type Error = StatusCode; - fn try_from(addr: EthAddress) -> Result { - TryFrom::try_from(&addr) +impl From for Address { + fn from(addr: EthAddress) -> Self { + From::from(&addr) } } -impl TryFrom<&EthAddress> for Address { - type Error = StatusCode; - fn try_from(addr: &EthAddress) -> Result { - if is_reserved_precompile_address(addr) { - return Err(StatusCode::BadAddress(format!( - "Cannot convert a precompile address: {:?} to an f4 address", - addr - ))); - } - - let f4_addr = if let Some(id) = addr.as_id() { +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() - }; - - Ok(f4_addr) + } } } @@ -100,8 +86,6 @@ impl AsRef<[u8]> for EthAddress { #[cfg(test)] mod tests { - use fvm_shared::address::Address; - use crate::interpreter::address::EthAddress; use crate::U256; @@ -116,7 +100,7 @@ mod tests { #[test] fn $name() { let evm_bytes = $input.concat(); - let evm_addr = EthAddress::try_from(U256::from(evm_bytes.as_slice())).unwrap(); + let evm_addr = EthAddress::from(U256::from(evm_bytes.as_slice())); assert_eq!( evm_addr.as_id(), $expectation @@ -167,27 +151,4 @@ mod tests { vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) ] => None, } - - #[test] - #[allow(unused)] - fn precompile_reserved_conversion() { - // in range precompile addresses - let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); - Address::try_from(addr).expect_err("can't convert precompile into f4!"); - let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); - Address::try_from(addr).expect_err("can't convert precompile into f4!"); - - // can convert null address - let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000000")); - let _: Address = addr.try_into().unwrap(); - // can convert 0 index native prefix - let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000000")); - let _: Address = addr.try_into().unwrap(); - - // out of range, but reserved - let addr = EthAddress(hex_literal::hex!("fe000000000000000000000000000000000000aa")); - Address::try_from(addr).expect_err("can't convert precompile into f4!"); - let addr = EthAddress(hex_literal::hex!("00000000000000000000000000000000000000aa")); - Address::try_from(addr).expect_err("can't convert precompile into f4!"); - } } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 2f647d81c..8ccdf6866 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -204,10 +204,7 @@ pub fn call_generic( } else { let call_result = match kind { CallKind::Call | CallKind::StaticCall => { - let dst_addr: Address = dst.try_into().map_err(|_| ActorError::assertion_failed( - "Reached a precompile address when a precompile should've been caught earlier in the system" - .to_string(), - ))?; + let dst_addr: Address = dst.into(); // Special casing for account/placeholder/non-existent actors: we just do a SEND (method 0) // which allows us to transfer funds (and create placeholders) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 97b7b7c51..9f508fc34 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -103,9 +103,8 @@ pub fn get_contract_type(rt: &RT, addr: &EthAddress) -> ContractTyp return ContractType::Precompile; } - addr.try_into() - .ok() // into filecoin address - .and_then(|addr| rt.resolve_address(&addr)) // resolve actor id + 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 diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 871f56a29..9ad64387e 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -165,10 +165,10 @@ pub fn selfdestruct( } // Try to give funds to the beneficiary. If this fails, we just keep them. - if let Ok(addr) = EthAddress::from(beneficiary).try_into() { - let balance = system.rt.current_balance(); - let _ = system.rt.send(&addr, METHOD_SEND, None, balance); - } + let beneficiary: EthAddress = beneficiary.into(); + let beneficiary: Address = beneficiary.into(); + let balance = system.rt.current_balance(); + let _ = system.rt.send(&beneficiary, METHOD_SEND, None, balance); // Now mark ourselves as deleted. system.mark_selfdestructed(); diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 6de7d3aa3..a26c4b91f 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -13,12 +13,12 @@ pub fn balance( system: &System, actor: U256, ) -> Result { - let actor: EthAddress = actor.into(); + let addr: EthAddress = actor.into(); + let addr: Address = addr.into(); - let balance = actor - .try_into() - .ok() - .and_then(|addr: Address| system.rt.resolve_address(&addr)) + let balance = system + .rt + .resolve_address(&addr) .and_then(|id| system.rt.actor_balance(id).as_ref().map(U256::from)) .unwrap_or_default(); diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index cc0c78bed..9886bb6de 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, num::TryFromIntError}; -use fil_actors_runtime::{runtime::Runtime, EAM_ACTOR_ID}; +use fil_actors_runtime::runtime::Runtime; use fvm_shared::{address::Address, econ::TokenAmount}; use substrate_bn::{CurveError, FieldError, GroupError}; @@ -111,10 +111,7 @@ impl Precompiles { // 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() { - // Explicitly construct the precompile addr. We forbid this in the usual try_into for - // safety. - let fil_addr = Address::new_delegated(EAM_ACTOR_ID, precompile_addr.as_ref()) - .expect("incorrect address size"); + let fil_addr: Address = precompile_addr.into(); system .transfer(&fil_addr, TokenAmount::from(&context.value)) .map_err(|_| PrecompileError::TransferFailed)?; From 34d70721527a813045ceed858f74f1e622bfb396 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 18 Jan 2023 17:25:02 -0800 Subject: [PATCH 279/339] EVM: Tests call actor invalid inputs (#1075) * add some tests for call-actor precompile input reading * remove duplicate into vec * use .into & fmt * call actor id with full address params --- actors/evm/tests/call.rs | 369 +++++++++++++++++++++++++++++++-------- actors/evm/tests/util.rs | 6 +- 2 files changed, 302 insertions(+), 73 deletions(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 291fc3459..85cb78b39 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -14,7 +14,7 @@ use evm::EVM_CONTRACT_REVERTED; use fil_actor_evm as evm; 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, IPLD_RAW}; +use fvm_ipld_encoding::{BytesDe, BytesSer, DAG_CBOR, IPLD_RAW}; use fvm_shared::address::Address as FILAddress; use fvm_shared::address::Address; use fvm_shared::bigint::Zero; @@ -468,15 +468,15 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i // a bit messy but a "test" for CallActorParams let params: Vec = CallActorParams { - method: method_num, + method: U256::from(method_num), value: U256::from(0), - flags: SendFlags::empty(), - codec: 0, - param_offset: data_off.as_u32(), - addr_offset: target_off.as_u32(), - param_len: data_size.as_u32(), - params: proxy_call_input_data.clone(), - addr_len: target_size.as_u32(), + 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(); @@ -539,7 +539,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i }; let (expected_exit, expected_out) = if valid_call_input { - (util::PrecompileExit::Success, expect.into_vec()) + (util::PrecompileExit::Success, expect.into()) } else { (util::PrecompileExit::Reverted, vec![]) }; @@ -569,15 +569,15 @@ fn call_actor_weird_offset() { let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); let addr_bytes = addr.to_bytes(); let params = CallActorParams { - method: 0, + method: U256::from(0), value: U256::from(0), - flags: SendFlags::empty(), - codec: 0, - param_offset: 200, - addr_offset: 300, - param_len: 0, - params: vec![], - addr_len: addr_bytes.len() as u32, + 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, }; @@ -609,57 +609,297 @@ fn call_actor_weird_offset() { test.run_test_expecting(&mut rt, precompile_return, util::PrecompileExit::Success); } -#[derive(Debug)] +#[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(DAG_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_generalized( + addr, + 0, + Some(IpldBlock { codec: DAG_CBOR, data: addr_bytes }), + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + ); + + 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_generalized( + Address::new_id(actual_id_addr), + 0, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + ); + + test.input = call_params.into(); + test.run_test_expecting(&mut rt, CallActorReturn::default(), 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: MethodNum, + method: U256, value: U256, - flags: SendFlags, - codec: u64, - param_offset: u32, - addr_offset: u32, - param_len: u32, - params: Vec, - addr_len: u32, + 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 method = U256::from(src.method); - let value = src.value; - let flags = U256::from(src.flags.bits()); - let codec = U256::from(src.codec); - let param_offset = U256::from(src.param_offset); - let data_offset = U256::from(src.addr_offset); + let param_offset = src.param_offset.as_usize(); + let addr_offset = src.addr_offset.as_usize(); - let mut out = [method, value, flags, codec, param_offset, data_offset] - .iter() - .map(|p| p.to_bytes().to_vec()) - .collect::>>() - .concat(); + let param_len_usize = src.param_len.as_usize(); + let addr_len_usize = src.addr_len.as_usize(); - assert!(src.param_offset >= out.len() as u32); - assert!(src.addr_offset >= src.param_offset + src.param_len); + 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(); - let addr_len_offset = src.addr_offset as usize; - let addr_begin = (addr_len_offset + 32) as usize; - let addr_end = addr_begin + src.addr_len as usize; + assert_eq!(out.len(), CallActorParams::FIRST_DYNAMIC_OFFSET); + assert!(param_offset >= out.len()); - let param_len_offset = src.param_offset as usize; - let param_begin = (param_len_offset + 32) as usize; - let param_end = param_begin + src.param_len as usize; + 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 = U256::from(src.param_len).to_bytes(); - let addr_len = U256::from(src.addr_len).to_bytes(); + let param_len = src.param_len.to_bytes(); + let addr_len = src.addr_len.to_bytes(); - out[param_len_offset as usize..param_len_offset + 32].copy_from_slice(¶m_len); - out[addr_len_offset as usize..addr_len_offset + 32].copy_from_slice(&addr_len); + // 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); - out[param_begin..param_end].copy_from_slice(&src.params); + // 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 } } @@ -692,31 +932,18 @@ impl From for Vec { .collect::>>() .concat(); - assert_eq!(src.data.len(), src.data_size as usize); + 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 } } -impl CallActorReturn { - fn into_vec(self) -> Vec { - assert_eq!(self.data.len() % 32, 0); - - let exit = U256::from(self.exit_code.value()); - let codec = U256::from(self.codec); - let data_offset = U256::from(self.data_offset); - let data_size = U256::from(self.data_size); - - let mut out = [exit, codec, data_offset, data_size] - .iter() - .map(|p| p.to_bytes().to_vec()) - .collect::>>() - .concat(); - out.extend_from_slice(&self.data); - out - } -} - fn make_raw_params(bytes: Vec) -> Option { if bytes.is_empty() { return None; diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 0f8f9b2bf..78e6dd188 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -62,7 +62,7 @@ pub fn init_construct_and_verify( .is_none()); let evm_st: State = rt.state().unwrap(); let evm_code = rt.store.get(&evm_st.bytecode).unwrap().unwrap(); - log::trace!("{:?}", evm_code); + log::trace!("bytecode constructed: {}", hex::encode(evm_code)); rt.verify(); rt @@ -102,7 +102,7 @@ lazy_static! { } #[repr(u8)] -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum PrecompileExit { Reverted = 0, Success = 1, @@ -116,6 +116,7 @@ pub enum NativePrecompile { LookupDelegatedAddress = 2, CallActor = 3, GetActorType = 4, + CallActorId = 5, } #[allow(dead_code)] @@ -157,6 +158,7 @@ impl NativePrecompile { } } +#[derive(Clone)] pub struct PrecompileTest { pub expected_exit_code: PrecompileExit, pub precompile_address: EthAddress, From b7492852c53e8395290ef425b3e653c0722144e3 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 19 Jan 2023 09:31:40 -0800 Subject: [PATCH 280/339] EVM: Test call output region (#1079) * add test for call output range * fmt --- actors/evm/tests/call.rs | 83 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 85cb78b39..3ec6aa7de 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -10,7 +10,7 @@ use ethers::providers::{MockProvider, Provider}; use ethers::types::Bytes; use evm::interpreter::address::EthAddress; use evm::interpreter::U256; -use evm::EVM_CONTRACT_REVERTED; +use evm::{Method, EVM_CONTRACT_REVERTED}; use fil_actor_evm as evm; use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -355,6 +355,87 @@ fn test_call_convert_to_send3() { 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: DAG_CBOR, data: vec![0xff; 2048] }; + + let cases = [(32, 64), (64, 64), (1024, 1025)]; + for (output_size, return_size) in cases { + rt.expect_send_generalized( + (&address).try_into().unwrap(), + Method::InvokeContract as u64, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + Some(large_ret.clone()), + ExitCode::OK, + ); + + 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() From 02df0ad5219c6247fc93edab1dcc8545d3da9337 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 19 Jan 2023 13:13:17 -0800 Subject: [PATCH 281/339] EVM: Remove get_actor_type and allow precompile lookup table to have empty slots (#1080) * wip * Cleanup from wip: - merged types - fix clippy (remove unsafe!) - remove unused fn in native type * remove get_actor_type precompile, fix clippy * use work-around to use const instead of lazy init * rewrite docs for a bit better clarity * remove need for workaround * fmt --- actors/evm/src/interpreter/precompiles/fvm.rs | 46 +-------- actors/evm/src/interpreter/precompiles/mod.rs | 84 ++++++---------- actors/evm/tests/precompile.rs | 95 +------------------ 3 files changed, 33 insertions(+), 192 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index ff78d2127..30e0135d3 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,56 +1,14 @@ use crate::EVM_MAX_RESERVED_METHOD; -use fil_actors_runtime::runtime::{builtins::Type, Runtime}; +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 num_traits::FromPrimitive; -use crate::interpreter::{instructions::call::CallKind, precompiles::NativeType, System, U256}; +use crate::interpreter::{instructions::call::CallKind, System, U256}; use super::{PrecompileContext, PrecompileError, PrecompileResult}; use crate::reader::ValueReader; -/// Read right padded BE encoded low u64 ID address from a u256 word. -/// - Reverts if the input is greater than `MAX::u64`. -/// - Returns variant of [`BuiltinType`] encoded as a u256 word. -pub(super) fn get_actor_type( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut reader = ValueReader::new(input); - let id: u64 = reader.read_value()?; - - // resolve type from code CID - let builtin_type = system - .rt - .get_actor_code_cid(&id) - .and_then(|cid| system.rt.resolve_builtin_actor_type(&cid)); - - let builtin_type = match builtin_type { - Some(t) => match t { - Type::Account | Type::EthAccount => NativeType::Account, - Type::Placeholder => NativeType::Placeholder, - Type::EVM => NativeType::EVMContract, - Type::Miner => NativeType::StorageProvider, - // Others - Type::PaymentChannel | Type::Multisig => NativeType::OtherTypes, - // Singletons (this should be caught earlier, but we are being exhaustive) - Type::Market - | Type::Power - | Type::Init - | Type::Cron - | Type::Reward - | Type::VerifiedRegistry - | Type::DataCap - | Type::EAM - | Type::System => NativeType::System, - }, - None => NativeType::NonExistent, - }; - - Ok(builtin_type.word_vec()) -} - /// !! DISABLED !! /// /// Params: diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 9886bb6de..b12d0329b 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -7,60 +7,24 @@ use substrate_bn::{CurveError, FieldError, GroupError}; use crate::reader::OverflowError; use super::{address::EthAddress, instructions::call::CallKind, System, U256}; - 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, get_actor_type, lookup_delegated_address, resolve_address}; +use fvm::{call_actor, call_actor_id, lookup_delegated_address, resolve_address}; -// really I'd want to have context as a type parameter, but since the table we generate must have the same types (or dyn) its messy -type PrecompileFn = unsafe fn(*mut System, &[u8], PrecompileContext) -> PrecompileResult; +type PrecompileFn = fn(&mut System, &[u8], PrecompileContext) -> PrecompileResult; pub type PrecompileResult = Result, PrecompileError>; pub const NATIVE_PRECOMPILE_ADDRESS_PREFIX: u8 = 0xFE; -macro_rules! precompiles { - ($($precompile:ident,)*) => { - mod trampolines { - use fil_actors_runtime::runtime::Runtime; - use crate::System; - use super::{PrecompileContext, PrecompileResult}; - $( - #[inline(always)] - pub unsafe fn $precompile(s: *mut System, inp: &[u8], ctx: PrecompileContext) -> PrecompileResult { - super::$precompile(&mut *s, inp, ctx) - } - )* - } - [ - $(trampolines::$precompile,)* - ] - } -} - -/// Generates a list of precompile smart contracts, index + 1 is the address. -const fn gen_evm_precompiles() -> [PrecompileFn; 9] { - precompiles! { - ec_recover, // 0x01 ecrecover - sha256, // 0x02 SHA2-256 - ripemd160, // 0x03 ripemd160 - identity, // 0x04 identity - modexp, // 0x05 modexp - ec_add, // 0x06 ecAdd - ec_mul, // 0x07 ecMul - ec_pairing, // 0x08 ecPairing - blake2f, // 0x09 blake2f - } -} +struct PrecompileTable([Option>; N]); -const fn gen_native_precompiles() -> [PrecompileFn; 5] { - precompiles! { - resolve_address, // 0xfe00..01 resolve_address - lookup_delegated_address, // 0xfe00..02 lookup_delegated_address - call_actor, // 0xfe00..03 call_actor - get_actor_type, // 0xfe00..04 get_actor_type - call_actor_id, // 0xfe00..05 call_actor_id +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() } } @@ -74,8 +38,27 @@ pub fn is_reserved_precompile_address(addr: &EthAddress) -> bool { pub struct Precompiles(PhantomData); impl Precompiles { - const EVM_PRECOMPILES: [PrecompileFn; 9] = gen_evm_precompiles(); - const NATIVE_PRECOMPILES: [PrecompileFn; 5] = gen_native_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; @@ -86,7 +69,6 @@ impl Precompiles { 0x00 => Self::EVM_PRECOMPILES.get(index), _ => None, } - .copied() } else { None } @@ -102,7 +84,7 @@ impl Precompiles { ) -> PrecompileResult { // First, try to call the precompile, if defined. let result = Self::lookup_precompile(precompile_addr) - .map(|precompile_fn| unsafe { precompile_fn(system, input, context) }) + .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 @@ -189,12 +171,6 @@ pub enum NativeType { OtherTypes = 6, } -impl NativeType { - fn word_vec(self) -> Vec { - U256::from(self as u32).to_bytes().to_vec() - } -} - #[cfg(test)] mod test { use fil_actors_runtime::test_utils::MockRuntime; diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 00aa25871..184db6fb6 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -3,11 +3,7 @@ mod asm; use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::{ - test_utils::{ - new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, - MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, - PLACEHOLDER_ACTOR_CODE_ID, - }, + test_utils::{new_bls_addr, MockRuntime}, EAM_ACTOR_ID, }; use fvm_shared::{address::Address as FILAddress, econ::TokenAmount, error::ExitCode, METHOD_SEND}; @@ -72,95 +68,6 @@ fn tester_bytecode() -> Vec { asm::new_contract("precompile-tester", &init, &body).unwrap() } -#[test] -fn test_native_actor_type() { - use evm::interpreter::precompiles::NativeType; - - let mut rt = util::construct_and_verify(tester_bytecode()); - - // 0x88 is an EVM actor - let evm_target = FILAddress::new_id(0x88); - rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); - - // f0 10 is the EAM actor (System) - let eam_target = FILAddress::new_id(10); - rt.set_address_actor_type(eam_target, *EAM_ACTOR_CODE_ID); - - // f0 7 is the Market actor (System) - let market_target = FILAddress::new_id(7); - rt.set_address_actor_type(market_target, *MARKET_ACTOR_CODE_ID); - - // f0 101 is an account - let account_target = FILAddress::new_id(101); - rt.set_address_actor_type(account_target, *ACCOUNT_ACTOR_CODE_ID); - - // f0 102 is a placeholder - let placeholder_target = FILAddress::new_id(102); - rt.set_address_actor_type(placeholder_target, *PLACEHOLDER_ACTOR_CODE_ID); - - // f0 103 is a storage provider - let miner_target = FILAddress::new_id(103); - rt.set_address_actor_type(miner_target, *MINER_ACTOR_CODE_ID); - - // f0 104 is a multisig - let other_target = FILAddress::new_id(104); - rt.set_address_actor_type(other_target, *MULTISIG_ACTOR_CODE_ID); - - fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { - let test = PrecompileTest { - precompile_address: NativePrecompile::GetActorType.eth_address(), - output_size: 32, - expected_exit_code: PrecompileExit::Success, - gas_avaliable: 10_000_000_000, - call_op: util::PrecompileCallOpcode::Call(0), - input: id_to_vec(&id), - expected_return: U256::from(expected as u32).to_bytes().to_vec(), - }; - test.run_test(rt); - } - - test_type(&mut rt, evm_target, NativeType::EVMContract); - test_type(&mut rt, eam_target, NativeType::System); - test_type(&mut rt, market_target, NativeType::System); - test_type(&mut rt, account_target, NativeType::Account); - test_type(&mut rt, placeholder_target, NativeType::Placeholder); - test_type(&mut rt, miner_target, NativeType::StorageProvider); - test_type(&mut rt, other_target, NativeType::OtherTypes); - test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); - - // invalid id parameter (over) - fn test_type_invalid(rt: &mut MockRuntime, input: Vec) { - let test = PrecompileTest { - precompile_address: NativePrecompile::GetActorType.eth_address(), - output_size: 32, - expected_exit_code: PrecompileExit::Reverted, - gas_avaliable: 10_000_000_000, - call_op: util::PrecompileCallOpcode::Call(0), - expected_return: vec![], - input, - }; - test.run_test(rt); - } - - // extra bytes - test_type_invalid(&mut rt, vec![0xff; 64]); - // single byte get padded and is invalid - test_type_invalid(&mut rt, vec![0xff]); - - // VERY weird and NOBODY should depend on this, but this is expected behavior soo - // ¯\_(ツ)_/¯ - { - // f0 (0xff00) - let padded_target = FILAddress::new_id(0xff00); - rt.set_address_actor_type(padded_target, *EVM_ACTOR_CODE_ID); - - // not enough bytes (but still valid id when padded) - let mut input = vec![0u8; 31]; - input[30] = 0xff; // will get padded to 0xff00 - test_type(&mut rt, evm_target, NativeType::EVMContract); - } -} - fn resolve_address_contract() -> Vec { let init = ""; let body = r#" From 8a6a447ed60c89448e14fdef37732711b84c2cf9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 19 Jan 2023 14:33:08 -0800 Subject: [PATCH 282/339] test: check placeholder/non-existent behavior on extcodesize (#1077) --- actors/evm/tests/ext_opcodes.rs | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 1c5a7a4f1..55e626e4b 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -30,6 +30,8 @@ fn test_extcodesize() { # 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: @@ -59,6 +61,21 @@ native_account: 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() @@ -88,6 +105,10 @@ 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); @@ -138,6 +159,26 @@ native_account: 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] From a0beee1f21d6890000301eb95399e1a0a525fc9c Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 19 Jan 2023 14:55:29 -0800 Subject: [PATCH 283/339] EVM: Remove get_randomness precompile (#1082) remove get_randomness precompile --- actors/evm/src/interpreter/precompiles/fvm.rs | 54 ------------------- 1 file changed, 54 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 30e0135d3..bf9337f5f 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -2,66 +2,12 @@ use crate::EVM_MAX_RESERVED_METHOD; 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 num_traits::FromPrimitive; use crate::interpreter::{instructions::call::CallKind, System, U256}; use super::{PrecompileContext, PrecompileError, PrecompileResult}; use crate::reader::ValueReader; -/// !! DISABLED !! -/// -/// Params: -/// -/// | Param | Value | -/// |------------------|---------------------------| -/// | randomness_type | U256 - low i32: `Chain`(0) OR `Beacon`(1) | -/// | personalization | U256 - low i64 | -/// | randomness_epoch | U256 - low i64 | -/// | entropy_length | U256 - low u32 | -/// | entropy | input\[32..] (right padded)| -/// -/// any bytes in between values are ignored -/// -/// Returns empty array if invalid randomness type -/// Errors if unable to fetch randomness -#[allow(unused)] -pub(super) fn get_randomness( - system: &mut System, - input: &[u8], - _: PrecompileContext, -) -> PrecompileResult { - let mut input_params = ValueReader::new(input); - - #[derive(num_derive::FromPrimitive)] - #[repr(i32)] - enum RandomnessType { - Chain = 0, - Beacon = 1, - } - - let randomness_type = RandomnessType::from_i32(input_params.read_value::()?); - let personalization = input_params.read_value::()?; - let rand_epoch = input_params.read_value::()?; - let entropy_len = input_params.read_value::()? as usize; - - let entropy = input_params.read_padded(entropy_len); - - let randomness = match randomness_type { - Some(RandomnessType::Chain) => system - .rt - .user_get_randomness_from_chain(personalization, rand_epoch, &entropy) - .map(|a| a.to_vec()), - Some(RandomnessType::Beacon) => system - .rt - .user_get_randomness_from_beacon(personalization, rand_epoch, &entropy) - .map(|a| a.to_vec()), - None => Ok(Vec::new()), - }; - - randomness.map_err(|_| PrecompileError::InvalidInput) -} - /// 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( From 94f92ec35c18c12309ada93d34bcd37581f89e31 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jan 2023 07:56:30 -0800 Subject: [PATCH 284/339] EVM: Remove StatusCode and return meaningful actor errors (#1083) Remove "StatusCode" as we were just turning those into actor errors anyways. Instead, we return meaningful exit codes: - 33: Invalid Instruction (assert) - 34: Undefined Instruction (bad instruction) - 35: Stack Underflow - 36: Stack Underflow - 37: Illegal Memory Access - 38: Bad Jump Destination This change also ensures that meaningful error messages get sent through the FVM (although they're not committed on-chain). fixes #1195 --- actors/evm/src/interpreter/execution.rs | 21 ++-- .../evm/src/interpreter/instructions/call.rs | 24 +++-- .../src/interpreter/instructions/context.rs | 31 +++--- .../src/interpreter/instructions/control.rs | 79 ++++++++++----- .../evm/src/interpreter/instructions/ext.rs | 19 ++-- .../evm/src/interpreter/instructions/hash.rs | 9 +- .../src/interpreter/instructions/lifecycle.rs | 23 +++-- .../src/interpreter/instructions/log_event.rs | 10 +- .../src/interpreter/instructions/memory.rs | 59 ++++++----- .../evm/src/interpreter/instructions/mod.rs | 4 +- .../evm/src/interpreter/instructions/stack.rs | 14 +-- .../evm/src/interpreter/instructions/state.rs | 7 +- .../src/interpreter/instructions/storage.rs | 10 +- actors/evm/src/interpreter/mod.rs | 2 +- actors/evm/src/interpreter/output.rs | 98 +------------------ actors/evm/src/interpreter/stack.rs | 54 +++++----- actors/evm/src/interpreter/system.rs | 58 ++++++----- actors/evm/src/lib.rs | 19 ++-- runtime/src/actor_error.rs | 3 + 19 files changed, 253 insertions(+), 291 deletions(-) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index cc98fce3b..4088d14e7 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,11 +1,11 @@ #![allow(dead_code)] +use fil_actors_runtime::ActorError; use fvm_shared::econ::TokenAmount; use super::address::EthAddress; use { super::instructions, - super::StatusCode, crate::interpreter::memory::Memory, crate::interpreter::stack::Stack, crate::interpreter::{Bytecode, Output, System}, @@ -60,7 +60,10 @@ macro_rules! def_opcodes { pub const fn jumptable<'r, 'a, RT: Runtime>() -> [Instruction<'r, 'a, RT>; 256] { def_ins_raw! { UNDEFINED(_m) { - Err(StatusCode::UndefinedInstruction) + Err(ActorError::unchecked( + crate::EVM_CONTRACT_UNDEFINED_INSTRUCTION, + "undefined instruction".into() + )) } } $(def_ins_raw! { @@ -80,7 +83,7 @@ macro_rules! def_opcodes { 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<(), StatusCode> { + 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; @@ -92,11 +95,11 @@ macro_rules! def_ins_raw { pub mod opcodes { use super::instructions; use super::Machine; - use crate::interpreter::StatusCode; use fil_actors_runtime::runtime::Runtime; + use fil_actors_runtime::ActorError; pub type Instruction<'r, 'a, RT> = - unsafe fn(*mut Machine<'r, 'a, RT>) -> Result<(), StatusCode>; + unsafe fn(*mut Machine<'r, 'a, RT>) -> Result<(), ActorError>; def_opcodes! { 0x00: STOP, @@ -255,12 +258,12 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { Machine { system, state, bytecode, pc: 0, output: Output::default() } } - pub fn execute(mut self) -> Result { + 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); + return Err(e.wrap(format!("ABORT(pc={})", self.pc))); } } @@ -268,7 +271,7 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } #[inline(always)] - fn step(&mut self) -> Result<(), StatusCode> { + fn step(&mut self) -> Result<(), ActorError> { let op = self.bytecode[self.pc]; unsafe { Self::JMPTABLE[op as usize](self) } } @@ -280,6 +283,6 @@ pub fn execute( bytecode: &Bytecode, runtime: &mut ExecutionState, system: &mut System, -) -> Result { +) -> Result { Machine::new(system, runtime, bytecode).execute() } diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 8ccdf6866..d9b055ffb 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -12,7 +12,6 @@ use { super::memory::{copy_to_memory, get_memory_region}, crate::interpreter::address::EthAddress, crate::interpreter::instructions::memory::MemoryRegion, - crate::interpreter::output::StatusCode, crate::interpreter::precompiles, crate::interpreter::ExecutionState, crate::interpreter::System, @@ -36,7 +35,7 @@ pub fn calldataload( state: &mut ExecutionState, _: &System, index: U256, -) -> Result { +) -> Result { let input_len = state.input_data.len(); Ok(index .try_into() @@ -55,7 +54,7 @@ pub fn calldataload( pub fn calldatasize( state: &mut ExecutionState, _: &System, -) -> Result { +) -> Result { Ok(u128::try_from(state.input_data.len()).unwrap().into()) } @@ -66,7 +65,7 @@ pub fn calldatacopy( mem_index: U256, input_index: U256, size: U256, -) -> Result<(), StatusCode> { +) -> Result<(), ActorError> { copy_to_memory(&mut state.memory, mem_index, size, input_index, &state.input_data, true) } @@ -75,7 +74,7 @@ pub fn codesize( _state: &mut ExecutionState, _: &System, code: &[u8], -) -> Result { +) -> Result { Ok(U256::from(code.len())) } @@ -87,7 +86,7 @@ pub fn codecopy( mem_index: U256, input_index: U256, size: U256, -) -> Result<(), StatusCode> { +) -> Result<(), ActorError> { copy_to_memory(&mut state.memory, mem_index, size, input_index, code, true) } @@ -102,7 +101,7 @@ pub fn call_call( input_size: U256, output_offset: U256, output_size: U256, -) -> Result { +) -> Result { call_generic( state, system, @@ -121,7 +120,7 @@ pub fn call_delegatecall( input_size: U256, output_offset: U256, output_size: U256, -) -> Result { +) -> Result { call_generic( state, system, @@ -140,7 +139,7 @@ pub fn call_staticcall( input_size: U256, output_offset: U256, output_size: U256, -) -> Result { +) -> Result { call_generic( state, system, @@ -154,18 +153,17 @@ pub fn call_generic( system: &mut System, kind: CallKind, params: (U256, U256, U256, U256, U256, U256, U256), -) -> Result { +) -> Result { let ExecutionState { stack: _, memory, .. } = state; let (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(StatusCode::StaticModeViolation); + return Err(ActorError::read_only("cannot transfer value when read-only".into())); } - let input_region = get_memory_region(memory, input_offset, input_size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + 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 diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index b206f0974..367b5ff67 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,7 +1,8 @@ +use fil_actors_runtime::ActorError; use fvm_shared::clock::ChainEpoch; use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, }; @@ -10,7 +11,7 @@ pub fn blockhash( _state: &mut ExecutionState, system: &System, bn: U256, -) -> Result { +) -> Result { let result = bn .try_into() .ok() @@ -35,7 +36,7 @@ pub fn blockhash( } #[inline] -pub fn caller(state: &mut ExecutionState, _: &System) -> Result { +pub fn caller(state: &mut ExecutionState, _: &System) -> Result { Ok(state.caller.as_evm_word()) } @@ -43,7 +44,7 @@ pub fn caller(state: &mut ExecutionState, _: &System) -> Result, -) -> Result { +) -> Result { Ok(state.receiver.as_evm_word()) } @@ -51,7 +52,7 @@ pub fn address( pub fn origin( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> Result { let origin_addr = system .resolve_ethereum_address(&system.rt.message().origin()) .expect("failed to resolve origin address"); @@ -62,7 +63,7 @@ pub fn origin( pub fn call_value( state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { Ok(U256::from(&state.value_received)) } @@ -70,7 +71,7 @@ pub fn call_value( pub fn coinbase( _state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { // TODO do we want to return the zero ID address, or just a plain 0? Ok(U256::zero()) } @@ -79,13 +80,13 @@ pub fn coinbase( pub fn gas_price( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> 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 { +pub fn gas(_state: &mut ExecutionState, system: &System) -> Result { Ok(U256::from(system.rt.gas_available())) } @@ -93,7 +94,7 @@ pub fn gas(_state: &mut ExecutionState, system: &System) -> Result pub fn timestamp( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> Result { Ok(U256::from(system.rt.tipset_timestamp())) } @@ -101,7 +102,7 @@ pub fn timestamp( pub fn block_number( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> Result { Ok(U256::from(system.rt.curr_epoch())) } @@ -110,7 +111,7 @@ pub fn block_number( pub fn prevrandao( _state: &mut ExecutionState, system: &mut System, -) -> Result { +) -> 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 @@ -122,7 +123,7 @@ pub fn prevrandao( pub fn gas_limit( _state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; Ok(U256::from(BLOCK_GAS_LIMIT)) } @@ -131,7 +132,7 @@ pub fn gas_limit( pub fn chain_id( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> Result { Ok(U256::from_u64(system.rt.chain_id().into())) } @@ -139,6 +140,6 @@ pub fn chain_id( pub fn base_fee( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> Result { Ok(U256::from(&system.rt.base_fee())) } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 156ac582e..4649cf394 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -1,17 +1,21 @@ use bytes::Bytes; +use fil_actors_runtime::{ActorError, AsActorError}; -use crate::interpreter::{memory::Memory, output::Outcome, Output}; +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::output::StatusCode, crate::interpreter::Bytecode, crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, }; #[inline] -pub fn nop(_state: &mut ExecutionState, _system: &System) -> Result<(), StatusCode> { +pub fn nop(_state: &mut ExecutionState, _system: &System) -> Result<(), ActorError> { Ok(()) } @@ -19,8 +23,8 @@ pub fn nop(_state: &mut ExecutionState, _system: &System) -> Resul pub fn invalid( _state: &mut ExecutionState, _system: &System, -) -> Result<(), StatusCode> { - Err(StatusCode::InvalidInstruction) +) -> Result<(), ActorError> { + Err(ActorError::unchecked(EVM_CONTRACT_INVALID_INSTRUCTION, "invalid instruction".into())) } #[inline] @@ -29,7 +33,7 @@ pub fn ret( _system: &System, offset: U256, size: U256, -) -> Result { +) -> Result { exit(&mut state.memory, offset, size, Outcome::Return) } @@ -39,7 +43,7 @@ pub fn revert( _system: &System, offset: U256, size: U256, -) -> Result { +) -> Result { exit(&mut state.memory, offset, size, Outcome::Revert) } @@ -47,7 +51,7 @@ pub fn revert( pub fn stop( _state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { Ok(Output { return_data: Bytes::new(), outcome: Outcome::Return }) } @@ -57,11 +61,10 @@ fn exit( offset: U256, size: U256, status: Outcome, -) -> Result { +) -> Result { Ok(Output { outcome: status, - return_data: super::memory::get_memory_region(memory, offset, size) - .map_err(|_| StatusCode::InvalidMemoryAccess)? + return_data: super::memory::get_memory_region(memory, offset, size)? .map(|region| memory[region.offset..region.offset + region.size.get()].to_vec().into()) .unwrap_or_default(), }) @@ -71,7 +74,7 @@ fn exit( pub fn returndatasize( state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { Ok(U256::from(state.return_data.len())) } @@ -82,17 +85,36 @@ pub fn returndatacopy( mem_index: U256, input_index: U256, size: U256, -) -> Result<(), StatusCode> { - let region = get_memory_region(&mut state.memory, mem_index, size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; +) -> Result<(), ActorError> { + let region = get_memory_region(&mut state.memory, mem_index, size)?; - let src = input_index.try_into().map_err(|_| StatusCode::InvalidMemoryAccess)?; + 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(StatusCode::InvalidMemoryAccess); + return Err(ActorError::unchecked( + EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + format!( + "returndatacopy start {} exceeds return-data length {}", + src, + state.return_data.len() + ), + )); } - if src + region.as_ref().map(|r| r.size.get()).unwrap_or(0) > state.return_data.len() { - return Err(StatusCode::InvalidMemoryAccess); + 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 { @@ -104,21 +126,28 @@ pub fn returndatacopy( } #[inline] -pub fn jump(bytecode: &Bytecode, _pc: usize, dest: U256) -> Result { - let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; +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(StatusCode::BadJumpDestination); + 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 { +pub fn jumpi(bytecode: &Bytecode, pc: usize, dest: U256, test: U256) -> Result { if !test.is_zero() { - let dst = dest.try_into().map_err(|_| StatusCode::BadJumpDestination)?; + let dst = + dest.try_into().context_code(EVM_CONTRACT_BAD_JUMPDEST, "jumpdest exceeds u32")?; if !bytecode.valid_jump_destination(dst) { - return Err(StatusCode::BadJumpDestination); + return Err(ActorError::unchecked( + EVM_CONTRACT_BAD_JUMPDEST, + format!("jumpdest {dst} is invalid"), + )); } // skip the JMPDEST noop sled Ok(dst + 1) diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 9f508fc34..f22a5f12c 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -2,15 +2,16 @@ use crate::interpreter::instructions::memory::copy_to_memory; use crate::interpreter::{address::EthAddress, precompiles::Precompiles}; use crate::{BytecodeHash, U256}; use cid::Cid; -use fil_actors_runtime::deserialize_block; 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, StatusCode, System}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; @@ -18,7 +19,7 @@ pub fn extcodesize( _state: &mut ExecutionState, system: &mut System, addr: U256, -) -> Result { +) -> 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 @@ -38,7 +39,7 @@ pub fn extcodehash( _state: &mut ExecutionState, system: &mut System, addr: U256, -) -> Result { +) -> 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. @@ -73,7 +74,7 @@ pub fn extcodecopy( dest_offset: U256, data_offset: U256, size: U256, -) -> Result<(), StatusCode> { +) -> 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(), @@ -133,16 +134,14 @@ pub fn get_evm_bytecode_cid( pub fn get_evm_bytecode( system: &mut System, addr: &Address, -) -> Result, StatusCode> { +) -> 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. - .map_err(|e| { - StatusCode::InternalError(format!("failed to get bytecode block: {}", &e)) - })? - .ok_or_else(|| ActorError::not_found("bytecode block not found".to_string()))?; + .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()) diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index 0526d2092..b0e459c8b 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -1,6 +1,8 @@ +use fil_actors_runtime::ActorError; + use { super::memory::get_memory_region, - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, fvm_shared::crypto::hash::SupportedHashes, }; @@ -10,9 +12,8 @@ pub fn keccak256( system: &System, index: U256, size: U256, -) -> Result { - let region = get_memory_region(&mut state.memory, index, size) // - .map_err(|_| StatusCode::InvalidMemoryAccess)?; +) -> Result { + let region = get_memory_region(&mut state.memory, index, size)?; let (buf, size) = system.rt.hash_64( SupportedHashes::Keccak256, diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 9ad64387e..96fcf8df4 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,6 +1,7 @@ use bytes::Bytes; use fil_actors_runtime::deserialize_block; +use fil_actors_runtime::ActorError; use fil_actors_runtime::EAM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{strict_bytes, tuple::*}; @@ -15,7 +16,7 @@ use crate::interpreter::{address::EthAddress, U256}; use super::memory::{get_memory_region, MemoryRegion}; use { - crate::interpreter::{ExecutionState, StatusCode, System}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; @@ -51,9 +52,9 @@ pub fn create( value: U256, offset: U256, size: U256, -) -> Result { +) -> Result { if system.readonly { - return Err(StatusCode::StaticModeViolation); + return Err(ActorError::read_only("create called while read-only".into())); } let ExecutionState { stack: _, memory, .. } = state; @@ -62,8 +63,7 @@ pub fn create( if value > system.rt.current_balance() { return Ok(U256::zero()); } - let input_region = - get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let input_region = get_memory_region(memory, offset, size)?; let input_data = if let Some(MemoryRegion { offset, size }) = input_region { &memory[offset..][..size.get()] @@ -83,9 +83,9 @@ pub fn create2( offset: U256, size: U256, salt: U256, -) -> Result { +) -> Result { if system.readonly { - return Err(StatusCode::StaticModeViolation); + return Err(ActorError::read_only("create2 called while read-only".into())); } let ExecutionState { stack: _, memory, .. } = state; @@ -96,8 +96,7 @@ pub fn create2( return Ok(U256::zero()); } - let input_region = - get_memory_region(memory, offset, size).map_err(|_| StatusCode::InvalidMemoryAccess)?; + let input_region = get_memory_region(memory, offset, size)?; // BE encoded array let salt: [u8; 32] = salt.into(); @@ -120,7 +119,7 @@ fn create_init( params: Option, method: MethodNum, value: TokenAmount, -) -> Result { +) -> Result { // send bytecode & params to EAM to generate the address and contract let ret = system.send(&EAM_ACTOR_ADDR, method, params, value, None, SendFlags::default()); @@ -157,11 +156,11 @@ pub fn selfdestruct( _state: &mut ExecutionState, system: &mut System, beneficiary: U256, -) -> Result { +) -> Result { use crate::interpreter::output::Outcome; if system.readonly { - return Err(StatusCode::StaticModeViolation); + return Err(ActorError::read_only("selfdestruct called while read-only".into())); } // Try to give funds to the beneficiary. If this fails, we just keep them. diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs index 3da15b2f0..653e433a3 100644 --- a/actors/evm/src/interpreter/instructions/log_event.rs +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -1,8 +1,9 @@ use crate::interpreter::instructions::memory::get_memory_region; +use fil_actors_runtime::ActorError; use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; use fvm_shared::event::{Entry, Flags}; use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, }; @@ -20,17 +21,16 @@ pub fn log( mem_index: U256, size: U256, topics: &[U256], -) -> Result<(), StatusCode> { +) -> Result<(), ActorError> { if system.readonly { - return Err(StatusCode::StaticModeViolation); + 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) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; + 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. diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index f974a0a50..8ae9d0107 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -1,8 +1,12 @@ #!allow[clippy::result-unit-err] +use fil_actors_runtime::{ActorError, AsActorError}; + +use crate::EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS; + use { crate::interpreter::memory::Memory, - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, std::num::NonZeroUsize, }; @@ -17,7 +21,7 @@ pub struct MemoryRegion { } #[inline] -fn grow_memory(mem: &mut Memory, mut new_size: usize) -> Result<(), ()> { +fn grow_memory(mem: &mut Memory, mut new_size: usize) { // Align to the next u256. // Guaranteed to not overflow. let alignment = new_size % WORD_SIZE; @@ -25,28 +29,38 @@ fn grow_memory(mem: &mut Memory, mut new_size: usize) -> Result<(), ()> { new_size += WORD_SIZE - alignment; } mem.grow(new_size); - Ok(()) } #[inline] -#[allow(clippy::result_unit_err)] pub fn get_memory_region( mem: &mut Memory, offset: impl TryInto, size: impl TryInto, -) -> Result, ()> { +) -> 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(|_| ())?; + 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(|_| ())?; - let new_size: u32 = offset.checked_add(size).ok_or(())?; + 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")?; let current_size = mem.len(); if new_size as usize > current_size { - grow_memory(mem, new_size as usize)?; + grow_memory(mem, new_size as usize); } Ok(Some(MemoryRegion { @@ -62,9 +76,8 @@ pub fn copy_to_memory( data_offset: U256, data: &[u8], zero_fill: bool, -) -> Result<(), StatusCode> { - let region = get_memory_region(memory, dest_offset, dest_size) - .map_err(|_| StatusCode::InvalidMemoryAccess)?; +) -> Result<(), ActorError> { + let region = get_memory_region(memory, dest_offset, dest_size)?; #[inline(always)] fn min(a: U256, b: usize) -> usize { @@ -98,10 +111,8 @@ pub fn mload( state: &mut ExecutionState, _system: &System, index: U256, -) -> Result { - let region = get_memory_region(&mut state.memory, index, WORD_SIZE) - .map_err(|_| StatusCode::InvalidMemoryAccess)? - .expect("empty region"); +) -> Result { + let region = get_memory_region(&mut state.memory, index, WORD_SIZE)?.expect("empty region"); let value = U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); @@ -114,10 +125,8 @@ pub fn mstore( _system: &System, index: U256, value: U256, -) -> Result<(), StatusCode> { - let region = get_memory_region(&mut state.memory, index, WORD_SIZE) - .map_err(|_| StatusCode::InvalidMemoryAccess)? - .expect("empty region"); +) -> Result<(), ActorError> { + let region = get_memory_region(&mut state.memory, index, WORD_SIZE)?.expect("empty region"); let mut bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut bytes); @@ -132,10 +141,8 @@ pub fn mstore8( _system: &System, index: U256, value: U256, -) -> Result<(), StatusCode> { - let region = get_memory_region(&mut state.memory, index, 1) - .map_err(|_| StatusCode::InvalidMemoryAccess)? - .expect("empty region"); +) -> 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; @@ -147,7 +154,7 @@ pub fn mstore8( pub fn msize( state: &mut ExecutionState, _system: &System, -) -> Result { +) -> Result { Ok(u64::try_from(state.memory.len()).unwrap().into()) } @@ -167,7 +174,7 @@ mod tests { &[], true, ); - assert_eq!(result, Err(StatusCode::InvalidMemoryAccess)); + assert_eq!(result.unwrap_err().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); } #[test] diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index f7f2d769e..d0583c052 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -16,9 +16,9 @@ pub mod state; pub mod storage; use crate::interpreter::execution::Machine; -use crate::interpreter::output::StatusCode; use crate::interpreter::U256; use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::ActorError; macro_rules! rev { ($($args:ident),*) => { @@ -36,7 +36,7 @@ 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<(), StatusCode> { + pub fn $op<'r, 'a, RT: Runtime + 'a>($m: &mut Machine<'r, 'a, RT> ) -> Result<(), ActorError> { $($body)* } } diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index dffa31ae5..80ff79fdd 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -1,7 +1,9 @@ #![allow(clippy::missing_safety_doc)] -use crate::interpreter::StatusCode; -use {crate::interpreter::stack::Stack, crate::interpreter::U256}; +use fil_actors_runtime::ActorError; + +use crate::interpreter::stack::Stack; +use crate::interpreter::U256; macro_rules! be_u64 { ($byte:expr) => {$byte as u64}; @@ -16,7 +18,7 @@ macro_rules! be_shift { } #[inline] -pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result { +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) @@ -46,17 +48,17 @@ pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result(stack: &mut Stack) -> Result<(), StatusCode> { +pub(crate) fn dup(stack: &mut Stack) -> Result<(), ActorError> { stack.dup(HEIGHT) } #[inline] -pub(crate) fn swap(stack: &mut Stack) -> Result<(), StatusCode> { +pub(crate) fn swap(stack: &mut Stack) -> Result<(), ActorError> { stack.swap_top(HEIGHT) } #[inline] -pub(crate) fn pop(stack: &mut Stack) -> Result<(), StatusCode> { +pub(crate) fn pop(stack: &mut Stack) -> Result<(), ActorError> { stack.drop() } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index a26c4b91f..db81d9147 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -1,9 +1,10 @@ +use fil_actors_runtime::ActorError; use fvm_shared::address::Address; use crate::U256; use { crate::interpreter::address::EthAddress, - crate::interpreter::{ExecutionState, StatusCode, System}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; @@ -12,7 +13,7 @@ pub fn balance( _state: &mut ExecutionState, system: &System, actor: U256, -) -> Result { +) -> Result { let addr: EthAddress = actor.into(); let addr: Address = addr.into(); @@ -29,7 +30,7 @@ pub fn balance( pub fn selfbalance( _state: &mut ExecutionState, system: &System, -) -> Result { +) -> 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())) diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index 52ded26a6..d992e9cd9 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -1,5 +1,7 @@ +use fil_actors_runtime::ActorError; + use { - crate::interpreter::{ExecutionState, StatusCode, System, U256}, + crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, }; @@ -8,7 +10,7 @@ pub fn sload( _state: &mut ExecutionState, system: &mut System, location: U256, -) -> Result { +) -> Result { // get from storage and place on stack system.get_storage(location) } @@ -19,9 +21,9 @@ pub fn sstore( system: &mut System, key: U256, value: U256, -) -> Result<(), StatusCode> { +) -> Result<(), ActorError> { if system.readonly { - return Err(StatusCode::StaticModeViolation); + return Err(ActorError::read_only("store called while read-only".into())); } system.set_storage(key, value) diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 95ca0ea89..1eacff968 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -12,7 +12,7 @@ pub mod uints; pub use { bytecode::Bytecode, execution::{execute, ExecutionState}, - output::{Output, StatusCode}, + output::Output, system::System, uints::{U256, U512}, }; diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index fb06a2b57..a4b0e158b 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,5 +1,6 @@ -use fil_actors_runtime::ActorError as RTActorError; -use {bytes::Bytes, std::fmt::Debug, strum_macros::Display}; +use std::fmt::Debug; + +use bytes::Bytes; #[derive(Debug, Clone, PartialEq, Eq, Default)] pub enum Outcome { @@ -16,96 +17,3 @@ pub struct Output { /// The return data. pub return_data: Bytes, } - -/// Message status code. -#[must_use] -#[derive(Clone, Debug, Display, PartialEq, Eq)] -pub enum StatusCode { - /// The designated INVALID instruction has been hit during execution. - /// - /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md) - /// defines the instruction 0xfe as INVALID instruction to indicate execution - /// abortion coming from high-level languages. This status code is reported - /// in case this INVALID instruction has been encountered. - #[strum(serialize = "invalid instruction")] - InvalidInstruction, - - /// An argument passed to an instruction does not meet expectations. - #[strum(serialize = "invalid argument")] - InvalidArgument(String), - - /// An undefined instruction has been encountered. - #[strum(serialize = "undefined instruction")] - UndefinedInstruction, - - /// The execution has attempted to put more items on the EVM stack - /// than the specified limit. - #[strum(serialize = "stack overflow")] - StackOverflow, - - /// Execution of an opcode has required more items on the EVM stack. - #[strum(serialize = "stack underflow")] - StackUnderflow, - - /// Execution has violated the jump destination restrictions. - #[strum(serialize = "bad jump destination")] - BadJumpDestination, - - /// Tried to read outside memory bounds. - /// - /// An example is RETURNDATACOPY reading past the available buffer. - #[strum(serialize = "invalid memory access")] - InvalidMemoryAccess, - - /// Call depth has exceeded the limit (if any) - #[strum(serialize = "call depth exceeded")] - CallDepthExceeded, - - /// Tried to execute an operation which is restricted in static mode. - #[strum(serialize = "static mode violation")] - StaticModeViolation, - - /// A call to a precompiled or system contract has ended with a failure. - /// - /// An example: elliptic curve functions handed invalid EC points. - #[strum(serialize = "precompile failure")] - PrecompileFailure, - - /// Contract validation has failed. - #[strum(serialize = "contract validation failure")] - ContractValidationFailure, - - /// An argument to a state accessing method has a value outside of the - /// accepted range of values. - #[strum(serialize = "argument out of range")] - ArgumentOutOfRange(String), - - /// The caller does not have enough funds for value transfer. - #[strum(serialize = "insufficient balance")] - InsufficientBalance, - - /// EVM implementation generic internal error. - #[strum(serialize = "internal error")] - InternalError(String), - - /// Invalid Address - #[strum(serialize = "bad address")] - BadAddress(String), - - /// Nested Actor invocation Error - #[strum(serialize = "runtime actor error")] - ActorError(RTActorError), -} - -// Map ActorError to a generic internal error status code. -impl From for StatusCode { - fn from(ae: RTActorError) -> Self { - Self::ActorError(ae) - } -} - -impl From for StatusCode { - fn from(err: fvm_ipld_encoding::Error) -> Self { - Self::InternalError(format!("IPLD error: {:?}", &err)) - } -} diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index d8cfa3aca..74a0a258b 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -1,8 +1,8 @@ #![allow(dead_code, clippy::missing_safety_doc)] -use crate::interpreter::U256; +use fil_actors_runtime::{ActorError, AsActorError}; -use super::StatusCode; +use crate::{interpreter::U256, EVM_CONTRACT_STACK_OVERFLOW, EVM_CONTRACT_STACK_UNDERFLOW}; /// Ethereum Yellow Paper (9.1) pub const STACK_SIZE: usize = 1024; @@ -37,9 +37,9 @@ impl Stack { } #[inline(always)] - pub fn push(&mut self, value: U256) -> Result<(), StatusCode> { + pub fn push(&mut self, value: U256) -> Result<(), ActorError> { if self.stack.len() >= STACK_SIZE { - Err(StatusCode::StackOverflow) + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) } else { self.stack.push(value); Ok(()) @@ -47,9 +47,12 @@ impl Stack { } #[inline] - pub fn pop_many(&mut self) -> Result<&[U256; S], StatusCode> { + pub fn pop_many(&mut self) -> Result<&[U256; S], ActorError> { if self.len() < S { - return Err(StatusCode::StackUnderflow); + return Err(ActorError::unchecked( + EVM_CONTRACT_STACK_UNDERFLOW, + "stack underflow".into(), + )); } let new_len = self.len() - S; unsafe { @@ -66,21 +69,21 @@ impl Stack { #[inline(always)] /// Ensures at least one more item is able to be allocated on the stack. - pub fn ensure_one(&self) -> Result<(), StatusCode> { + pub fn ensure_one(&self) -> Result<(), ActorError> { if self.stack.len() >= STACK_SIZE { - Err(StatusCode::StackOverflow) + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) } else { Ok(()) } } #[inline] - pub fn dup(&mut self, i: usize) -> Result<(), StatusCode> { + pub fn dup(&mut self, i: usize) -> Result<(), ActorError> { let len = self.stack.len(); if len >= STACK_SIZE { - Err(StatusCode::StackOverflow) + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) } else if i > len { - Err(StatusCode::StackUnderflow) + 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 @@ -96,26 +99,29 @@ impl Stack { } #[inline] - pub fn swap_top(&mut self, i: usize) -> Result<(), StatusCode> { + pub fn swap_top(&mut self, i: usize) -> Result<(), ActorError> { let len = self.stack.len(); if len <= i { - return Err(StatusCode::StackUnderflow); + 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().ok_or(StatusCode::StackUnderflow) + pub fn pop(&mut self) -> Result { + self.stack.pop().context_code(EVM_CONTRACT_STACK_UNDERFLOW, "stack underflow") } #[inline] - pub fn drop(&mut self) -> Result<(), StatusCode> { + pub fn drop(&mut self) -> Result<(), ActorError> { if self.stack.pop().is_some() { Ok(()) } else { - Err(StatusCode::StackUnderflow) + Err(ActorError::unchecked(EVM_CONTRACT_STACK_UNDERFLOW, "stack underflow".into())) } } } @@ -157,13 +163,13 @@ fn test_stack_swap() { #[test] fn test_stack_swap_underflow() { let mut stack = Stack::new(); - assert_eq!(stack.swap_top(1).unwrap_err(), StatusCode::StackUnderflow); + 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(), StatusCode::StackUnderflow); + 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(), StatusCode::StackUnderflow); + assert_eq!(stack.swap_top(2).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); } #[test] @@ -182,9 +188,9 @@ fn test_stack_dup() { #[test] fn test_stack_dup_underflow() { let mut stack = Stack::new(); - assert_eq!(stack.dup(1).unwrap_err(), StatusCode::StackUnderflow); + 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(), StatusCode::StackUnderflow); + assert_eq!(stack.dup(2).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); } #[test] @@ -194,8 +200,8 @@ fn test_stack_overflow() { stack.push(i.into()).unwrap(); } - assert_eq!(stack.push(1024.into()).unwrap_err(), StatusCode::StackOverflow); - assert_eq!(stack.dup(1).unwrap_err(), StatusCode::StackOverflow); + 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 index 39266d39b..8e711aac6 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -24,7 +24,7 @@ use crate::BytecodeHash; use super::{address::EthAddress, Bytecode}; use { - crate::interpreter::{StatusCode, U256}, + crate::interpreter::U256, cid::Cid, fil_actors_runtime::{runtime::Runtime, ActorError}, fvm_ipld_blockstore::Blockstore, @@ -349,23 +349,28 @@ impl<'r, RT: Runtime> System<'r, RT> { } /// Get value of a storage key. - pub fn get_storage(&mut self, key: U256) -> Result { + pub fn get_storage(&mut self, key: U256) -> Result { Ok(self .slots .get(&key) - .map_err(|e| StatusCode::InternalError(e.to_string()))? + .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<(), StatusCode> { + 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()) + 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)) - } - .map_err(|e| StatusCode::InternalError(e.to_string()))?; + 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. @@ -379,35 +384,34 @@ impl<'r, RT: Runtime> System<'r, RT> { /// - 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 { + 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().map_err(|_| { - StatusCode::BadAddress("invalid ethereum address length".into()) - })?; + 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).ok_or_else(|| { - StatusCode::BadAddress(format!( - "non-ethereum address {addr} cannot be resolved 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. - // - // TODO: this method doesn't differentiate between "actor doesn't have a delegated - // address" and "actor doesn't exist". We should probably fix that and return an error if - // the actor doesn't exist. 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().map_err(|_| { - StatusCode::BadAddress("invalid ethereum address length".into()) - })?; + 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. @@ -416,15 +420,15 @@ impl<'r, RT: Runtime> System<'r, RT> { } /// Gets the cached EVM randomness seed of the current epoch - pub fn get_randomness(&mut self) -> Result<&[u8; 32], StatusCode> { + pub fn get_randomness(&mut self) -> Result<&[u8; 32], ActorError> { const ENTROPY: &[u8] = b"prevrandao"; self.randomness.get_or_try_init(|| { // get randomness from current beacon epoch with entropy of "prevrandao" - Ok(self.rt.get_randomness_from_beacon( + self.rt.get_randomness_from_beacon( fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, self.rt.curr_epoch(), ENTROPY, - )?) + ) }) } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 29c9c0f9b..893fa9eb3 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -14,7 +14,7 @@ pub(crate) mod reader; mod state; use { - crate::interpreter::{execute, Bytecode, ExecutionState, StatusCode, System, U256}, + crate::interpreter::{execute, Bytecode, ExecutionState, System, U256}, bytes::Bytes, cid::Cid, fil_actors_runtime::{ @@ -34,7 +34,12 @@ pub use state::*; fil_actors_runtime::wasm_trampoline!(EvmContractActor); pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(33); -pub const EVM_CONTRACT_EXECUTION_ERROR: ExitCode = ExitCode::new(34); +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); const EVM_MAX_RESERVED_METHOD: u64 = 1023; pub const NATIVE_METHOD_SIGNATURE: &str = "handle_filecoin_method(uint64,uint64,bytes)"; @@ -115,10 +120,7 @@ pub fn initialize_evm_contract( let initcode = Bytecode::new(initcode); // invoke the contract constructor - let output = execute(&initcode, &mut exec_state, system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; + let output = execute(&initcode, &mut exec_state, system)?; match output.outcome { Outcome::Return => { @@ -157,10 +159,7 @@ where let mut exec_state = ExecutionState::new(*caller, receiver_eth_addr, value_received, input_data.to_vec().into()); - let output = execute(&bytecode, &mut exec_state, system).map_err(|e| match e { - StatusCode::ActorError(e) => e, - _ => ActorError::unspecified(format!("EVM execution error: {e:?}")), - })?; + let output = execute(&bytecode, &mut exec_state, system)?; match output.outcome { Outcome::Return => { diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index f9f0145b7..e265efb1c 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -90,6 +90,9 @@ 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 } + } /// Returns the exit code of the error. pub fn exit_code(&self) -> ExitCode { From abfb608696740e00b6adb383bb6f41aa01c8e5c4 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 20 Jan 2023 12:46:06 -0500 Subject: [PATCH 285/339] feat: Use CBOR, not DAG_CBOR, for message params & returns (#1076) https://github.com/filecoin-project/ref-fvm/issues/1001 --- Cargo.lock | 42 +++++++++---------- actors/account/Cargo.toml | 2 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 2 +- actors/eam/Cargo.toml | 2 +- actors/ethaccount/Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- .../evm/src/interpreter/instructions/call.rs | 2 +- actors/evm/src/interpreter/precompiles/fvm.rs | 2 + actors/evm/src/lib.rs | 10 +++-- actors/evm/tests/call.rs | 13 +++--- .../evm/tests/contracts/FilecoinFallback.bin | 2 +- .../evm/tests/contracts/FilecoinFallback.hex | 2 +- .../evm/tests/contracts/FilecoinFallback.sol | 4 +- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 2 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 2 +- actors/multisig/tests/multisig_actor_test.rs | 4 +- actors/paych/Cargo.toml | 2 +- actors/paych/src/lib.rs | 4 +- actors/paych/tests/paych_actor_test.rs | 4 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- state/Cargo.toml | 2 +- 28 files changed, 64 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ef85218a..5641fa23a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" [[package]] name = "atty" @@ -498,9 +498,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -753,9 +753,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ "crossbeam-utils", ] @@ -2244,7 +2244,7 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "3.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#132b0d2b662290d09a6cc8dd7618495bcfef6b0a" dependencies = [ "frc42_hasher", "frc42_macros", @@ -2257,7 +2257,7 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "1.3.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#132b0d2b662290d09a6cc8dd7618495bcfef6b0a" dependencies = [ "fvm_sdk", "fvm_shared", @@ -2267,7 +2267,7 @@ dependencies = [ [[package]] name = "frc42_macros" version = "1.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#132b0d2b662290d09a6cc8dd7618495bcfef6b0a" dependencies = [ "blake2b_simd", "frc42_hasher", @@ -2279,7 +2279,7 @@ dependencies = [ [[package]] name = "frc46_token" version = "3.1.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#132b0d2b662290d09a6cc8dd7618495bcfef6b0a" dependencies = [ "anyhow", "cid", @@ -2428,7 +2428,7 @@ dependencies = [ [[package]] name = "fvm_actor_utils" version = "3.0.0" -source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#d8a2364f9ea23428ac095c0360211a4696f1a21b" +source = "git+https://github.com/filecoin-project/filecoin-actor-utils?branch=feat/fvm-m2#132b0d2b662290d09a6cc8dd7618495bcfef6b0a" dependencies = [ "anyhow", "cid", @@ -2499,9 +2499,9 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189214585b1dc3c2a92682aa175cce1991c1a09542e5338c400d00c15b706142" +checksum = "f0816a2a6df4853de08a723d261110d56a121aa313bc570fe9d248f0a4bc5288" dependencies = [ "anyhow", "cid", @@ -2570,9 +2570,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.16" +version = "3.0.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95666bb2e4b450f1750015a5f6c5da7544e744e295690cb4fcc5ba453283142" +checksum = "3779876441e390f414161474701404b5641e02a5b9acfece9b212f6d24e482e1" dependencies = [ "anyhow", "bitflags", @@ -3676,9 +3676,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -4436,9 +4436,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -4568,9 +4568,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.24.1" +version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg", "bytes", diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 25856bab9..2298ce235 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -21,7 +21,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" anyhow = "1.0.65" [dev-dependencies] diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 97ae26504..8f3504921 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -21,7 +21,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" [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 03300a51f..20106023d 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -21,7 +21,7 @@ frc42_dispatch = "3.0.0" frc46_token = "3.1.0" fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_hamt = "0.6.1" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } lazy_static = "1.4.0" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 0b8ddddbf..3f6ec4d5b 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -21,7 +21,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 4bad5f5dd..21ce0a906 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/Cargo.toml @@ -18,7 +18,7 @@ fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.0" fvm_actor_utils = "3.0.0" serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 8f30a70aa..3d3351186 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -25,7 +25,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" rlp = { version = "0.5.1", default-features = false } bytes = { version = "1.1.0", features = ["serde"], default-features = false } strum = "0.24" diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index d9b055ffb..7e1a1a001 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -274,7 +274,7 @@ pub fn call_generic( system.send( &system.rt.message().receiver(), Method::InvokeContractDelegate as u64, - IpldBlock::serialize_cbor(¶ms)?, + IpldBlock::serialize_dag_cbor(¶ms)?, TokenAmount::from(&value), Some(effective_gas_limit(system, gas)), SendFlags::default(), diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index bf9337f5f..3b305f78c 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -167,6 +167,8 @@ pub(super) fn call_actor_shared( let result = { // TODO only CBOR or "nothing" for now 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), diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 893fa9eb3..74cfaa9b6 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,6 +1,6 @@ use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ADDR, INIT_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer, DAG_CBOR}; +use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -349,10 +349,12 @@ fn handle_filecoin_method_output(output: &[u8]) -> Result, Act 0 => { return Err(ActorError::serialization(format!( "codec 0 is only valid for empty returns, got a return value of length {length}" - ))) + ))); } // Supported codecs. - DAG_CBOR => Some(IpldBlock { codec, data: return_data.into() }), + 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}"))), }; @@ -404,7 +406,7 @@ impl ActorCode for EvmContractActor { } Some(Method::GetBytecode) => { let ret = Self::bytecode(rt)?; - Ok(IpldBlock::serialize_cbor(&ret)?) + Ok(IpldBlock::serialize_dag_cbor(&ret)?) } Some(Method::GetBytecodeHash) => { let hash = Self::bytecode_hash(rt)?; diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 3ec6aa7de..c84ba35a9 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -14,7 +14,7 @@ use evm::{Method, EVM_CONTRACT_REVERTED}; use fil_actor_evm as evm; 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, DAG_CBOR, IPLD_RAW}; +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; @@ -395,7 +395,7 @@ return let address = EthAddress(util::CONTRACT_ADDRESS); // large set of data - let large_ret = IpldBlock { codec: DAG_CBOR, data: vec![0xff; 2048] }; + 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 { @@ -466,7 +466,7 @@ fn test_native_call() { rt.expect_validate_caller_any(); let result = rt.call::(1025, None).unwrap(); - assert_eq!(result, Some(IpldBlock { codec: 0x71, data: "foobar".into() })); + assert_eq!(result, Some(IpldBlock { codec: CBOR, data: "foobar".into() })); rt.expect_validate_caller_any(); let err = rt.call::(1026, None).unwrap_err(); @@ -588,7 +588,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i ); // expected return data - // Test with a codec _other_ than DAG_CBOR, to make sure we are actually passing the returned codec + // 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 }; @@ -703,7 +703,7 @@ fn call_actor_overlapping() { // not valid CBOR, but params should parse fine in precompile let addr_bytes = addr.to_bytes(); - call_params.codec(U256::from(DAG_CBOR)); + 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()); @@ -727,7 +727,7 @@ fn call_actor_overlapping() { rt.expect_send_generalized( addr, 0, - Some(IpldBlock { codec: DAG_CBOR, data: addr_bytes }), + Some(IpldBlock { codec: CBOR, data: addr_bytes }), TokenAmount::zero(), Some(0), SendFlags::empty(), @@ -850,6 +850,7 @@ mod call_actor_invalid { bad_params_inner(call_params, addr, false) } + #[test] fn invalid_params_zero_codec_2() { let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); diff --git a/actors/evm/tests/contracts/FilecoinFallback.bin b/actors/evm/tests/contracts/FilecoinFallback.bin index bade12b28..636dfcbe9 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.bin +++ b/actors/evm/tests/contracts/FilecoinFallback.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea2646970667358221220219c8240bb34d70255de9298a7b39e5aa844709954bfc12c1c75fc2cbbcc306a64736f6c63430008110033 \ No newline at end of file +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea264697066735822122032ee8363794cb126de27841436bd2a246c5014fef0a9dcf6e3059206933bd05e64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.hex b/actors/evm/tests/contracts/FilecoinFallback.hex index bade12b28..636dfcbe9 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.hex +++ b/actors/evm/tests/contracts/FilecoinFallback.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60716040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea2646970667358221220219c8240bb34d70255de9298a7b39e5aa844709954bfc12c1c75fc2cbbcc306a64736f6c63430008110033 \ No newline at end of file +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea264697066735822122032ee8363794cb126de27841436bd2a246c5014fef0a9dcf6e3059206933bd05e64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.sol b/actors/evm/tests/contracts/FilecoinFallback.sol index d433fc28c..b6f36b54f 100644 --- a/actors/evm/tests/contracts/FilecoinFallback.sol +++ b/actors/evm/tests/contracts/FilecoinFallback.sol @@ -7,11 +7,11 @@ contract FilecoinFallback { if (method == 1024) { return ( 0, 0, bytes("") ); } else if (method == 1025) { - return ( 0, 0x71, bytes("foobar") ); + return ( 0, 0x51, bytes("foobar") ); } else if (method == 1026) { return ( 42, 0, bytes("") ); } else if (method == 1027) { - return ( 42, 0x71, bytes("foobar") ); + return ( 42, 0x51, bytes("foobar") ); } revert(); } diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 8579b8e18..8324e680a 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -25,7 +25,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 20b0675b0..605a84d97 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "3.0.0" frc46_token = "3.1.0" fvm_ipld_bitfield = "0.5.2" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_hamt = "0.6.1" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } integer-encoding = { version = "3.0.3", default-features = false } diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index 26cdea6e2..4203fb8ec 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -30,7 +30,7 @@ byteorder = "1.4.3" anyhow = "1.0.65" itertools = "0.10.3" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" multihash = { version = "0.16.2", default-features = false } [dev-dependencies] diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 77adf2e27..ced5bb540 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -21,7 +21,7 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] frc42_dispatch = "3.0.0" fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_hamt = "0.6.1" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } diff --git a/actors/multisig/tests/multisig_actor_test.rs b/actors/multisig/tests/multisig_actor_test.rs index 3c405f7f1..47ec59e44 100644 --- a/actors/multisig/tests/multisig_actor_test.rs +++ b/actors/multisig/tests/multisig_actor_test.rs @@ -9,7 +9,7 @@ use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; use fvm_actor_utils::receiver::UniversalReceiverParams; use fvm_ipld_encoding::tuple::*; -use fvm_ipld_encoding::{RawBytes, DAG_CBOR}; +use fvm_ipld_encoding::{RawBytes, CBOR}; use fvm_shared::address::{Address, BLS_PUB_LEN}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -2376,5 +2376,5 @@ fn token_receiver() { } fn to_ipld_block(p: RawBytes) -> Option { - Some(IpldBlock { codec: DAG_CBOR, data: p.to_vec() }) + Some(IpldBlock { codec: CBOR, data: p.to_vec() }) } diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index 26e74622e..9cfb76a19 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -23,7 +23,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index a9a96e522..35c29d048 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 { rt.send( &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 736036117..fc68308ef 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( 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 893ffc8c2..cde45abdb 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -28,7 +28,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index e0f45916b..7efd62b4e 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -22,7 +22,7 @@ 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.2" +fvm_ipld_encoding = "0.3.3" [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 c1960dbf5..b3da0cc22 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } fvm_shared = { version = "3.0.0-alpha.16", default-features = false } -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" anyhow = "1.0.65" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 065abaec6..367a7dcd0 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -22,7 +22,7 @@ frc42_dispatch = "3.0.0" frc46_token = "3.1.0" fvm_actor_utils = "3.0.0" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_hamt = "0.6.1" fvm_shared = { version = "3.0.0-alpha.16", default-features = false } lazy_static = "1.4.0" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 05b279e42..f7807e4aa 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -23,7 +23,7 @@ log = "0.4.14" thiserror = "1.0.30" anyhow = "1.0.65" fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" fvm_ipld_bitfield = "0.5.2" multihash = { version = "0.16.1", default-features = false } serde_repr = "0.1.8" diff --git a/state/Cargo.toml b/state/Cargo.toml index 46862107b..9b5ea1718 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -28,7 +28,7 @@ fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} fvm_shared = { version = "3.0.0-alpha.16", default-features = false } -fvm_ipld_encoding = "0.3.2" +fvm_ipld_encoding = "0.3.3" frc46_token = "3.0.0" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" From 89d2aec3da0be9d0a9e08d67a09e85a50b7be0e9 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 20 Jan 2023 19:30:02 -0800 Subject: [PATCH 286/339] EVM: dont panic if calling i256_neg with 0 (#1090) * dont panic if calling i256_neg with 0 * fmt is litterally the death of me * add simple test --- actors/evm/src/interpreter/uints.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index 4f89e6178..bf5422a19 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -57,7 +57,11 @@ impl U256 { /// turns a i256 value to negative #[inline(always)] pub fn i256_neg(&self) -> U256 { - !*self + U256::ONE + if self.is_zero() { + U256::ZERO + } else { + !*self + U256::ONE + } } pub fn to_bytes(&self) -> [u8; 32] { @@ -291,6 +295,18 @@ mod tests { assert_eq!(i256_div(zero, one), zero); } + #[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(); From 09aa10f082565565b08572532b245236525f778c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Jan 2023 14:43:31 -0800 Subject: [PATCH 287/339] feat: allow all unrestricted method numbers on accounts (#1086) feat: allow unrestricted method numbers on accounts --- actors/account/src/lib.rs | 24 +++++++++++++--------- actors/account/tests/account_actor_test.rs | 2 +- actors/ethaccount/src/lib.rs | 22 ++++++++++++-------- actors/ethaccount/tests/ethaccount_test.rs | 2 +- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/actors/account/src/lib.rs b/actors/account/src/lib.rs index 7f0e40975..909f0460e 100644 --- a/actors/account/src/lib.rs +++ b/actors/account/src/lib.rs @@ -1,17 +1,17 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use fvm_actor_utils::receiver::UniversalReceiverParams; +use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::{Address, Protocol}; use fvm_shared::crypto::signature::SignatureType::{Secp256k1, BLS}; use fvm_shared::crypto::signature::{Signature, SignatureType}; use fvm_shared::error::ExitCode; -use fvm_shared::METHOD_CONSTRUCTOR; +use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR; use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::{actor_dispatch, ActorDowncast}; +use fil_actors_runtime::{actor_dispatch, ActorDowncast, FIRST_EXPORTED_METHOD_NUMBER}; use fil_actors_runtime::{actor_error, ActorError}; use crate::types::AuthenticateMessageParams; @@ -34,7 +34,6 @@ pub enum Method { // Deprecated in v10 // AuthenticateMessage = 3, AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), - UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } /// Account Actor @@ -91,13 +90,18 @@ impl Actor { Ok(()) } - // Always succeeds, accepting any transfers. - pub fn universal_receiver_hook( + /// Fallback method for unimplemented method numbers. + pub fn fallback( rt: &mut impl Runtime, - _params: UniversalReceiverParams, - ) -> Result<(), ActorError> { + method: MethodNum, + _: Option, + ) -> Result, ActorError> { rt.validate_immediate_caller_accept_any()?; - Ok(()) + if method >= FIRST_EXPORTED_METHOD_NUMBER { + Ok(None) + } else { + Err(actor_error!(unhandled_message; "invalid method: {}", method)) + } } } @@ -107,6 +111,6 @@ impl ActorCode for Actor { Constructor => constructor, PubkeyAddress => pubkey_address, AuthenticateMessageExported => authenticate_message, - UniversalReceiverHook => universal_receiver_hook, + _ => fallback [raw], } } diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index 218da33a1..fc432e2e8 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -76,7 +76,7 @@ fn token_receiver() { rt.expect_validate_caller_any(); let ret = rt .call::( - Method::UniversalReceiverHook as MethodNum, + frc42_dispatch::method_hash!("Receive"), IpldBlock::serialize_cbor(&UniversalReceiverParams { type_: 0, payload: RawBytes::new(vec![1, 2, 3]), diff --git a/actors/ethaccount/src/lib.rs b/actors/ethaccount/src/lib.rs index 610f90705..770cf5781 100644 --- a/actors/ethaccount/src/lib.rs +++ b/actors/ethaccount/src/lib.rs @@ -1,17 +1,17 @@ pub mod types; -use fvm_actor_utils::receiver::UniversalReceiverParams; +use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::{Payload, Protocol}; use fvm_shared::crypto::hash::SupportedHashes::Keccak256; use fvm_shared::error::ExitCode; -use fvm_shared::METHOD_CONSTRUCTOR; +use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; use num_derive::FromPrimitive; use crate::types::AuthenticateMessageParams; use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ actor_dispatch, actor_error, ActorDowncast, ActorError, AsActorError, EAM_ACTOR_ID, - SYSTEM_ACTOR_ADDR, + FIRST_EXPORTED_METHOD_NUMBER, SYSTEM_ACTOR_ADDR, }; #[cfg(feature = "fil-actor")] @@ -23,7 +23,6 @@ fil_actors_runtime::wasm_trampoline!(EthAccountActor); pub enum Method { Constructor = METHOD_CONSTRUCTOR, AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"), - UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"), } /// Ethereum Account actor. @@ -127,12 +126,17 @@ impl EthAccountActor { } // Always succeeds, accepting any transfers. - pub fn universal_receiver_hook( + pub fn fallback( rt: &mut impl Runtime, - _params: UniversalReceiverParams, - ) -> Result<(), ActorError> { + method: MethodNum, + _: Option, + ) -> Result, ActorError> { rt.validate_immediate_caller_accept_any()?; - Ok(()) + if method >= FIRST_EXPORTED_METHOD_NUMBER { + Ok(None) + } else { + Err(actor_error!(unhandled_message; "invalid method: {}", method)) + } } } @@ -141,6 +145,6 @@ impl ActorCode for EthAccountActor { actor_dispatch! { Constructor => constructor, AuthenticateMessageExported => authenticate_message, - UniversalReceiverHook => universal_receiver_hook, + _ => fallback [raw], } } diff --git a/actors/ethaccount/tests/ethaccount_test.rs b/actors/ethaccount/tests/ethaccount_test.rs index 3c30bb300..6e4a706d6 100644 --- a/actors/ethaccount/tests/ethaccount_test.rs +++ b/actors/ethaccount/tests/ethaccount_test.rs @@ -36,7 +36,7 @@ fn token_receiver() { rt.expect_validate_caller_any(); let ret = rt .call::( - Method::UniversalReceiverHook as MethodNum, + frc42_dispatch::method_hash!("Receive"), IpldBlock::serialize_cbor(&UniversalReceiverParams { type_: 0, payload: RawBytes::new(vec![1, 2, 3]), From 252c36904c6427ffaf29ccee73c13f25f125dd64 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 24 Jan 2023 10:18:03 -0500 Subject: [PATCH 288/339] fix: paych: correct error message (#1094) --- actors/paych/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index f7651643e..daebabda3 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -61,7 +61,7 @@ impl Actor { let from = Self::resolve_address(rt, ¶ms.from) .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { - format!("to address not found {}", params.to) + format!("from address not found {}", params.to) })?; let empty_arr_cid = From 2c1db5ada44377fde06bd9c91c0fea32aa46a622 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 24 Jan 2023 14:31:49 -0500 Subject: [PATCH 289/339] fix: paych: correct error message (#1098) --- actors/paych/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index daebabda3..62f773795 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -61,7 +61,7 @@ impl Actor { let from = Self::resolve_address(rt, ¶ms.from) .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { - format!("from address not found {}", params.to) + format!("from address not found {}", params.from) })?; let empty_arr_cid = From 764d4a8642b9d833b55c65e6682149487f948201 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 24 Jan 2023 12:16:48 -0800 Subject: [PATCH 290/339] fix: bubble up the error from the callactor precompile (#1097) --- actors/evm/src/interpreter/precompiles/fvm.rs | 24 +++++++---- actors/evm/src/interpreter/precompiles/mod.rs | 8 +++- actors/evm/src/interpreter/system.rs | 41 ++++++++++++++----- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 3b305f78c..00d30b3b3 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -173,7 +173,18 @@ pub(super) fn call_actor_shared( 0 if params.is_empty() => None, _ => return Err(PrecompileError::InvalidInput), }; - system.send(&address, method, params, TokenAmount::from(&value), Some(ctx.gas_limit), flags) + // 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 ------- @@ -183,16 +194,13 @@ pub(super) fn call_actor_shared( // positive values are user/actor errors // success is 0 let (exit_code, data) = match result { - Err(mut ae) => { - // TODO handle revert - // TODO https://github.com/filecoin-project/ref-fvm/issues/1020 - // put error number from call into revert - let exit_code = U256::from(ae.exit_code().value()); + Err(errno) => { + let exit_code = U256::from(errno as u32).i256_neg(); // no return only exit code - (exit_code, ae.take_data()) + (exit_code, None) } - Ok(ret) => (U256::zero(), ret), + Ok(resp) => (U256::from(resp.exit_code.value()), resp.return_data), }; let ret_blk = data.unwrap_or(IpldBlock { codec: 0, data: vec![] }); diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index b12d0329b..7550aff92 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, num::TryFromIntError}; -use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{runtime::Runtime, ActorError}; use fvm_shared::{address::Address, econ::TokenAmount}; use substrate_bn::{CurveError, FieldError, GroupError}; @@ -118,8 +118,14 @@ pub enum PrecompileError { 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 diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 8e711aac6..e766fe620 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -11,9 +11,9 @@ use fvm_shared::{ address::{Address, Payload}, crypto::hash::SupportedHashes, econ::TokenAmount, - error::ExitCode, + error::{ErrorNumber, ExitCode}, sys::SendFlags, - MethodNum, IPLD_RAW, METHOD_SEND, + MethodNum, Response, IPLD_RAW, METHOD_SEND, }; use multihash::Code; use once_cell::unsync::OnceCell; @@ -227,11 +227,9 @@ impl<'r, RT: Runtime> System<'r, RT> { gas_limit: Option, send_flags: SendFlags, ) -> Result, ActorError> { - self.flush()?; - let result = self - .rt - .send_generalized(to, method, params, value, gas_limit, send_flags) - .map_err(|err| actor_error!(unspecified; "send syscall failed: {}", err))?; + 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() { @@ -242,11 +240,34 @@ impl<'r, RT: Runtime> System<'r, RT> { )); } - if !send_flags.read_only() { - self.reload()?; + 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_generalized(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.return_data) + Ok(result) } /// Flush the actor state (bytecode, nonce, and slots). From 076c7f83630732d1c38bdf27d13709630e342f5b Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 24 Jan 2023 13:39:46 -0800 Subject: [PATCH 291/339] EVM: test send error changes (#1099) * add test for getting a system error from send for callactor * add SendError to mock runtime expectations * return send error in mock runtime, fix tests * update comment --- actors/evm/tests/call.rs | 74 ++++++++++++++++++++++++++++--- actors/evm/tests/delegate_call.rs | 2 + actors/evm/tests/ext_opcodes.rs | 5 +++ runtime/src/test_utils.rs | 8 ++++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index c84ba35a9..3b775f840 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -19,7 +19,7 @@ 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::ExitCode; +use fvm_shared::error::{ErrorNumber, ExitCode}; use fvm_shared::sys::SendFlags; use fvm_shared::{ActorID, MethodNum, METHOD_SEND}; use once_cell::sync::Lazy; @@ -203,6 +203,7 @@ fn test_call() { IpldBlock::serialize_cbor(&BytesSer(&return_data)) .expect("failed to serialize return data"), ExitCode::OK, + None, ); let result = util::invoke_contract(&mut rt, &contract_params); @@ -273,6 +274,7 @@ fn test_call_convert_to_send() { IpldBlock::serialize_cbor(&BytesSer(&return_data)) .expect("failed to serialize return data"), ExitCode::OK, + None, ); let result = util::invoke_contract(&mut rt, &contract_params); @@ -408,6 +410,7 @@ return SendFlags::empty(), Some(large_ret.clone()), ExitCode::OK, + None, ); rt.expect_gas_available(10_000_000_000); @@ -604,6 +607,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i send_flags, Some(send_return.clone()), exit_code, + None, ); } @@ -612,7 +616,7 @@ fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_i v[..4].copy_from_slice(&send_return.data); let expect = CallActorReturn { - exit_code, + send_exit_code: U256::from(exit_code.value()), codec: send_return.codec, data_offset: 96, data_size: send_return.data.len() as u32, @@ -683,6 +687,7 @@ fn call_actor_weird_offset() { SendFlags::empty(), None, ExitCode::OK, + None, ); let precompile_return = CallActorReturn::default(); @@ -733,6 +738,7 @@ fn call_actor_overlapping() { SendFlags::empty(), None, ExitCode::OK, + None, ); test.input = call_params.into(); @@ -775,12 +781,59 @@ fn call_actor_id_with_full_address() { 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_generalized( + 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::*; @@ -988,13 +1041,19 @@ impl From for Vec { impl Default for CallActorReturn { fn default() -> Self { - Self { exit_code: ExitCode::OK, codec: 0, data_offset: 3 * 32, data_size: 0, data: vec![] } + 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 { - exit_code: ExitCode, + send_exit_code: U256, codec: u64, data_offset: u32, data_size: u32, @@ -1003,7 +1062,9 @@ struct CallActorReturn { impl From for Vec { fn from(src: CallActorReturn) -> Self { - let exit_code = U256::from(src.exit_code.value()); + // 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); @@ -1056,6 +1117,7 @@ fn call_actor_solidity() { 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) = @@ -1098,6 +1160,7 @@ fn call_actor_solidity() { 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) = @@ -1140,6 +1203,7 @@ fn call_actor_send_solidity() { 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) = diff --git a/actors/evm/tests/delegate_call.rs b/actors/evm/tests/delegate_call.rs index a94953717..03971ac29 100644 --- a/actors/evm/tests/delegate_call.rs +++ b/actors/evm/tests/delegate_call.rs @@ -117,6 +117,7 @@ fn test_delegate_call_caller() { SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&EMPTY_ARR_CID).expect("failed to serialize bytecode hash"), ExitCode::OK, + None, ); rt.expect_send_generalized( @@ -129,6 +130,7 @@ fn test_delegate_call_caller() { 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); diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index 55e626e4b..c58364175 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -122,6 +122,7 @@ non_existent: SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), ExitCode::OK, + None, ); let result = util::invoke_contract(&mut rt, &method); @@ -256,6 +257,7 @@ account: SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&bytecode_hash).unwrap(), ExitCode::OK, + None, ); // Evm code @@ -402,6 +404,7 @@ precompile: SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), ExitCode::OK, + None, ); let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); @@ -477,6 +480,7 @@ init_extsize: SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&BytecodeHash::EMPTY).unwrap(), ExitCode::OK, + None, ); rt.store.put_keyed(&EMPTY_ARR_CID, &[]).unwrap(); @@ -489,6 +493,7 @@ init_extsize: SendFlags::READ_ONLY, IpldBlock::serialize_cbor(&EMPTY_ARR_CID).unwrap(), ExitCode::OK, + None, ); }); diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index cf9f94280..5183fcb29 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -392,6 +392,7 @@ pub struct ExpectedMessage { // returns from applying expectedMessage pub send_return: Option, pub exit_code: ExitCode, + pub send_error: Option, } #[derive(Debug)] @@ -691,6 +692,7 @@ impl MockRuntime { exit_code, send_flags: SendFlags::default(), gas_limit: None, + send_error: None, }) } @@ -706,6 +708,7 @@ impl MockRuntime { send_flags: SendFlags, send_return: Option, exit_code: ExitCode, + send_error: Option, ) { self.expectations.borrow_mut().expect_sends.push_back(ExpectedMessage { to, @@ -716,6 +719,7 @@ impl MockRuntime { value, send_flags, gas_limit, + send_error, }) } @@ -1179,6 +1183,10 @@ impl Runtime for MockRuntime { expected_msg.value == value, ); + if let Some(e) = expected_msg.send_error { + return Err(e); + } + { let mut balance = self.balance.borrow_mut(); if value > *balance { From 68f3f79fcb7ccf6a990fbc0218e1d33e8a8a347d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 24 Jan 2023 14:39:14 -0800 Subject: [PATCH 292/339] EVM: fix addmod and test (#1100) --- .../interpreter/instructions/arithmetic.rs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 64ff0ec9b..8551d91da 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -52,7 +52,7 @@ pub fn addmod(a: U256, b: U256, c: U256) -> U256 { let bl: U512 = b.into(); let cl: U512 = c.into(); - (al + bl % cl).low_u256() + ((al + bl) % cl).low_u256() } else { c } @@ -64,7 +64,7 @@ pub fn mulmod(a: U256, b: U256, c: U256) -> U256 { let al: U512 = a.into(); let bl: U512 = b.into(); let cl: U512 = c.into(); - (al * bl % cl).low_u256() + ((al * bl) % cl).low_u256() } else { c } @@ -114,6 +114,28 @@ mod test { use crate::interpreter::instructions::arithmetic::*; use crate::interpreter::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"); From 992c534f9d0b9fcc99c573420e70aff12c20249f Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 24 Jan 2023 16:18:05 -0800 Subject: [PATCH 293/339] EVM: fix and test DUP opcodes (#1104) * add tests for OOB dup, remove UB case from dup * fmt * fix doc comments --- .../evm/src/interpreter/instructions/stack.rs | 15 +++++++++++ actors/evm/src/interpreter/stack.rs | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index 80ff79fdd..295d8dd97 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -81,3 +81,18 @@ fn test_push0() { assert_eq!(stack.len(), 1); assert_eq!(stack.pop().unwrap(), U256::ZERO); } + +#[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/stack.rs b/actors/evm/src/interpreter/stack.rs index 74a0a258b..efc39beec 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -78,7 +78,13 @@ impl Stack { } #[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())) @@ -185,6 +191,26 @@ fn test_stack_dup() { 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(); From 520e96f732a2399daf2c03eb56d81249a9c33dd5 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Tue, 24 Jan 2023 18:31:52 -0800 Subject: [PATCH 294/339] EVM: test swap 0 (#1105) test swap 0 --- actors/evm/src/interpreter/stack.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index efc39beec..b3ffab81f 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -166,6 +166,14 @@ fn test_stack_swap() { 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(); From 2d7f62aa66819fed4c9d20b3a70f9d83a2c547a3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 25 Jan 2023 09:01:48 -0800 Subject: [PATCH 295/339] EVM: abort if we fail to transfer to the beneficiary on selfdestruct (#1106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * EVM: abort if we fail to transfer to the beneficiary on selfdestruct This is somewhat of a damned if we do, damned if we don't situation. This operation _can't_ fail in the EVM, but it _can_ fail in the FVM because we implement this with an actual send. However, this is far from the only instruction that can fail unexpectedly (e.g., EXTCODEHASH). 🤷‍♂️ fixes #1558 * EVM: add a dedicated exit code --- .../src/interpreter/instructions/lifecycle.rs | 15 +++++++-- actors/evm/src/lib.rs | 1 + actors/evm/tests/selfdestruct.rs | 32 +++++++++++++++---- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 96fcf8df4..cb50e25a9 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -13,6 +13,7 @@ use serde_tuple::{Deserialize_tuple, Serialize_tuple}; use crate::interpreter::Output; use crate::interpreter::{address::EthAddress, U256}; +use crate::EVM_CONTRACT_SELFDESTRUCT_FAILED; use super::memory::{get_memory_region, MemoryRegion}; use { @@ -163,11 +164,21 @@ pub fn selfdestruct( return Err(ActorError::read_only("selfdestruct called while read-only".into())); } - // Try to give funds to the beneficiary. If this fails, we just keep them. + // 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(); - let _ = system.rt.send(&beneficiary, METHOD_SEND, None, balance); + system.rt.send(&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(); diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 74cfaa9b6..e50830ea8 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -40,6 +40,7 @@ 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)"; diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index 5220aeb06..17e67cdf6 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -1,10 +1,16 @@ use fil_actor_evm::{ interpreter::{address::EthAddress, U256}, - EvmContractActor, Method, ResurrectParams, State, Tombstone, + EvmContractActor, Method, ResurrectParams, State, Tombstone, EVM_CONTRACT_SELFDESTRUCT_FAILED, }; use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ADDR, INIT_ACTOR_ADDR}; -use fvm_ipld_encoding::{ipld_block::IpldBlock, RawBytes}; -use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, MethodNum, METHOD_SEND}; +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; @@ -108,18 +114,30 @@ fn test_selfdestruct_missing_beneficiary() { let solidity_params = hex::decode("35f46994").unwrap(); rt.expect_validate_caller_any(); - rt.expect_send( + rt.expect_send_generalized( beneficiary, METHOD_SEND, None, rt.get_balance(), None, - ExitCode::SYS_INVALID_RECEIVER, + SendFlags::default(), + None, + ExitCode::OK, // doesn't matter + Some(ErrorNumber::NotFound), ); // It still works even if the beneficiary doesn't exist. - assert!(util::invoke_contract(&mut rt, &solidity_params).is_empty()); + + 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, Some(Tombstone { origin: 100, nonce: 0 })); + assert_eq!(state.tombstone, None); rt.verify(); } From ee5dad08630943458c50cdb21fd67c3672367554 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 25 Jan 2023 17:26:45 -0800 Subject: [PATCH 296/339] EVM: Test `PUSH*` opcodes a bit more (#1109) add some tests and documentation for pushN opcodes --- .../evm/src/interpreter/instructions/mod.rs | 3 +- .../evm/src/interpreter/instructions/stack.rs | 23 ++++++++++ actors/evm/tests/basic.rs | 43 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index d0583c052..20e451f7e 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -77,8 +77,9 @@ macro_rules! def_stackop { }; } -// pusho variants: push stuff on the stack taken as input from bytecode; the kind of thing that +// 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) => { diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index 295d8dd97..cb6144669 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -18,6 +18,11 @@ macro_rules! be_shift { } #[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. @@ -82,6 +87,24 @@ fn test_push0() { 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(); diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 1db498451..e7bf4723e 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -148,3 +148,46 @@ sstore"; 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, &[]); +} From 03d2d535c2cdd3bb11fd540a882a3f94419a2afc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 26 Jan 2023 08:20:54 -0800 Subject: [PATCH 297/339] feat: accept all methods in the placeholder (#1112) --- actors/placeholder/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/actors/placeholder/src/lib.rs b/actors/placeholder/src/lib.rs index d5e51031c..8de4fb094 100644 --- a/actors/placeholder/src/lib.rs +++ b/actors/placeholder/src/lib.rs @@ -1,8 +1,5 @@ #[cfg(feature = "fil-actor")] #[no_mangle] pub extern "C" fn invoke(_: u32) -> u32 { - fvm_sdk::vm::abort( - fvm_shared::error::ExitCode::USR_UNHANDLED_MESSAGE.value(), - Some("there is no contract deployed at this address; placeholder actors may only receive value transfers with method 0"), - ) + 0 } From 47a90adf40a645e0b45955ae5b684de8e0ea994a Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 26 Jan 2023 11:25:23 -0800 Subject: [PATCH 298/339] EVM: test remaining math ops (#1111) * add test for smod * add mod test * fmt --- .../interpreter/instructions/arithmetic.rs | 17 +++++++++ actors/evm/src/interpreter/uints.rs | 35 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 8551d91da..2417d86cf 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -198,6 +198,23 @@ mod test { ); } + #[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 { diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/src/interpreter/uints.rs index bf5422a19..123b9f2e0 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/src/interpreter/uints.rs @@ -295,6 +295,41 @@ mod tests { assert_eq!(i256_div(zero, 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!(i256_mod(minus_one, U256::ZERO), U256::ZERO); + assert_eq!(i256_mod(max_value, U256::ZERO), U256::ZERO); + assert_eq!(i256_mod(U256::ZERO, U256::ZERO), U256::ZERO); + + assert_eq!(i256_mod(minus_one, two), minus_one); + assert_eq!(i256_mod(U256::I128_MIN, one), 0); + assert_eq!(i256_mod(one, U256::I128_MIN), one); + assert_eq!(i256_mod(one, U256::from(i128::MAX)), one); + + assert_eq!(i256_mod(max_value, minus_one), zero); + assert_eq!(i256_mod(neg_one_hundred, minus_one), zero); + assert_eq!(i256_mod(one_hundred, two), zero); + assert_eq!(i256_mod(one_hundred, neg_three), one); + + assert_eq!(i256_mod(neg_one_hundred, 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); From 68717bc02481d301181846de48cedd1ee829831d Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 26 Jan 2023 12:09:13 -0800 Subject: [PATCH 299/339] EVM: Test boolean ops (#1115) * test bitwise ops * test cmp ops --- .../src/interpreter/instructions/boolean.rs | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/boolean.rs b/actors/evm/src/interpreter/instructions/boolean.rs index c1c6e3186..8dbb3d33a 100644 --- a/actors/evm/src/interpreter/instructions/boolean.rs +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -49,3 +49,158 @@ pub(crate) fn xor(a: U256, b: U256) -> U256 { 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); + } +} From 67b759f2cf3a7656fa837ccc52f6d162d60edf37 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 26 Jan 2023 15:20:24 -0500 Subject: [PATCH 300/339] fix: test_vm: Don't set predictable address for accounts (#1114) --- test_vm/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 62f0d10b0..25beaa6b8 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -679,7 +679,7 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { read_only: false, }; if is_account { - new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, Some(*target)).unwrap(); + new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, None).unwrap(); let res = new_ctx.invoke(); let invoc = new_ctx.gather_trace(res); RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { From b81009e0adb2d99110da0dddd595b7d87fddd28f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 26 Jan 2023 16:41:00 -0500 Subject: [PATCH 301/339] placeholder: drop deps on FVM SDK and shared (#1117) --- Cargo.lock | 4 ---- actors/placeholder/Cargo.toml | 6 +----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfb26985e..f6dcd808b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2021,10 +2021,6 @@ dependencies = [ [[package]] name = "fil_actor_placeholder" version = "10.0.0-alpha.1" -dependencies = [ - "fvm_sdk 3.0.0-alpha.22", - "fvm_shared 3.0.0-alpha.17", -] [[package]] name = "fil_actor_power" diff --git a/actors/placeholder/Cargo.toml b/actors/placeholder/Cargo.toml index 22732c1fc..bab52c72c 100644 --- a/actors/placeholder/Cargo.toml +++ b/actors/placeholder/Cargo.toml @@ -12,9 +12,5 @@ keywords = ["filecoin", "web3", "wasm"] ## cdylib is necessary for Wasm build crate-type = ["cdylib", "lib"] -[dependencies] -fvm_sdk = { version = "3.0.0-alpha.22", optional = true } -fvm_shared = { version = "3.0.0-alpha.17", optional = true } - [features] -fil-actor = ["fvm_sdk", "fvm_shared"] +fil-actor = [] From 6ea329b418c7bab352945c33b466227c269ae6ba Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 26 Jan 2023 16:58:56 -0800 Subject: [PATCH 302/339] EVM: Test Fe lang simplecoin (#1120) * fe-lang simplecoin contract * remove extra args * add fe-lang simplecoin test --- actors/evm/tests/basic.rs | 11 +++ actors/evm/tests/contracts/FeSimplecoin.fe | 33 +++++++ .../output/FeSimplecoin/FeSimplecoin.bin | 1 + .../output/FeSimplecoin/FeSimplecoin_abi.json | 92 +++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 actors/evm/tests/contracts/FeSimplecoin.fe create mode 100644 actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin.bin create mode 100644 actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin_abi.json diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index e7bf4723e..6c3f4fefc 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -10,9 +10,20 @@ 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| { 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/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 From 28377381471634ad8ada08a839cf879b5f096d3d Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 26 Jan 2023 20:47:11 -0800 Subject: [PATCH 303/339] EVM: Test `POP` opcode (#1110) * test pop opcode * fmt --- actors/evm/src/interpreter/stack.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index b3ffab81f..d424bac0d 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -147,6 +147,19 @@ fn test_stack_push_pop() { 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(); From cfaa4e36ed93becc5a235418de7bc9abe86004bc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 27 Jan 2023 09:20:50 -0800 Subject: [PATCH 304/339] EVM: simplify call logic and CALL on transfer (#1122) EVM: simplify call logic 1. Turn solidity "transfers" into full calls, but only grant them 10M gas. 2. Remove a lot of special casing. --- .../evm/src/interpreter/instructions/call.rs | 150 ++++++++++-------- actors/evm/tests/call.rs | 99 +++--------- 2 files changed, 98 insertions(+), 151 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 7e1a1a001..397fc6aac 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -2,7 +2,7 @@ use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::BytesDe; -use fvm_shared::{address::Address, sys::SendFlags, IPLD_RAW, METHOD_SEND}; +use fvm_shared::{address::Address, sys::SendFlags, MethodNum, IPLD_RAW}; use crate::interpreter::precompiles::{is_reserved_precompile_address, PrecompileContext}; @@ -17,12 +17,15 @@ use { crate::interpreter::System, crate::interpreter::U256, crate::{DelegateCallParams, Method}, - fil_actors_runtime::runtime::builtins::Type, 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); + /// The kind of call-like instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CallKind { @@ -156,7 +159,7 @@ pub fn call_generic( ) -> Result { let ExecutionState { stack: _, memory, .. } = state; - let (gas, dst, value, input_offset, input_size, output_offset, output_size) = params; + 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 @@ -203,61 +206,65 @@ pub fn call_generic( let call_result = match kind { CallKind::Call | CallKind::StaticCall => { let dst_addr: Address = dst.into(); - - // Special casing for account/placeholder/non-existent actors: we just do a SEND (method 0) - // which allows us to transfer funds (and create placeholders) - let target_actor_code = system - .rt - .resolve_address(&dst_addr) - .and_then(|actor_id| system.rt.get_actor_code_cid(&actor_id)); - let target_actor_type = target_actor_code - .as_ref() - .and_then(|cid| system.rt.resolve_builtin_actor_type(cid)); - let actor_exists = target_actor_code.is_some(); - - if !actor_exists && value.is_zero() { - // If the actor doesn't exist and we're not sending value, return with - // "success". The EVM only auto-creates actors when sending value. - // - // NOTE: this will also apply if we're in read-only mode, because we can't - // send value in read-only mode anyways. - Ok(None) + 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 { - let (method, gas_limit) = if !actor_exists - || matches!(target_actor_type, Some(Type::Placeholder | Type::Account | Type::EthAccount)) - // See https://github.com/filecoin-project/ref-fvm/issues/980 for this - // hocus pocus - || (input_data.is_empty() && ((gas == 0 && value > 0) || (gas == 2300 && value == 0))) - { - // We switch to a bare send when: - // - // 1. The target is a placeholder/account or doesn't exist. Otherwise, - // sending funds to an account/placeholder would fail when we try to call - // InvokeContract. - // 2. The gas wouldn't let code execute anyways. This lets us support - // solidity's "transfer" method. + 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. // - // At the same time, we ignore the supplied gas value and set it to - // infinity as user code won't execute anyways. The only code that might - // run is related to account creation, which doesn't count against this - // gas limit in the EVM anyways. - (METHOD_SEND, None) - } else { - // Otherwise, invoke normally. - (Method::InvokeContract as u64, 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() - }; - system.send(&dst_addr, method, params, value, gas_limit, send_flags) + // 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) { @@ -271,14 +278,16 @@ pub fn call_generic( 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(), - ) + 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) @@ -289,17 +298,18 @@ pub fn call_generic( ContractType::Account | ContractType::NotFound => Ok(None), // If we're calling a "native" actor, always revert. ContractType::Native(_) => { - Err(ActorError::forbidden("cannot delegate-call to native actors".into())) + 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) } - ContractType::Precompile => Err(ActorError::assertion_failed( - "Reached a precompile address in DelegateCall when a precompile should've been caught earlier in the system" - .to_string(), - )), }, }; let (code, data) = match call_result { Ok(result) => (1, result), - Err(mut ae) => (0, ae.take_data()), + Err(result) => (0, result), }; ( diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index 10d7fbca3..cf1083a5b 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -21,7 +21,7 @@ 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, METHOD_SEND}; +use fvm_shared::{ActorID, MethodNum}; use once_cell::sync::Lazy; mod util; @@ -210,82 +210,11 @@ fn test_call() { assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); } -// Test that a zero-value call to an actor that doesn't exist doesn't actually create the actor. -#[test] -fn test_empty_call_no_side_effects() { - 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 evm_target = EthAddress(hex_literal::hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - - 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]); +const TRANSFER_GAS_VALUE: u64 = 10_000_000; - // expected return data - let mut return_data = vec![0u8; 32]; - return_data[31] = 0x42; - - let result = util::invoke_contract(&mut rt, &contract_params); - assert_eq!(U256::from_big_endian(&result), U256::from(0)); - // Expect no calls - rt.verify(); -} - -// Make sure we do bare sends when calling accounts/placeholder, and make sure it works. +// Make sure we set the correct gas limit with value and 0 gas. #[test] -fn test_call_convert_to_send() { - let contract = call_proxy_contract(); - - for code in [*ACCOUNT_ACTOR_CODE_ID, *PLACEHOLDER_ACTOR_CODE_ID] { - // construct the proxy - let mut rt = util::construct_and_verify(contract.clone()); - - // 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, code); - - let evm_target_word = EthAddress::from_id(target_id).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_send_generalized( - target, - METHOD_SEND, - proxy_call_input_data, - TokenAmount::zero(), - None, - 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)); - rt.verify(); - } -} - -// Make sure we do bare sends when calling with 0 gas and value -#[test] -fn test_call_convert_to_send2() { +fn test_transfer_nogas() { let contract = call_proxy_transfer_contract(); // construct the proxy @@ -305,14 +234,18 @@ fn test_call_convert_to_send2() { // we don't expected return data let return_data = vec![]; - rt.expect_send( + rt.expect_gas_available(TRANSFER_GAS_VALUE * 10); + rt.expect_send_generalized( target, - METHOD_SEND, + 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); @@ -320,9 +253,9 @@ fn test_call_convert_to_send2() { rt.verify(); } -// Make sure we do bare sends when calling with 2300 gas and no value +// Make sure we set the correct gas limit with no value and 2300 gas. #[test] -fn test_call_convert_to_send3() { +fn test_transfer_2300() { let contract = call_proxy_gas2300_contract(); // construct the proxy @@ -342,14 +275,18 @@ fn test_call_convert_to_send3() { // we don't expected return data let return_data = vec![]; - rt.expect_send( + rt.expect_gas_available(TRANSFER_GAS_VALUE * 10); + rt.expect_send_generalized( target, - METHOD_SEND, + 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); From c55927915ab738a119ddc22600317adc0660f33b Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 27 Jan 2023 23:22:41 +0200 Subject: [PATCH 305/339] EVM: unit tests: Control Flow, Call Context, Call (#1116) * unit test scaffolding + pc test * macroize * Evm unittest fixes (#1119) * cleanup types and macro * cleanup * move bytecode into tester * remove types and only use macro, expose only runtime and machine to test cases * fmt & remove closure * move dep to macro * jump tests * jumpi tests * test: rework evm unit test macro 1. Give it a name. 2. Avoid hard-coded hex. 3. Syntax magic. * remove redundant cfg --------- Co-authored-by: Melanie Riise Co-authored-by: Steven Allen --- actors/evm/src/interpreter/execution.rs | 3 +- .../src/interpreter/instructions/control.rs | 155 ++++++++++++++++++ actors/evm/src/interpreter/mod.rs | 3 + actors/evm/src/interpreter/system.rs | 2 +- actors/evm/src/interpreter/test_util.rs | 41 +++++ 5 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 actors/evm/src/interpreter/test_util.rs diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 4088d14e7..13fe39fc9 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -271,7 +271,8 @@ impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { } #[inline(always)] - fn step(&mut self) -> Result<(), ActorError> { + // 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) } } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 4649cf394..b0f33f356 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -155,3 +155,158 @@ pub fn jumpi(bytecode: &Bytecode, pc: usize, dest: U256, test: U256) -> Result { } impl<'r, RT: Runtime> System<'r, RT> { - fn new(rt: &'r mut RT, readonly: bool) -> Self + pub(crate) fn new(rt: &'r mut RT, readonly: bool) -> Self where RT::Blockstore: Clone, { diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs new file mode 100644 index 000000000..8a8c4a87e --- /dev/null +++ b/actors/evm/src/interpreter/test_util.rs @@ -0,0 +1,41 @@ +#[macro_export] +macro_rules! evm_instruction { + ($i:ident) => { + $crate::interpreter::execution::opcodes::$i + }; + ($i:literal) => { + $i + }; +} + +#[macro_export] +macro_rules! evm_unit_test { + (($rt:ident, $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, Bytes, EthAddress, ExecutionState}; + + let mut $rt = MockRuntime::default(); + let mut state = ExecutionState::new( + EthAddress::from_id(1000), + EthAddress::from_id(1000), + TokenAmount::from_atto(0), + Bytes::default(), + ); + + let code = vec![$($crate::evm_instruction!($inst)),*]; + + let mut system = System::new(&mut $rt, false); + let bytecode = Bytecode::new(code); + let mut $machine = Machine { + system: &mut system, + state: &mut state, + bytecode: &bytecode, + pc: 0, + output: Output::default(), + }; + + $($body)* + }; +} From acfa4a1ae66e7051f28f6cfb78a9d60a553033f4 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Fri, 27 Jan 2023 15:23:45 -0800 Subject: [PATCH 306/339] EVM: Test memory ops (#1123) * unit test scaffolding + pc test * macroize * Evm unittest fixes (#1119) * cleanup types and macro * cleanup * move bytecode into tester * remove types and only use macro, expose only runtime and machine to test cases * fmt & remove closure * move dep to macro * jump tests * jumpi tests * test: rework evm unit test macro 1. Give it a name. 2. Avoid hard-coded hex. 3. Syntax magic. * remove redundant cfg * mload tests * test mstore8 * test mstore * test mstore8 overwrite * add msize tests * add docs and debug assert to memory grow * excersize invalid memory length panic (in debug) * fmt * test for read offset for mload * use expect for execution steps in tests * fmt and ignore broken grow test for now --------- Co-authored-by: vyzo Co-authored-by: Steven Allen --- .../src/interpreter/instructions/memory.rs | 296 +++++++++++++++++- actors/evm/src/interpreter/memory.rs | 4 + actors/evm/src/interpreter/test_util.rs | 3 + 3 files changed, 302 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 8ae9d0107..11033f7e6 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -161,7 +161,7 @@ pub fn msize( #[cfg(test)] mod tests { use super::*; - use crate::interpreter::memory::Memory; + use crate::{evm_unit_test, interpreter::memory::Memory}; #[test] fn copy_to_memory_big() { @@ -215,4 +215,298 @@ mod tests { assert_eq!(mem.len(), 32); assert_eq!(&mem[0..4], result_data); } + + #[test] + fn test_mload_nothing() { + evm_unit_test! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, 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! { + (rt, m) { + PUSH2; + 0xff; + 0xfe; + PUSH0; + MSTORE; + } + m.state.memory.grow(64); + m.state.memory[..32].copy_from_slice(&[0xff; 32]); + // 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_mszie_multiple_mstore8() { + evm_unit_test! { + (rt, 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_mszie_multiple_mstore() { + evm_unit_test! { + (rt, 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] + #[should_panic] + fn test_mszie_debug_panic() { + // Demonstrate that MSIZE depends on memory.len() + // Normally this should never happen and we wont panic from it. + evm_unit_test! { + (rt, m) { + MSIZE; + } + + m.state.memory.grow(12); + m.step().expect("execution step failed"); + }; + } } diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs index b2a346727..d6e361261 100644 --- a/actors/evm/src/interpreter/memory.rs +++ b/actors/evm/src/interpreter/memory.rs @@ -16,12 +16,15 @@ impl Default for Memory { impl Memory { #[inline] + /// Resizes memory to `size` length, reserving extra WASM pages as-needed. + /// TODO this should be renamed resize since it can also shrink memory. pub fn grow(&mut self, size: usize) { let cap = self.0.capacity(); if size > cap { let required_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; self.0.reserve((PAGE_SIZE * required_pages) - self.0.len()); } + debug_assert_eq!(size % 32, 0, "MSIZE depends on memory length being multiple of 32"); self.0.resize(size, 0); } } @@ -31,6 +34,7 @@ mod tests { use super::*; #[test] + #[ignore = "failing because of new assert, replacing grow in new PR"] fn grow() { let mut mem = Memory::default(); mem.grow(PAGE_SIZE * 2 + 1); diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs index 8a8c4a87e..f7e995e89 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -6,6 +6,9 @@ macro_rules! evm_instruction { ($i:literal) => { $i }; + ($i:expr) => { + $i + }; } #[macro_export] From c894913363d94a82351220c0843b92d8f8e3f48b Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 30 Jan 2023 12:36:22 -0800 Subject: [PATCH 307/339] EVM: Fix `Memory::grow` (#1124) * fix Memory .grow, remove * remove redundant size check --- .../src/interpreter/instructions/memory.rs | 37 +++++----------- actors/evm/src/interpreter/memory.rs | 43 ++++++++++++++----- actors/evm/src/lib.rs | 2 + 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 11033f7e6..6f5f1103a 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -2,7 +2,7 @@ use fil_actors_runtime::{ActorError, AsActorError}; -use crate::EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS; +use crate::{EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, EVM_WORD_SIZE}; use { crate::interpreter::memory::Memory, @@ -11,26 +11,12 @@ use { std::num::NonZeroUsize, }; -/// The size of the EVM 256-bit word in bytes. -const WORD_SIZE: usize = 32; - #[derive(Debug)] pub struct MemoryRegion { pub offset: usize, pub size: NonZeroUsize, } -#[inline] -fn grow_memory(mem: &mut Memory, mut new_size: usize) { - // Align to the next u256. - // Guaranteed to not overflow. - let alignment = new_size % WORD_SIZE; - if alignment > 0 { - new_size += WORD_SIZE - alignment; - } - mem.grow(new_size); -} - #[inline] pub fn get_memory_region( mem: &mut Memory, @@ -58,10 +44,7 @@ pub fn get_memory_region( .checked_add(size) .context_code(EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, "new memory size exceeds max u32")?; - let current_size = mem.len(); - if new_size as usize > current_size { - grow_memory(mem, new_size as usize); - } + mem.grow(new_size as usize); Ok(Some(MemoryRegion { offset: offset as usize, @@ -112,7 +95,7 @@ pub fn mload( _system: &System, index: U256, ) -> Result { - let region = get_memory_region(&mut state.memory, index, WORD_SIZE)?.expect("empty region"); + 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()]); @@ -126,11 +109,11 @@ pub fn mstore( index: U256, value: U256, ) -> Result<(), ActorError> { - let region = get_memory_region(&mut state.memory, index, WORD_SIZE)?.expect("empty region"); + let region = get_memory_region(&mut state.memory, index, EVM_WORD_SIZE)?.expect("empty region"); - let mut bytes = [0u8; WORD_SIZE]; + let mut bytes = [0u8; EVM_WORD_SIZE]; value.to_big_endian(&mut bytes); - state.memory[region.offset..region.offset + WORD_SIZE].copy_from_slice(&bytes); + state.memory[region.offset..region.offset + EVM_WORD_SIZE].copy_from_slice(&bytes); Ok(()) } @@ -451,7 +434,7 @@ mod tests { } #[test] - fn test_mszie_multiple_mstore8() { + fn test_msize_multiple_mstore8() { evm_unit_test! { (rt, m) { PUSH1; @@ -474,7 +457,7 @@ mod tests { } #[test] - fn test_mszie_multiple_mstore() { + fn test_msize_multiple_mstore() { evm_unit_test! { (rt, m) { PUSH1; @@ -496,8 +479,7 @@ mod tests { }; } #[test] - #[should_panic] - fn test_mszie_debug_panic() { + 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! { @@ -507,6 +489,7 @@ mod tests { m.state.memory.grow(12); m.step().expect("execution step failed"); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(32)); }; } } diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs index d6e361261..f5336a6e4 100644 --- a/actors/evm/src/interpreter/memory.rs +++ b/actors/evm/src/interpreter/memory.rs @@ -3,6 +3,8 @@ use { derive_more::{Deref, DerefMut}, }; +use crate::EVM_WORD_SIZE; + const PAGE_SIZE: usize = 4 * 1024; #[derive(Clone, Debug, Deref, DerefMut)] @@ -16,16 +18,38 @@ impl Default for Memory { impl Memory { #[inline] - /// Resizes memory to `size` length, reserving extra WASM pages as-needed. - /// TODO this should be renamed resize since it can also shrink memory. - pub fn grow(&mut self, size: usize) { + /// 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 size > cap { - let required_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; - self.0.reserve((PAGE_SIZE * required_pages) - self.0.len()); + if new_size > cap { + let required_pages = (new_size + PAGE_SIZE - 1) / PAGE_SIZE; + self.reserve_pages(required_pages); } - debug_assert_eq!(size % 32, 0, "MSIZE depends on memory length being multiple of 32"); - self.0.resize(size, 0); + + 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); } } @@ -34,11 +58,10 @@ mod tests { use super::*; #[test] - #[ignore = "failing because of new assert, replacing grow in new PR"] fn grow() { let mut mem = Memory::default(); mem.grow(PAGE_SIZE * 2 + 1); - assert_eq!(mem.len(), PAGE_SIZE * 2 + 1); + assert_eq!(mem.len(), PAGE_SIZE * 2 + EVM_WORD_SIZE); assert_eq!(mem.capacity(), PAGE_SIZE * 3); } } diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index e50830ea8..3af937532 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -46,6 +46,8 @@ 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. From b4e957a8214ab6b1c9c26c3b79fd2dd3d0345f43 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Mon, 30 Jan 2023 14:10:49 -0800 Subject: [PATCH 308/339] EVM: Use word size const (#1129) use word size const in certian places --- actors/evm/src/interpreter/instructions/call.rs | 4 ++-- actors/evm/src/interpreter/instructions/context.rs | 6 ++++-- actors/evm/src/interpreter/instructions/memory.rs | 2 +- actors/evm/src/interpreter/precompiles/evm.rs | 9 ++++++--- actors/evm/src/interpreter/precompiles/fvm.rs | 10 +++++----- actors/evm/src/lib.rs | 10 ++++++---- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 397fc6aac..515eeaa46 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -45,8 +45,8 @@ pub fn calldataload( .ok() .filter(|&start| start < input_len) .map(|start: usize| { - let end = core::cmp::min(start.saturating_add(32usize), input_len); - let mut data = [0; 32]; + 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) }) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 367b5ff67..a60e3ea52 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,6 +1,8 @@ use fil_actors_runtime::ActorError; use fvm_shared::clock::ChainEpoch; +use crate::EVM_WORD_SIZE; + use { crate::interpreter::{ExecutionState, System, U256}, fil_actors_runtime::runtime::Runtime, @@ -26,8 +28,8 @@ pub fn blockhash( .and_then(|height| system.rt.tipset_cid(height)) .map(|cid| { let mut hash = cid.hash().digest(); - if hash.len() > 32 { - hash = &hash[..32] + if hash.len() > EVM_WORD_SIZE { + hash = &hash[..EVM_WORD_SIZE] } U256::from_big_endian(hash) }) diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 6f5f1103a..ecea19d71 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -416,7 +416,7 @@ mod tests { MSTORE; } m.state.memory.grow(64); - m.state.memory[..32].copy_from_slice(&[0xff; 32]); + m.state.memory[..EVM_WORD_SIZE].copy_from_slice(&[0xff; EVM_WORD_SIZE]); // single byte outside expected overwritten area m.state.memory[32] = 0xfe; diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index c1eb3180a..4bab7105d 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -9,7 +9,10 @@ use num_traits::{One, Zero}; use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1, G2}; use uint::byteorder::{ByteOrder, LE}; -use crate::interpreter::{precompiles::PrecompileError, System, U256}; +use crate::{ + interpreter::{precompiles::PrecompileError, System, U256}, + EVM_WORD_SIZE, +}; use super::{PrecompileContext, PrecompileResult}; use crate::reader::ValueReader; @@ -85,7 +88,7 @@ pub(super) fn ripemd160( let mut out = vec![0; 12]; let hash = system.rt.hash(SupportedHashes::Ripemd160, input); out.extend_from_slice(&hash); - debug_assert_eq!(out.len(), 32); + debug_assert_eq!(out.len(), EVM_WORD_SIZE); Ok(out) } @@ -234,7 +237,7 @@ pub(super) fn ec_pairing( let accumulated = pairing_batch(&groups); let paring_success = if accumulated == Gt::one() { U256::one() } else { U256::zero() }; - let mut ret = [0u8; 32]; + let mut ret = [0u8; EVM_WORD_SIZE]; paring_success.to_big_endian(&mut ret); Ok(ret.to_vec()) } diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index 00d30b3b3..e66abca9c 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,4 +1,4 @@ -use crate::EVM_MAX_RESERVED_METHOD; +use crate::{EVM_MAX_RESERVED_METHOD, EVM_WORD_SIZE}; use fil_actors_runtime::runtime::Runtime; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags, METHOD_SEND}; @@ -205,16 +205,16 @@ pub(super) fn call_actor_shared( let ret_blk = data.unwrap_or(IpldBlock { codec: 0, data: vec![] }); - let mut output = Vec::with_capacity(4 * 32 + ret_blk.data.len()); + 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() + 32).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() % 32; + let offset = output.len() % EVM_WORD_SIZE; if offset > 0 { - output.resize(output.len() - offset + 32, 0); + output.resize(output.len() - offset + EVM_WORD_SIZE, 0); } output }; diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 3af937532..c3b2769ff 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -300,15 +300,17 @@ impl EvmContractActor { /// Format "filecoin_native_method" input parameters. fn handle_filecoin_method_input(method: u64, codec: u64, params: &[u8]) -> Vec { - let static_args = [method, codec, 32 * 3 /* start of params */, params.len() as u64]; - let total_words = static_args.len() + (params.len() / 32) + (params.len() % 32 > 0) as usize; - let len = 4 + total_words * 32; + 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() + (32 - encoded.len()), 0); + buf.resize(buf.len() + (EVM_WORD_SIZE - encoded.len()), 0); buf.extend_from_slice(&encoded); } // Extend with the params, then right-pad with zeros. From 7c9d111794f07b3e928bee41d84e5d51a9d8b577 Mon Sep 17 00:00:00 2001 From: raulk Date: Tue, 31 Jan 2023 00:12:09 +0000 Subject: [PATCH 309/339] fix selfdestruct test files. (#1134) This was messing up Git on macOS due to case sensitivity issues. --- actors/evm/tests/contracts/SelfDestruct_storage.json | 1 - actors/evm/tests/contracts/Selfdestruct_storage.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 actors/evm/tests/contracts/SelfDestruct_storage.json diff --git a/actors/evm/tests/contracts/SelfDestruct_storage.json b/actors/evm/tests/contracts/SelfDestruct_storage.json deleted file mode 100644 index ed90c78fe..000000000 --- a/actors/evm/tests/contracts/SelfDestruct_storage.json +++ /dev/null @@ -1 +0,0 @@ -{"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/Selfdestruct_storage.json b/actors/evm/tests/contracts/Selfdestruct_storage.json index 325d0043f..ed90c78fe 100644 --- a/actors/evm/tests/contracts/Selfdestruct_storage.json +++ b/actors/evm/tests/contracts/Selfdestruct_storage.json @@ -1 +1 @@ -{"storage":[]} \ No newline at end of file +{"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 From 1d473c5feb6818a943792eb463df6b106c559b69 Mon Sep 17 00:00:00 2001 From: raulk Date: Tue, 31 Jan 2023 19:03:32 +0000 Subject: [PATCH 310/339] EVM: LOG{0..4}: adjust event definitions as per FIP-0054. (#1057) * EVM: LOG{0..4}: adjust event definitions as per FIP-0054. * fix selfdestruct test files. This was messing up Git on macOS due to case sensitivity issues. --- .../src/interpreter/instructions/log_event.rs | 8 +++---- actors/evm/tests/events.rs | 24 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs index 653e433a3..760b32890 100644 --- a/actors/evm/src/interpreter/instructions/log_event.rs +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -8,10 +8,10 @@ use { }; /// The event key for the Ethereum log data. -const EVENT_DATA_KEY: &str = "data"; +const EVENT_DATA_KEY: &str = "d"; /// The event keys for the Ethereum log topics. -const EVENT_TOPIC_KEYS: &[&str] = &["topic1", "topic2", "topic3", "topic4"]; +const EVENT_TOPIC_KEYS: &[&str] = &["t1", "t2", "t3", "t4"]; #[inline] pub fn log( @@ -39,7 +39,7 @@ pub fn log( let key = EVENT_TOPIC_KEYS[i]; let topic = topics[i]; let entry = Entry { - flags: Flags::FLAG_INDEXED_VALUE, + flags: Flags::FLAG_INDEXED_ALL, key: (*key).to_owned(), value: to_vec(&topic)?.into(), // U256 serializes as a byte string. }; @@ -50,7 +50,7 @@ pub fn log( 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_VALUE, + flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), value: RawBytes::serialize(BytesSer(&data))?, }; diff --git a/actors/evm/tests/events.rs b/actors/evm/tests/events.rs index 0391418d3..54c12a1a4 100644 --- a/actors/evm/tests/events.rs +++ b/actors/evm/tests/events.rs @@ -76,8 +76,8 @@ fn test_events() { let mut contract_params = vec![0u8; 32]; rt.expect_emitted_event(ActorEvent { entries: vec![Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "data".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "d".to_string(), value: to_vec(&RawBytes::from( [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), )) @@ -97,28 +97,28 @@ fn test_events() { rt.expect_emitted_event(ActorEvent { entries: vec![ Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "topic1".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "t1".to_string(), value: to_vec(&RawBytes::from([0x11, 0x11].to_vec())).unwrap().into(), }, Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "topic2".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "t2".to_string(), value: to_vec(&RawBytes::from([0x22, 0x22].to_vec())).unwrap().into(), }, Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "topic3".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "t3".to_string(), value: to_vec(&RawBytes::from([0x33, 0x33].to_vec())).unwrap().into(), }, Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "topic4".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "t4".to_string(), value: to_vec(&RawBytes::from([0x44, 0x44].to_vec())).unwrap().into(), }, Entry { - flags: Flags::FLAG_INDEXED_VALUE, - key: "data".to_string(), + flags: Flags::FLAG_INDEXED_ALL, + key: "d".to_string(), value: to_vec(&RawBytes::from( [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), )) From 300d83fd584a8033359e3935ecfe22c975fe318e Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 1 Feb 2023 07:43:10 -0800 Subject: [PATCH 311/339] EVM: Test keccak opcode (#1132) test keccak --- .../evm/src/interpreter/instructions/hash.rs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index b0e459c8b..59760d4d7 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -26,3 +26,62 @@ pub fn keccak256( Ok(U256::from_big_endian(&buf[..size])) } + +#[cfg(test)] +mod test { + use fil_actors_runtime::runtime::Primitives; + + use crate::{evm_unit_test, interpreter::U256, 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! { + (rt, 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! { + (rt, 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)); + }; + } + } +} From 01a1a7804f0b6dfd903bc2eacb4a2bd11385c0b4 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 1 Feb 2023 07:44:33 -0800 Subject: [PATCH 312/339] EVM: Test balance ops (#1142) * add unit test for balance * add selfbalance unit test * fmt --- .../evm/src/interpreter/instructions/state.rs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index db81d9147..3811536fc 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -35,3 +35,113 @@ pub fn selfbalance( // no conversion needed (atto, 1e18). Ok(U256::from(&system.rt.current_balance())) } + +#[cfg(test)] +mod test { + use fvm_shared::address::Address; + + use crate::{ + evm_unit_test, + interpreter::{address::EthAddress, U256}, + }; + + #[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, m) { + PUSH0; + MLOAD; + BALANCE; + } + + m.system.rt.in_call = true; + + let id_address = 1111; + if has_id { + let addr: EthAddress = addr.into(); + m.system.rt.add_id_address(addr.into(), Address::new_id(id_address)) + } + m.system.rt.actor_balances.insert(id_address, TokenAmount::from_atto(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, m) { + PUSH0; + MLOAD; + BALANCE; + } + + m.system.rt.in_call = true; + + // 0xff id address gets balance + m.system.rt.actor_balances.insert(id as u64, TokenAmount::from_atto(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, m) { + SELFBALANCE; + } + + m.system.rt.in_call = true; + m.system.rt.add_balance(TokenAmount::from(&balance)); + + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), balance); + }; + } + } +} From 859a4b4c1c4234d81ccfc985b08cb7e0dd714978 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 1 Feb 2023 13:01:18 -0800 Subject: [PATCH 313/339] EVM: Factor out shared crate & reorg (#1145) * EVM: Reorganize to keep internals internal The next step will be to move some shared components into a new crate, but keeping internals private is the first step. * EVM: factor out a shared crate --- Cargo.lock | 15 +- Cargo.toml | 2 +- actors/evm/Cargo.toml | 2 +- actors/evm/shared/Cargo.toml | 17 ++ .../interpreter => shared/src}/address.rs | 6 +- actors/evm/shared/src/lib.rs | 2 + .../{src/interpreter => shared/src}/uints.rs | 241 ++++++++---------- actors/evm/src/ext.rs | 30 +++ actors/evm/src/interpreter/bytecode.rs | 4 +- actors/evm/src/interpreter/execution.rs | 14 +- .../interpreter/instructions/arithmetic.rs | 10 +- .../src/interpreter/instructions/bitwise.rs | 2 +- .../src/interpreter/instructions/boolean.rs | 8 +- .../evm/src/interpreter/instructions/call.rs | 16 +- .../src/interpreter/instructions/context.rs | 3 +- .../src/interpreter/instructions/control.rs | 6 +- .../evm/src/interpreter/instructions/ext.rs | 7 +- .../evm/src/interpreter/instructions/hash.rs | 6 +- .../src/interpreter/instructions/lifecycle.rs | 41 +-- .../src/interpreter/instructions/log_event.rs | 3 +- .../src/interpreter/instructions/memory.rs | 3 +- .../evm/src/interpreter/instructions/mod.rs | 30 +-- .../evm/src/interpreter/instructions/stack.rs | 2 +- .../evm/src/interpreter/instructions/state.rs | 9 +- .../src/interpreter/instructions/storage.rs | 3 +- actors/evm/src/interpreter/mod.rs | 37 ++- actors/evm/src/interpreter/precompiles/evm.rs | 9 +- actors/evm/src/interpreter/precompiles/fvm.rs | 3 +- actors/evm/src/interpreter/precompiles/mod.rs | 21 +- actors/evm/src/interpreter/stack.rs | 5 +- actors/evm/src/interpreter/system.rs | 18 +- actors/evm/src/lib.rs | 24 +- actors/evm/src/reader.rs | 3 +- actors/evm/src/state.rs | 8 + actors/evm/tests/asm.rs | 2 +- actors/evm/tests/basic.rs | 2 +- actors/evm/tests/calc.rs | 3 +- actors/evm/tests/call.rs | 6 +- actors/evm/tests/create.rs | 20 +- actors/evm/tests/delegate_call.rs | 6 +- actors/evm/tests/env.rs | 2 +- actors/evm/tests/ext_opcodes.rs | 2 +- actors/evm/tests/misc.rs | 4 +- actors/evm/tests/precompile.rs | 3 +- actors/evm/tests/selfdestruct.rs | 2 +- actors/evm/tests/storage_footprint.rs | 2 +- actors/evm/tests/util.rs | 4 +- test_vm/Cargo.toml | 1 + test_vm/tests/evm_test.rs | 2 +- 49 files changed, 333 insertions(+), 338 deletions(-) create mode 100644 actors/evm/shared/Cargo.toml rename actors/evm/{src/interpreter => shared/src}/address.rs (98%) create mode 100644 actors/evm/shared/src/lib.rs rename actors/evm/{src/interpreter => shared/src}/uints.rs (61%) create mode 100644 actors/evm/src/ext.rs diff --git a/Cargo.lock b/Cargo.lock index f6dcd808b..2b6857ac9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1874,6 +1874,7 @@ dependencies = [ "derive_more", "ethers 1.0.2", "etk-asm", + "fil_actors_evm_shared", "fil_actors_runtime", "fixed-hash 0.7.0", "frc42_dispatch", @@ -1899,7 +1900,6 @@ dependencies = [ "strum", "strum_macros", "substrate-bn", - "uint", ] [[package]] @@ -2096,6 +2096,18 @@ dependencies = [ "serde", ] +[[package]] +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.17", + "hex", + "serde", + "uint", +] + [[package]] name = "fil_actors_runtime" version = "10.0.0-alpha.1" @@ -4552,6 +4564,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_system", "fil_actor_verifreg", + "fil_actors_evm_shared", "fil_actors_runtime", "fil_builtin_actors_state", "frc46_token", diff --git a/Cargo.toml b/Cargo.toml index ad3cf5c2a..2e343e7e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ 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" } diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 27e488e8f..59349029f 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -32,7 +32,6 @@ strum = "0.24" strum_macros = "0.24" multihash = { version = "0.16.1", default-features = false } derive_more = "0.99" -uint = { version = "0.9.3", default-features = false } fixed-hash = { version = "0.7.0", default-features = false } impl-serde = { version = "0.3.2", default-features = false } arrayvec = { version = "0.7.2", features = ["serde"] } @@ -43,6 +42,7 @@ near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/ne lazy_static = "1.4.0" once_cell = { version = "1.16.0", default-features = false} frc42_dispatch = "3.0.1-alpha.1" +fil_actors_evm_shared = { version = "10.0.0-alpha.1", path = "shared" } [dev-dependencies] fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/evm/shared/Cargo.toml b/actors/evm/shared/Cargo.toml new file mode 100644 index 000000000..22c7ad944 --- /dev/null +++ b/actors/evm/shared/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "fil_actors_evm_shared" +description = "Shared libraries for the built-in EVM actor for Filecoin" +version = "10.0.0-alpha.1" +license = "MIT OR Apache-2.0" +authors = ["Protocol Labs", "Filecoin Core Devs"] +edition = "2021" +repository = "https://github.com/filecoin-project/builtin-actors" +keywords = ["filecoin", "web3", "wasm", "evm"] + +[dependencies] +serde = { version = "1.0.136", features = ["derive"] } +fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../../runtime" } +fvm_ipld_encoding = "0.3.3" +uint = { version = "0.9.3", default-features = false } +hex = "0.4.3" diff --git a/actors/evm/src/interpreter/address.rs b/actors/evm/shared/src/address.rs similarity index 98% rename from actors/evm/src/interpreter/address.rs rename to actors/evm/shared/src/address.rs index d92869fbd..305038cfc 100644 --- a/actors/evm/src/interpreter/address.rs +++ b/actors/evm/shared/src/address.rs @@ -1,4 +1,4 @@ -use crate::U256; +use crate::uints::U256; use fil_actors_runtime::EAM_ACTOR_ID; use fvm_ipld_encoding::{serde, strict_bytes}; use fvm_shared::address::Address; @@ -86,8 +86,8 @@ impl AsRef<[u8]> for EthAddress { #[cfg(test)] mod tests { - use crate::interpreter::address::EthAddress; - use crate::U256; + use super::EthAddress; + use crate::uints::U256; const TYPE_PADDING: &[u8] = &[0; 12]; // padding (12 bytes) const ID_ADDRESS_MARKER: &[u8] = &[0xff]; // ID address marker (1 byte) diff --git a/actors/evm/shared/src/lib.rs b/actors/evm/shared/src/lib.rs new file mode 100644 index 000000000..f74d12fca --- /dev/null +++ b/actors/evm/shared/src/lib.rs @@ -0,0 +1,2 @@ +pub mod address; +pub mod uints; diff --git a/actors/evm/src/interpreter/uints.rs b/actors/evm/shared/src/uints.rs similarity index 61% rename from actors/evm/src/interpreter/uints.rs rename to actors/evm/shared/src/uints.rs index 123b9f2e0..d51e0a306 100644 --- a/actors/evm/src/interpreter/uints.rs +++ b/actors/evm/shared/src/uints.rs @@ -1,12 +1,12 @@ -#![allow(dead_code)] // 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)] -use serde::{Deserialize, Serialize}; -use substrate_bn::arith; +#[doc(inline)] +pub use uint::byteorder; -use crate::BytecodeHash; +use serde::{Deserialize, Serialize}; +//use substrate_bn::arith; use { fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, std::cmp::Ordering, std::fmt, @@ -64,6 +64,84 @@ impl U256 { } } + #[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); @@ -94,12 +172,6 @@ impl From<&TokenAmount> for U256 { } } -impl From for arith::U256 { - fn from(src: U256) -> arith::U256 { - arith::U256::from(src.0) - } -} - impl From for U512 { fn from(v: U256) -> Self { let [a, b, c, d] = v.0; @@ -115,13 +187,6 @@ impl From<&U256> for TokenAmount { } } -impl From for U256 { - fn from(bytecode: BytecodeHash) -> Self { - let bytes: [u8; 32] = bytecode.into(); - Self::from(bytes) - } -} - impl Serialize for U256 { fn serialize(&self, serializer: S) -> Result where @@ -165,102 +230,6 @@ fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { &v[v.iter().take_while(|&&b| b == 0).count()..] } -macro_rules! impl_rlp_codec_uint { - ($type:ident, $bytes_len: expr) => { - impl rlp::Encodable for $type { - fn rlp_append(&self, s: &mut rlp::RlpStream) { - let mut bytes = [0u8; $bytes_len]; - self.to_big_endian(&mut bytes); - let zbytes = zeroless_view(&bytes); - s.encoder().encode_value(&zbytes); - } - } - impl rlp::Decodable for $type { - fn decode(rlp: &rlp::Rlp) -> Result { - rlp - .decoder() - .decode_value(|bytes| Ok($type::from_big_endian(bytes))) - } - } - }; -} - -// RLP Support -impl_rlp_codec_uint!(U256, 32); -impl_rlp_codec_uint!(U512, 64); - -#[inline(always)] -pub fn i256_div(mut first: U256, mut second: U256) -> U256 { - if first.is_zero() || second.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 (first, second) == (U256::I128_MIN, U256::ONE) { - return U256::I128_MIN; - } - - // 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(always)] -pub fn i256_mod(mut first: U256, mut second: U256) -> U256 { - if first.is_zero() || second.is_zero() { - // X % 0 or 0 % X is always 0. - return U256::ZERO; - } - - // 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 - } -} - -#[inline(always)] -pub fn i256_cmp(first: U256, second: U256) -> Ordering { - // true > false: - // - true < positive: - match second.i256_is_negative().cmp(&first.i256_is_negative()) { - Ordering::Equal => first.cmp(&second), - sign_cmp => sign_cmp, - } -} - #[cfg(test)] mod tests { use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; @@ -282,17 +251,17 @@ mod tests { let max_value = U256::from(2).pow(255.into()) - 1; let neg_max_value = U256::from(2).pow(255.into()) - 1; - assert_eq!(i256_div(U256::I128_MIN, minus_one), U256::I128_MIN); - assert_eq!(i256_div(U256::I128_MIN, one), U256::I128_MIN); - assert_eq!(i256_div(one, U256::I128_MIN), zero); - assert_eq!(i256_div(max_value, one), max_value); - assert_eq!(i256_div(max_value, minus_one), neg_max_value); - assert_eq!(i256_div(one_hundred, minus_one), neg_one_hundred); - assert_eq!(i256_div(one_hundred, two), fifty); - - assert_eq!(i256_div(zero, zero), zero); - assert_eq!(i256_div(one, zero), zero); - assert_eq!(i256_div(zero, one), zero); + 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] @@ -309,21 +278,21 @@ mod tests { let max_value = U256::from(2).pow(255.into()) - 1; // zero - assert_eq!(i256_mod(minus_one, U256::ZERO), U256::ZERO); - assert_eq!(i256_mod(max_value, U256::ZERO), U256::ZERO); - assert_eq!(i256_mod(U256::ZERO, U256::ZERO), U256::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!(i256_mod(minus_one, two), minus_one); - assert_eq!(i256_mod(U256::I128_MIN, one), 0); - assert_eq!(i256_mod(one, U256::I128_MIN), one); - assert_eq!(i256_mod(one, U256::from(i128::MAX)), one); + 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!(i256_mod(max_value, minus_one), zero); - assert_eq!(i256_mod(neg_one_hundred, minus_one), zero); - assert_eq!(i256_mod(one_hundred, two), zero); - assert_eq!(i256_mod(one_hundred, neg_three), 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!(i256_mod(neg_one_hundred, three), minus_one); + assert_eq!(neg_one_hundred.i256_mod(&three), minus_one); let a = U256::from(95).i256_neg(); let b = U256::from(256); 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/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs index 6c34aa3e7..a2ba298e8 100644 --- a/actors/evm/src/interpreter/bytecode.rs +++ b/actors/evm/src/interpreter/bytecode.rs @@ -1,8 +1,6 @@ -#![allow(dead_code)] - use std::ops::Deref; -use super::execution::opcodes; +use super::opcodes; #[derive(Clone, Debug)] pub struct Bytecode { diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 13fe39fc9..492170662 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -1,14 +1,12 @@ -#![allow(dead_code)] - +use fil_actors_evm_shared::address::EthAddress; use fil_actors_runtime::ActorError; use fvm_shared::econ::TokenAmount; -use super::address::EthAddress; use { super::instructions, - crate::interpreter::memory::Memory, - crate::interpreter::stack::Stack, - crate::interpreter::{Bytecode, Output, System}, + super::memory::Memory, + super::stack::Stack, + super::{Bytecode, Output, System}, bytes::Bytes, fil_actors_runtime::runtime::Runtime, }; @@ -57,7 +55,7 @@ pub struct Machine<'r, 'a, RT: Runtime + 'a> { macro_rules! def_opcodes { ($($code:literal: $op:ident,)*) => { - pub const fn jumptable<'r, 'a, RT: Runtime>() -> [Instruction<'r, 'a, RT>; 256] { + pub(crate) const fn jumptable<'r, 'a, RT: Runtime>() -> [Instruction<'r, 'a, RT>; 256] { def_ins_raw! { UNDEFINED(_m) { Err(ActorError::unchecked( @@ -98,7 +96,7 @@ pub mod opcodes { use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::ActorError; - pub type Instruction<'r, 'a, RT> = + pub(crate) type Instruction<'r, 'a, RT> = unsafe fn(*mut Machine<'r, 'a, RT>) -> Result<(), ActorError>; def_opcodes! { diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs index 2417d86cf..77971e27d 100644 --- a/actors/evm/src/interpreter/instructions/arithmetic.rs +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -1,4 +1,4 @@ -use {crate::interpreter::uints::*, crate::interpreter::U256}; +use fil_actors_evm_shared::uints::{U256, U512}; #[inline] pub fn add(a: U256, b: U256) -> U256 { @@ -28,7 +28,7 @@ pub fn div(a: U256, b: U256) -> U256 { #[inline] pub fn sdiv(a: U256, b: U256) -> U256 { - i256_div(a, b) + a.i256_div(&b) } #[inline] @@ -42,7 +42,7 @@ pub fn modulo(a: U256, b: U256) -> U256 { #[inline] pub fn smod(a: U256, b: U256) -> U256 { - i256_mod(a, b) + a.i256_mod(&b) } #[inline] @@ -111,8 +111,8 @@ pub fn exp(mut base: U256, power: U256) -> U256 { #[cfg(test)] mod test { mod basic { - use crate::interpreter::instructions::arithmetic::*; - use crate::interpreter::U256; + use super::super::*; + use fil_actors_evm_shared::uints::U256; #[test] fn test_addmod() { diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs index 5a9024c74..b23452cbb 100644 --- a/actors/evm/src/interpreter/instructions/bitwise.rs +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -1,4 +1,4 @@ -use crate::interpreter::U256; +use fil_actors_evm_shared::uints::U256; #[inline] pub fn byte(i: U256, x: U256) -> U256 { diff --git a/actors/evm/src/interpreter/instructions/boolean.rs b/actors/evm/src/interpreter/instructions/boolean.rs index 8dbb3d33a..72f4e55d0 100644 --- a/actors/evm/src/interpreter/instructions/boolean.rs +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -1,4 +1,6 @@ -use {crate::interpreter::uints::*, crate::interpreter::U256, std::cmp::Ordering}; +use std::cmp::Ordering; + +use fil_actors_evm_shared::uints::U256; #[inline] pub fn lt(a: U256, b: U256) -> U256 { @@ -12,12 +14,12 @@ pub fn gt(a: U256, b: U256) -> U256 { #[inline] pub(crate) fn slt(a: U256, b: U256) -> U256 { - U256::from_u64((i256_cmp(a, b) == Ordering::Less).into()) + U256::from_u64((a.i256_cmp(&b) == Ordering::Less).into()) } #[inline] pub(crate) fn sgt(a: U256, b: U256) -> U256 { - U256::from_u64((i256_cmp(a, b) == Ordering::Greater).into()) + U256::from_u64((a.i256_cmp(&b) == Ordering::Greater).into()) } #[inline] diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 515eeaa46..1b4876266 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -1,21 +1,23 @@ #![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}; +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::address::EthAddress, crate::interpreter::instructions::memory::MemoryRegion, crate::interpreter::precompiles, crate::interpreter::ExecutionState, crate::interpreter::System, - crate::interpreter::U256, crate::{DelegateCallParams, Method}, fil_actors_runtime::runtime::Runtime, fil_actors_runtime::ActorError, @@ -26,14 +28,6 @@ use { /// The gas granted on bare "transfers". const TRANSFER_GAS_LIMIT: U256 = U256::from_u64(10_000_000); -/// The kind of call-like instruction. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CallKind { - Call, - DelegateCall, - StaticCall, -} - pub fn calldataload( state: &mut ExecutionState, _: &System, diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index a60e3ea52..1004e6154 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -1,10 +1,11 @@ +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, U256}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index b0f33f356..2f98cd96b 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -1,4 +1,5 @@ use bytes::Bytes; +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::{ActorError, AsActorError}; use crate::{ @@ -10,7 +11,7 @@ use crate::{ use { super::memory::get_memory_region, crate::interpreter::Bytecode, - crate::interpreter::{ExecutionState, System, U256}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; @@ -158,8 +159,9 @@ pub fn jumpi(bytecode: &Bytecode, pc: usize, dest: U256, test: U256) -> Result, - 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 EamReturn { - pub actor_id: u64, - pub robust_address: Option
, - pub eth_address: EthAddress, -} - #[inline] pub fn create( state: &mut ExecutionState, @@ -73,8 +48,8 @@ pub fn create( }; let nonce = system.increment_nonce(); - let params = CreateParams { code: input_data.to_vec(), nonce }; - create_init(system, IpldBlock::serialize_cbor(¶ms)?, CREATE_METHOD_NUM, value) + 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( @@ -107,10 +82,10 @@ pub fn create2( } else { &[] }; - let params = Create2Params { code: input_data.to_vec(), salt }; + let params = eam::Create2Params { code: input_data.to_vec(), salt }; system.increment_nonce(); - create_init(system, IpldBlock::serialize_cbor(¶ms)?, CREATE2_METHOD_NUM, endowment) + create_init(system, IpldBlock::serialize_cbor(¶ms)?, eam::CREATE2_METHOD_NUM, endowment) } /// call into Ethereum Address Manager to make the new account @@ -145,7 +120,7 @@ fn create_init( Ok(match ret { Ok(eam_ret) => { - let ret: EamReturn = deserialize_block(eam_ret)?; + let ret: eam::CreateReturn = deserialize_block(eam_ret)?; ret.eth_address.as_evm_word() } Err(_) => U256::zero(), diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs index 760b32890..90e39502c 100644 --- a/actors/evm/src/interpreter/instructions/log_event.rs +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -1,9 +1,10 @@ use crate::interpreter::instructions::memory::get_memory_region; +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::ActorError; use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; use fvm_shared::event::{Entry, Flags}; use { - crate::interpreter::{ExecutionState, System, U256}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index ecea19d71..c3b21af2f 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -1,12 +1,13 @@ #!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, U256}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, std::num::NonZeroUsize, }; diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs index 20e451f7e..7c9d7fe2d 100644 --- a/actors/evm/src/interpreter/instructions/mod.rs +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -1,22 +1,22 @@ #![allow(clippy::unnecessary_mut_passed)] -pub mod arithmetic; -pub mod bitwise; -pub mod boolean; -pub mod call; -pub mod context; -pub mod control; -pub mod ext; -pub mod hash; -pub mod lifecycle; -pub mod log_event; -pub mod memory; -pub mod stack; -pub mod state; -pub mod storage; +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 crate::interpreter::U256; +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::ActorError; diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs index cb6144669..67d7d6d20 100644 --- a/actors/evm/src/interpreter/instructions/stack.rs +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -1,9 +1,9 @@ #![allow(clippy::missing_safety_doc)] +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::ActorError; use crate::interpreter::stack::Stack; -use crate::interpreter::U256; macro_rules! be_u64 { ($byte:expr) => {$byte as u64}; diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 3811536fc..191b3929a 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -1,9 +1,8 @@ +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; use fil_actors_runtime::ActorError; use fvm_shared::address::Address; -use crate::U256; use { - crate::interpreter::address::EthAddress, crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; @@ -38,12 +37,10 @@ pub fn selfbalance( #[cfg(test)] mod test { + use fil_actors_evm_shared::{address::EthAddress, uints::U256}; use fvm_shared::address::Address; - use crate::{ - evm_unit_test, - interpreter::{address::EthAddress, U256}, - }; + use crate::evm_unit_test; #[test] fn balance_basic() { diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index d992e9cd9..d46ed5146 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -1,7 +1,8 @@ +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::ActorError; use { - crate::interpreter::{ExecutionState, System, U256}, + crate::interpreter::{ExecutionState, System}, fil_actors_runtime::runtime::Runtime, }; diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 961f22291..908f05d45 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -1,31 +1,26 @@ -pub mod address; -pub mod bytecode; -pub mod execution; -pub mod instructions; -pub mod memory; -pub mod output; -pub mod precompiles; -pub mod stack; -pub mod system; -pub mod uints; +mod bytecode; +mod execution; +mod instructions; +mod memory; +mod output; +mod precompiles; +mod stack; +mod system; #[cfg(test)] pub mod test_util; pub use { bytecode::Bytecode, - execution::{execute, ExecutionState}, - output::Output, + execution::{execute, opcodes, ExecutionState}, + output::{Outcome, Output}, system::System, - uints::{U256, U512}, }; -#[macro_export] -macro_rules! abort { - ($code:ident, $msg:literal $(, $ex:expr)*) => { - fvm_sdk::vm::abort( - fvm_shared::error::ExitCode::$code.value(), - Some(format!($msg, $($ex,)*).as_str()), - ) - }; +/// 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/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 4bab7105d..a20bcf102 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -1,5 +1,9 @@ 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, @@ -7,10 +11,9 @@ use fvm_shared::crypto::{ }; use num_traits::{One, Zero}; use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1, G2}; -use uint::byteorder::{ByteOrder, LE}; use crate::{ - interpreter::{precompiles::PrecompileError, System, U256}, + interpreter::{precompiles::PrecompileError, System}, EVM_WORD_SIZE, }; @@ -300,7 +303,7 @@ pub(super) fn blake2f( #[cfg(test)] mod tests { - use crate::interpreter::instructions::call::CallKind; + use crate::interpreter::CallKind; use super::*; use fil_actors_runtime::test_utils::MockRuntime; diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index e66abca9c..a7ed15eda 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -1,9 +1,10 @@ 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::{instructions::call::CallKind, System, U256}; +use crate::interpreter::{CallKind, System}; use super::{PrecompileContext, PrecompileError, PrecompileResult}; use crate::reader::ValueReader; diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index 7550aff92..b2c56302f 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -1,12 +1,13 @@ 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::{address::EthAddress, instructions::call::CallKind, System, U256}; +use super::{CallKind, System}; mod evm; mod fvm; @@ -113,7 +114,6 @@ pub enum PrecompileError { // EVM precompile errors EcErr(CurveError), IncorrectInputSize, - OutOfGas, // FVM precompile errors InvalidInput, CallForbidden, @@ -163,25 +163,12 @@ pub struct PrecompileContext { pub value: U256, } -/// Native Type of a given contract -#[repr(u32)] -pub enum NativeType { - NonExistent = 0, - // user actors are flattened to "system" - /// System includes any singletons not otherwise defined. - System = 1, - Placeholder = 2, - Account = 3, - StorageProvider = 4, - EVMContract = 5, - OtherTypes = 6, -} - #[cfg(test)] mod test { + use fil_actors_evm_shared::address::EthAddress; use fil_actors_runtime::test_utils::MockRuntime; - use crate::interpreter::{address::EthAddress, precompiles::is_reserved_precompile_address}; + use crate::interpreter::precompiles::is_reserved_precompile_address; use super::Precompiles; diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs index d424bac0d..73a19e3f8 100644 --- a/actors/evm/src/interpreter/stack.rs +++ b/actors/evm/src/interpreter/stack.rs @@ -1,8 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc)] +#![allow(clippy::missing_safety_doc)] +use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::{ActorError, AsActorError}; -use crate::{interpreter::U256, EVM_CONTRACT_STACK_OVERFLOW, EVM_CONTRACT_STACK_UNDERFLOW}; +use crate::{EVM_CONTRACT_STACK_OVERFLOW, EVM_CONTRACT_STACK_UNDERFLOW}; /// Ethereum Yellow Paper (9.1) pub const STACK_SIZE: usize = 1024; diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 01b4f03c5..4662902a6 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -1,7 +1,6 @@ -#![allow(dead_code)] - 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, }; @@ -23,10 +22,7 @@ use once_cell::unsync::OnceCell; use crate::state::{State, Tombstone}; use crate::BytecodeHash; -use super::{address::EthAddress, Bytecode}; - use { - crate::interpreter::U256, cid::Cid, fil_actors_runtime::{runtime::Runtime, ActorError}, fvm_ipld_blockstore::Blockstore, @@ -461,15 +457,3 @@ impl<'r, RT: Runtime> System<'r, RT> { self.tombstone = Some(crate::current_tombstone(self.rt)); } } - -pub fn load_bytecode(bs: &BS, 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))) - } -} diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index c3b2769ff..e632e90fa 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1,20 +1,24 @@ +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 interpreter::{address::EthAddress, system::load_bytecode}; -use crate::interpreter::output::Outcome; +use crate::interpreter::Outcome; use crate::reader::ValueReader; +#[doc(hidden)] +pub mod ext; pub mod interpreter; pub(crate) mod reader; mod state; use { - crate::interpreter::{execute, Bytecode, ExecutionState, System, U256}, + crate::interpreter::{execute, Bytecode, ExecutionState, System}, bytes::Bytes, cid::Cid, fil_actors_runtime::{ @@ -88,7 +92,19 @@ pub(crate) fn is_dead(rt: &impl Runtime, state: &State) -> bool { state.tombstone.map_or(false, |t| t != current_tombstone(rt)) } -pub fn initialize_evm_contract( +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, diff --git a/actors/evm/src/reader.rs b/actors/evm/src/reader.rs index 7424915bf..ffb52f91a 100644 --- a/actors/evm/src/reader.rs +++ b/actors/evm/src/reader.rs @@ -1,10 +1,9 @@ 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}; -use crate::interpreter::U256; - #[derive(Clone, Eq, PartialEq, Debug)] pub struct OverflowError; diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs index 1f678c692..ae5d6892f 100644 --- a/actors/evm/src/state.rs +++ b/actors/evm/src/state.rs @@ -1,5 +1,6 @@ use std::array::TryFromSliceError; +use fil_actors_evm_shared::uints::U256; use fvm_shared::ActorID; use { @@ -76,6 +77,13 @@ impl From for Vec { } } +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; diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs index 5ee860aaa..db2e08192 100644 --- a/actors/evm/tests/asm.rs +++ b/actors/evm/tests/asm.rs @@ -1,5 +1,5 @@ use etk_asm::ingest::Ingest; -use evm::interpreter::execution::opcodes; +use evm::interpreter::opcodes; use fil_actor_evm as evm; const PRELUDE: &str = r#" diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs index 6c3f4fefc..c9c592b5c 100644 --- a/actors/evm/tests/basic.rs +++ b/actors/evm/tests/basic.rs @@ -1,8 +1,8 @@ mod asm; use cid::Cid; -use evm::interpreter::U256; 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; diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs index 3c6039521..133dbbbaf 100644 --- a/actors/evm/tests/calc.rs +++ b/actors/evm/tests/calc.rs @@ -1,7 +1,6 @@ mod asm; -use evm::interpreter::U256; -use fil_actor_evm as evm; +use fil_actors_evm_shared::uints::U256; mod util; diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs index f696b6fe5..5faf31360 100644 --- a/actors/evm/tests/call.rs +++ b/actors/evm/tests/call.rs @@ -8,10 +8,10 @@ use ethers::prelude::builders::ContractCall; use ethers::prelude::*; use ethers::providers::{MockProvider, Provider}; use ethers::types::Bytes; -use evm::interpreter::address::EthAddress; -use evm::interpreter::U256; 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}; @@ -301,7 +301,7 @@ pub fn test_call_output_region() { # this contract truncates return from send to output length # prepare the proxy call -push1 0x00 +push1 0x00 calldataload # size from first word push1 0x00 # offset diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index 15e7cffba..c79bfffc8 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -1,10 +1,8 @@ mod asm; -use evm::interpreter::address::EthAddress; -use evm::interpreter::instructions::lifecycle::{ - Create2Params, CreateParams, EamReturn, CREATE2_METHOD_NUM, CREATE_METHOD_NUM, -}; +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; @@ -30,7 +28,7 @@ mstore # "bytecode" test_create: jumpdest push1 0x10 # in size (16 bytes) - push2 0x0110 # in offset + push2 0x0110 # in offset push1 0x01 # value (attoFil) create %return_stack_word() @@ -55,7 +53,7 @@ fn test_create() { let mut rt = util::construct_and_verify(contract); let fake_eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); - let fake_ret = EamReturn { + let fake_ret = eam::CreateReturn { actor_id: 12345, eth_address: fake_eth_addr, robust_address: Some((&fake_eth_addr).try_into().unwrap()), @@ -64,12 +62,12 @@ fn test_create() { let salt = hex_literal::hex!("0000000000000000000000000000000000000000000000796573206D616E6921"); - let create2_params = Create2Params { + let create2_params = eam::Create2Params { code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), salt, }; - let mut create_params = CreateParams { + let mut create_params = eam::CreateParams { code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), nonce: 1, }; @@ -83,7 +81,7 @@ fn test_create() { rt.expect_send_simple( EAM_ACTOR_ADDR, - CREATE_METHOD_NUM, + eam::CREATE_METHOD_NUM, IpldBlock::serialize_cbor(&create_params).unwrap(), TokenAmount::from_atto(1), IpldBlock::serialize_cbor(&fake_ret).unwrap(), @@ -105,7 +103,7 @@ fn test_create() { rt.expect_send_simple( EAM_ACTOR_ADDR, - CREATE_METHOD_NUM, + eam::CREATE_METHOD_NUM, IpldBlock::serialize_cbor(&create_params).unwrap(), TokenAmount::from_atto(1), IpldBlock::serialize_cbor(&fake_ret).unwrap(), @@ -127,7 +125,7 @@ fn test_create() { rt.expect_send_simple( EAM_ACTOR_ADDR, - CREATE2_METHOD_NUM, + eam::CREATE2_METHOD_NUM, IpldBlock::serialize_cbor(&create2_params).unwrap(), TokenAmount::from_atto(1), IpldBlock::serialize_cbor(&fake_ret).unwrap(), diff --git a/actors/evm/tests/delegate_call.rs b/actors/evm/tests/delegate_call.rs index 4d01822b6..60ba5f6bb 100644 --- a/actors/evm/tests/delegate_call.rs +++ b/actors/evm/tests/delegate_call.rs @@ -1,7 +1,5 @@ -use fil_actor_evm::{ - interpreter::{address::EthAddress, U256}, - DelegateCallParams, Method, -}; +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::{ diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs index e2ef0abe6..529bb2f35 100644 --- a/actors/evm/tests/env.rs +++ b/actors/evm/tests/env.rs @@ -3,8 +3,8 @@ use ethers::{ prelude::{builders::ContractCall, decode_function_data}, providers::{MockProvider, Provider}, }; -use evm::interpreter::address::EthAddress; 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, diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs index a25262cf3..a3eb97ba5 100644 --- a/actors/evm/tests/ext_opcodes.rs +++ b/actors/evm/tests/ext_opcodes.rs @@ -1,9 +1,9 @@ mod asm; use cid::Cid; -use evm::interpreter::U256; 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; diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index 219eac5cc..8ef9cbc83 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -2,8 +2,8 @@ mod asm; mod util; use cid::Cid; -use evm::interpreter::{address::EthAddress, U256}; -use fil_actor_evm as evm; +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}; diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 1e1c2437e..235bacdee 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -1,7 +1,6 @@ mod asm; -use evm::interpreter::{address::EthAddress, U256}; -use fil_actor_evm as evm; +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; use fil_actors_runtime::{ test_utils::{new_bls_addr, MockRuntime}, EAM_ACTOR_ID, diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs index e5ce25aec..9b7a5c0cc 100644 --- a/actors/evm/tests/selfdestruct.rs +++ b/actors/evm/tests/selfdestruct.rs @@ -1,7 +1,7 @@ use fil_actor_evm::{ - interpreter::{address::EthAddress, U256}, 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::{ diff --git a/actors/evm/tests/storage_footprint.rs b/actors/evm/tests/storage_footprint.rs index ce49aa8b3..1d2078a8c 100644 --- a/actors/evm/tests/storage_footprint.rs +++ b/actors/evm/tests/storage_footprint.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use ethers::contract::Lazy; use ethers::prelude::abigen; use ethers::providers::{MockProvider, Provider}; -use fil_actor_evm::interpreter::address::EthAddress; +use fil_actors_evm_shared::address::EthAddress; use fvm_ipld_blockstore::tracking::BSStats as BlockstoreStats; use fvm_shared::address::Address; use fvm_shared::ActorID; diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 4f789720c..a157453e9 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -1,8 +1,8 @@ use cid::Cid; -use evm::interpreter::address::EthAddress; -use evm::interpreter::U256; 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, *}, diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 588aec8fa..8218edd24 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -52,6 +52,7 @@ 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"] } diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs index ccbd5bb5b..df754f133 100644 --- a/test_vm/tests/evm_test.rs +++ b/test_vm/tests/evm_test.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ethers::prelude::abigen; use ethers::providers::Provider; use ethers::{core::types::Address as EthAddress, prelude::builders::ContractCall}; -use fil_actor_evm::interpreter::U256; +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, From b444b4bf55b26a5bc48cda50b45fe6d19489d6a5 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Wed, 1 Feb 2023 13:36:41 -0800 Subject: [PATCH 314/339] EVM: refactor unit test macro params (#1144) * refactor test macro params * Fix macro DSL (#1146) * editorialize on syntax; i got strong opinions when it comes to macro DSLs. * fix unit tests --------- Co-authored-by: vyzo --- .../src/interpreter/instructions/control.rs | 16 +++---- .../evm/src/interpreter/instructions/hash.rs | 4 +- .../src/interpreter/instructions/memory.rs | 24 +++++------ .../evm/src/interpreter/instructions/state.rs | 43 ++++++++++--------- actors/evm/src/interpreter/test_util.rs | 33 +++++++++++++- 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 2f98cd96b..1a70ad177 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -167,7 +167,7 @@ mod tests { #[test] fn test_jump() { evm_unit_test! { - (rt, m) { + (m) { JUMP; JUMPDEST; JUMPDEST; @@ -185,7 +185,7 @@ mod tests { #[test] fn test_jump_err() { evm_unit_test!( - (rt, m) { + (m) { JUMP; // JUMP PUSH4; // PUSH4 -- garbage 0x01; // garbage @@ -204,7 +204,7 @@ mod tests { #[test] fn test_jump_err2() { evm_unit_test! { - (rt, m) { + (m) { JUMP; // JUMP PUSH4; // PUSH4 -- garbage 0x01; // garbage @@ -224,7 +224,7 @@ mod tests { #[test] fn test_jump_err3() { evm_unit_test! { - (rt, m) { + (m) { JUMP; PUSH4; JUMPDEST; @@ -243,7 +243,7 @@ mod tests { #[test] fn test_jumpi_t() { evm_unit_test! { - (rt, m) { + (m) { JUMPI; JUMPDEST; JUMPDEST; @@ -262,7 +262,7 @@ mod tests { #[test] fn test_jumpi_f() { evm_unit_test! { - (rt, m) { + (m) { JUMPI; JUMPDEST; JUMPDEST; @@ -281,7 +281,7 @@ mod tests { #[test] fn test_jumpi_err() { evm_unit_test! { - (rt, m) { + (m) { JUMPI; JUMPDEST; JUMPDEST; @@ -300,7 +300,7 @@ mod tests { #[test] fn test_pc() { evm_unit_test! { - (rt, m) { + (m) { PC; JUMPDEST; } diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs index a1cdbcce2..e6c803725 100644 --- a/actors/evm/src/interpreter/instructions/hash.rs +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -43,7 +43,7 @@ mod test { let v = vec![0xff; len]; let [a, b] = u16::try_from(len).unwrap().to_be_bytes(); evm_unit_test! { - (rt, m) { + (m) { PUSH2; {a}; {b}; @@ -70,7 +70,7 @@ mod test { [([0xfe].as_slice(), BytecodeHash::NATIVE_ACTOR), (&[], BytecodeHash::EMPTY)] { evm_unit_test! { - (rt, m) { + (m) { PUSH1; {input.len() as u8}; PUSH0; diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index c3b21af2f..2773dca69 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -203,7 +203,7 @@ mod tests { #[test] fn test_mload_nothing() { evm_unit_test! { - (rt, m) { + (m) { PUSH0; MLOAD; } @@ -219,7 +219,7 @@ mod tests { #[test] fn test_mload_large_offset() { evm_unit_test! { - (rt, m) { + (m) { PUSH4; // garbage offset 0x01; 0x02; @@ -240,7 +240,7 @@ mod tests { fn test_mload_word() { for sh in 0..32 { evm_unit_test! { - (rt, m) { + (m) { PUSH1; {sh}; MLOAD; @@ -262,7 +262,7 @@ mod tests { fn test_mstore8_basic() { for i in 0..=u8::MAX { evm_unit_test! { - (rt, m) { + (m) { PUSH1; {i}; PUSH0; @@ -281,7 +281,7 @@ mod tests { #[test] fn test_mstore8_overwrite() { evm_unit_test! { - (rt, m) { + (m) { PUSH1; 0x01; PUSH1; @@ -314,7 +314,7 @@ mod tests { let i = 1u16 << sh; let [a, b] = i.to_be_bytes(); evm_unit_test! { - (rt, m) { + (m) { PUSH1; 0xff; PUSH2; @@ -337,7 +337,7 @@ mod tests { #[test] fn test_mstore8_garbage() { evm_unit_test! { - (rt, m) { + (m) { PUSH32; 0xff; 0xff; @@ -388,7 +388,7 @@ mod tests { #[test] fn test_mstore_basic() { evm_unit_test! { - (rt, m) { + (m) { PUSH2; 0xff; 0xfe; @@ -409,7 +409,7 @@ mod tests { #[test] fn test_mstore_overwrite() { evm_unit_test! { - (rt, m) { + (m) { PUSH2; 0xff; 0xfe; @@ -437,7 +437,7 @@ mod tests { #[test] fn test_msize_multiple_mstore8() { evm_unit_test! { - (rt, m) { + (m) { PUSH1; 0xff; PUSH1; @@ -460,7 +460,7 @@ mod tests { #[test] fn test_msize_multiple_mstore() { evm_unit_test! { - (rt, m) { + (m) { PUSH1; 0xff; PUSH1; @@ -484,7 +484,7 @@ mod tests { // Demonstrate that MSIZE depends on memory.len() // Normally this should never happen and we wont panic from it. evm_unit_test! { - (rt, m) { + (m) { MSIZE; } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 191b3929a..819130b1e 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -54,21 +54,22 @@ mod test { (false, U256::MAX), ] { evm_unit_test! { - (rt, m) { + (rt) { + rt.in_call = true; + + 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.system.rt.in_call = true; - - let id_address = 1111; - if has_id { - let addr: EthAddress = addr.into(); - m.system.rt.add_id_address(addr.into(), Address::new_id(id_address)) - } - m.system.rt.actor_balances.insert(id_address, TokenAmount::from_atto(balance)); - m.state.memory.grow(32); m.state.memory[..32].copy_from_slice(&addr.to_bytes()); @@ -98,17 +99,18 @@ mod test { let addr = U256::from(buf); evm_unit_test! { - (rt, m) { + (rt) { + rt.in_call = true; + + // 0xff id address gets balance + rt.actor_balances.insert(id as u64, TokenAmount::from_atto(balance)); + } + (m) { PUSH0; MLOAD; BALANCE; } - m.system.rt.in_call = true; - - // 0xff id address gets balance - m.system.rt.actor_balances.insert(id as u64, TokenAmount::from_atto(balance)); - m.state.memory.grow(32); m.state.memory[..32].copy_from_slice(&addr.to_bytes()); @@ -127,13 +129,14 @@ mod test { for i in 0..256 { let balance = U256::ONE << i; evm_unit_test! { - (rt, m) { + (rt) { + rt.in_call = true; + rt.add_balance(TokenAmount::from(&balance)); + } + (m) { SELFBALANCE; } - m.system.rt.in_call = true; - m.system.rt.add_balance(TokenAmount::from(&balance)); - m.step().expect("execution step failed"); assert_eq!(m.state.stack.len(), 1); diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs index f7e995e89..4d78dbe93 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -13,13 +13,15 @@ macro_rules! evm_instruction { #[macro_export] macro_rules! evm_unit_test { - (($rt:ident, $machine:ident) { $($inst:tt;)* } $($body:tt)*) => { + (($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, Bytes, EthAddress, ExecutionState}; let mut $rt = MockRuntime::default(); + $init + let mut state = ExecutionState::new( EthAddress::from_id(1000), EthAddress::from_id(1000), @@ -41,4 +43,33 @@ macro_rules! evm_unit_test { $($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, Bytes, EthAddress, ExecutionState}; + + let mut rt = MockRuntime::default(); + let mut state = ExecutionState::new( + EthAddress::from_id(1000), + EthAddress::from_id(1000), + TokenAmount::from_atto(0), + Bytes::default(), + ); + + let code = vec![$($crate::evm_instruction!($inst)),*]; + + let mut system = System::new(&mut rt, false); + let bytecode = Bytecode::new(code); + let mut $machine = Machine { + system: &mut system, + state: &mut state, + bytecode: &bytecode, + pc: 0, + output: Output::default(), + }; + + $($body)* + }; } From 0ec7dd865d13cbbd0332afc69478a1b3d8ff1d31 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 1 Feb 2023 17:14:29 -0500 Subject: [PATCH 315/339] Runtime: Drop unused user_get_randomness methods (#1147) --- runtime/src/runtime/fvm.rs | 30 ++----- runtime/src/runtime/mod.rs | 14 ---- runtime/src/test_utils.rs | 164 ++++++++++++++----------------------- test_vm/src/lib.rs | 18 ---- 4 files changed, 69 insertions(+), 157 deletions(-) diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index cde3d7805..d78f529ae 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -225,49 +225,31 @@ where fn get_randomness_from_tickets( &self, personalization: DomainSeparationTag, - rand_epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - self.user_get_randomness_from_chain(personalization as i64, rand_epoch, entropy) - } - - fn get_randomness_from_beacon( - &self, - personalization: DomainSeparationTag, - rand_epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - self.user_get_randomness_from_beacon(personalization as i64, rand_epoch, entropy) - } - - fn user_get_randomness_from_beacon( - &self, - personalization: i64, epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - fvm::rand::get_beacon_randomness(personalization, epoch, entropy).map_err(|e| { + fvm::rand::get_chain_randomness(personalization as i64, epoch, entropy).map_err(|e| { match e { ErrorNumber::LimitExceeded => { actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) } - e => actor_error!(assertion_failed; "get beacon randomness failed with an unexpected error: {}", e), + e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), } }) } - fn user_get_randomness_from_chain( + fn get_randomness_from_beacon( &self, - personalization: i64, + personalization: DomainSeparationTag, epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - fvm::rand::get_chain_randomness(personalization, epoch, entropy).map_err(|e| { + fvm::rand::get_beacon_randomness(personalization as i64, epoch, entropy).map_err(|e| { match e { ErrorNumber::LimitExceeded => { actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) } - e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), + e => actor_error!(assertion_failed; "get beacon randomness failed with an unexpected error: {}", e), } }) } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index ca750c603..738d82d97 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -123,20 +123,6 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; - fn user_get_randomness_from_chain( - &self, - personalization: i64, - epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; - - fn user_get_randomness_from_beacon( - &self, - personalization: i64, - epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError>; - /// Initializes the state object. /// This is only valid when the state has not yet been initialized. /// NOTE: we should also limit this to being invoked during the constructor method diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 9796750f3..d72b6fcab 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -207,8 +207,8 @@ pub struct Expectations { pub expect_verify_post: Option, pub expect_compute_unsealed_sector_cid: VecDeque, pub expect_verify_consensus_fault: Option, - pub expect_get_randomness_from_chain: VecDeque, - pub expect_get_randomness_from_beacon: VecDeque, + pub expect_get_randomness_tickets: VecDeque, + pub expect_get_randomness_beacon: VecDeque, pub expect_batch_verify_seals: Option, pub expect_aggregate_verify_seals: Option, pub expect_replica_verify: Option, @@ -282,14 +282,14 @@ impl Expectations { this.expect_verify_consensus_fault ); assert!( - this.expect_get_randomness_from_chain.is_empty(), - "expect_get_randomness_from_chain {:?}, not received", - this.expect_get_randomness_from_chain + this.expect_get_randomness_tickets.is_empty(), + "expect_get_randomness_tickets {:?}, not received", + this.expect_get_randomness_tickets ); assert!( - this.expect_get_randomness_from_beacon.is_empty(), - "expect_get_randomness_from_beacon {:?}, not received", - this.expect_get_randomness_from_beacon + this.expect_get_randomness_beacon.is_empty(), + "expect_get_randomness_beacon {:?}, not received", + this.expect_get_randomness_beacon ); assert!( this.expect_batch_verify_seals.is_none(), @@ -433,7 +433,7 @@ pub struct ExpectComputeUnsealedSectorCid { #[derive(Clone, Debug)] pub struct ExpectRandomness { - tag: i64, + tag: DomainSeparationTag, epoch: ChainEpoch, entropy: Vec, out: [u8; RANDOMNESS_LENGTH], @@ -770,7 +770,8 @@ impl MockRuntime { entropy: Vec, out: [u8; RANDOMNESS_LENGTH], ) { - self.expect_user_get_randomness_from_chain(tag as i64, epoch, entropy, out) + let a = ExpectRandomness { tag, epoch, entropy, out }; + self.expectations.borrow_mut().expect_get_randomness_tickets.push_back(a); } #[allow(dead_code)] @@ -781,29 +782,8 @@ impl MockRuntime { entropy: Vec, out: [u8; RANDOMNESS_LENGTH], ) { - self.expect_user_get_randomness_from_beacon(tag as i64, epoch, entropy, out) - } - - pub fn expect_user_get_randomness_from_beacon( - &mut self, - personalization: i64, - epoch: ChainEpoch, - entropy: Vec, - out: [u8; RANDOMNESS_LENGTH], - ) { - let a = ExpectRandomness { tag: personalization, epoch, entropy, out }; - self.expectations.borrow_mut().expect_get_randomness_from_beacon.push_back(a); - } - - pub fn expect_user_get_randomness_from_chain( - &mut self, - personalization: i64, - epoch: ChainEpoch, - entropy: Vec, - out: [u8; RANDOMNESS_LENGTH], - ) { - let a = ExpectRandomness { tag: personalization, epoch, entropy, out }; - self.expectations.borrow_mut().expect_get_randomness_from_chain.push_back(a); + let a = ExpectRandomness { tag, epoch, entropy, out }; + self.expectations.borrow_mut().expect_get_randomness_beacon.push_back(a); } #[allow(dead_code)] @@ -1068,7 +1048,31 @@ impl Runtime for MockRuntime { epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - self.user_get_randomness_from_chain(tag as i64, epoch, entropy) + let expected = self + .expectations + .borrow_mut() + .expect_get_randomness_tickets + .pop_front() + .expect("unexpected call to get_randomness_from_tickets"); + + assert!(epoch <= self.epoch, "attempt to get randomness from future"); + assert_eq!( + expected.tag, tag, + "unexpected domain separation tag, expected: {:?}, actual: {:?}", + expected.tag, tag + ); + assert_eq!( + expected.epoch, epoch, + "unexpected epoch, expected: {:?}, actual: {:?}", + expected.epoch, epoch + ); + assert_eq!( + expected.entropy, *entropy, + "unexpected entroy, expected {:?}, actual: {:?}", + expected.entropy, entropy + ); + + Ok(expected.out) } fn get_randomness_from_beacon( @@ -1077,7 +1081,31 @@ impl Runtime for MockRuntime { epoch: ChainEpoch, entropy: &[u8], ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - self.user_get_randomness_from_beacon(tag as i64, epoch, entropy) + let expected = self + .expectations + .borrow_mut() + .expect_get_randomness_beacon + .pop_front() + .expect("unexpected call to get_randomness_from_beacon"); + + assert!(epoch <= self.epoch, "attempt to get randomness from future"); + assert_eq!( + expected.tag, tag, + "unexpected domain separation tag, expected: {:?}, actual: {:?}", + expected.tag, tag + ); + assert_eq!( + expected.epoch, epoch, + "unexpected epoch, expected: {:?}, actual: {:?}", + expected.epoch, epoch + ); + assert_eq!( + expected.entropy, *entropy, + "unexpected entroy, expected {:?}, actual: {:?}", + expected.entropy, entropy + ); + + Ok(expected.out) } fn create(&mut self, obj: &T) -> Result<(), ActorError> { @@ -1264,72 +1292,6 @@ impl Runtime for MockRuntime { self.tipset_cids.get((self.epoch - epoch) as usize).copied() } - fn user_get_randomness_from_chain( - &self, - personalization: i64, - epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - let expected = self - .expectations - .borrow_mut() - .expect_get_randomness_from_chain - .pop_front() - .expect("unexpected call to get_chain_randomness"); - - assert!(epoch <= self.epoch, "attempt to get randomness from future"); - assert_eq!( - expected.tag, personalization, - "unexpected domain personalization tag, expected: {:?}, actual: {:?}", - expected.tag, personalization - ); - assert_eq!( - expected.epoch, epoch, - "unexpected epoch, expected: {:?}, actual: {:?}", - expected.epoch, epoch - ); - assert_eq!( - expected.entropy, *entropy, - "unexpected entroy, expected {:?}, actual: {:?}", - expected.entropy, entropy - ); - - Ok(expected.out) - } - - fn user_get_randomness_from_beacon( - &self, - personalization: i64, - epoch: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - let expected = self - .expectations - .borrow_mut() - .expect_get_randomness_from_beacon - .pop_front() - .expect("unexpected call to get_beacon_randomness"); - - assert!(epoch <= self.epoch, "attempt to get randomness from future"); - assert_eq!( - expected.tag, personalization, - "unexpected domain personalization tag, expected: {:?}, actual: {:?}", - expected.tag, personalization - ); - assert_eq!( - expected.epoch, epoch, - "unexpected epoch, expected: {:?}, actual: {:?}", - expected.epoch, epoch - ); - assert_eq!( - expected.entropy, *entropy, - "unexpected entroy, expected {:?}, actual: {:?}", - expected.entropy, entropy - ); - - Ok(expected.out) - } - fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { let expected = self .expectations diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 0ff82e16a..772cb8bd3 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1054,24 +1054,6 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { Ok(TEST_VM_RAND_ARRAY) } - fn user_get_randomness_from_beacon( - &self, - _personalization: i64, - _epoch: ChainEpoch, - _entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - Ok(TEST_VM_RAND_ARRAY) - } - - fn user_get_randomness_from_chain( - &self, - _personalization: i64, - _epoch: ChainEpoch, - _entropy: &[u8], - ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - Ok(TEST_VM_RAND_ARRAY) - } - fn get_state_root(&self) -> Result { Ok(self.v.get_actor(self.to()).unwrap().head) } From 553d2d3c3ce52473cf1cf08f24fb2742e14617e8 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 1 Feb 2023 17:14:59 -0500 Subject: [PATCH 316/339] remove duplicated authenticate_message_test (#1130) --- .../account_authenticate_message_test.rs | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 test_vm/tests/account_authenticate_message_test.rs 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, - ); -} From d84bbf132c183937b75842614329a27484d69233 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 1 Feb 2023 16:01:00 -0800 Subject: [PATCH 317/339] EVM: use shared evm create in the EAM (#1148) --- Cargo.lock | 1 + actors/eam/Cargo.toml | 1 + actors/eam/src/lib.rs | 58 ++++++-------------------------- actors/eam/tests/create.rs | 15 ++++----- actors/evm/shared/src/address.rs | 25 +++++++++++++- 5 files changed, 43 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b6857ac9..3e808160c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1834,6 +1834,7 @@ dependencies = [ "anyhow", "cid", "fil_actor_evm", + "fil_actors_evm_shared", "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index b475c43e8..2fe4bd001 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -28,6 +28,7 @@ fvm_shared = { version = "3.0.0-alpha.17", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" +fil_actors_evm_shared = { version = "10.0.0-alpha.1", path = "../evm/shared" } [dev-dependencies] fil_actor_evm = { version = "10.0.0-alpha.1", path = "../evm"} diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index b751bd1d5..d5a9052dd 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -1,5 +1,6 @@ use std::iter; +use fil_actors_evm_shared::address::EthAddress; use num_traits::Zero; use ext::{ @@ -68,38 +69,6 @@ pub fn compute_address_create_external(rt: &impl Runtime, from: &EthAddress) -> compute_address_create(rt, from, rt.message().nonce()) } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, PartialEq, Eq)] -pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); - -impl EthAddress { - /// 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] - 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] - fn is_id(&self) -> bool { - self.0[0] == 0xff && self.0[1..12].iter().all(|&i| i == 0) - } - - #[inline] - fn is_null(&self) -> bool { - self.0 == [0; 20] - } - - /// Returns true if the EthAddress is "reserved" (cannot be assigned by the EAM). - #[inline] - fn is_reserved(&self) -> bool { - self.is_precompile() || self.is_id() || self.is_null() - } -} - #[derive(Serialize_tuple, Deserialize_tuple)] pub struct CreateParams { #[serde(with = "strict_bytes")] @@ -153,6 +122,10 @@ fn hash_20(rt: &impl Runtime, data: &[u8]) -> [u8; 20] { rt.hash(SupportedHashes::Keccak256, data)[12..32].try_into().unwrap() } +fn can_assign_address(addr: &EthAddress) -> bool { + !addr.is_precompile() && !addr.is_id() && !addr.is_null() +} + fn create_actor( rt: &mut impl Runtime, creator: EthAddress, @@ -162,7 +135,7 @@ fn create_actor( // If the new address is reserved (an ID address, or a precompile), reject it. An attacker would // need to brute-force 96bits of a cryptographic hash and convince the target to use an attacker // chosen salt, but we might as well be safe. - if new_addr.is_reserved() { + if !can_assign_address(&new_addr) { return Err(ActorError::forbidden("cannot create address with a reserved prefix".into())); } @@ -256,11 +229,7 @@ fn resolve_caller_external(rt: &mut impl Runtime) -> Result<(EthAddress, EthAddr let robust_addr: Address = deserialize_block(result.return_data)?; let robust_eth_bytes = hash_20(rt, &robust_addr.to_bytes()); - let mut id_bytes = [0u8; 20]; - id_bytes[0] = 0xff; - id_bytes[12..].copy_from_slice(&caller_id.to_be_bytes()); - - Ok((EthAddress(id_bytes), EthAddress(robust_eth_bytes))) + Ok((EthAddress::from_id(caller_id), EthAddress(robust_eth_bytes))) } Some(Type::EthAccount) => { let addr = resolve_eth_address(rt, caller_id)?; @@ -360,22 +329,17 @@ mod test { #[test] fn test_create_actor_rejects() { let mut rt = MockRuntime::default(); - let mut creator = EthAddress([0; 20]); - creator.0[0] = 0xff; - creator.0[19] = 0x1; + let creator = EthAddress::from_id(1); // Reject ID. - let mut new_addr = EthAddress([0; 20]); - new_addr.0[0] = 0xff; - new_addr.0[18] = 0x20; - new_addr.0[19] = 0x20; + let new_addr = EthAddress::from_id(8224); assert_eq!( ExitCode::USR_FORBIDDEN, create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() ); // Reject EVM Precompile. - let mut new_addr = EthAddress([0; 20]); + let mut new_addr = EthAddress::null(); new_addr.0[19] = 0x20; assert_eq!( ExitCode::USR_FORBIDDEN, @@ -390,7 +354,7 @@ mod test { ); // Reject Null. - let new_addr = EthAddress([0; 20]); + let new_addr = EthAddress::null(); assert_eq!( ExitCode::USR_FORBIDDEN, create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() diff --git a/actors/eam/tests/create.rs b/actors/eam/tests/create.rs index 06ec9a2b6..07d61ed7b 100644 --- a/actors/eam/tests/create.rs +++ b/actors/eam/tests/create.rs @@ -1,10 +1,9 @@ use eam::ext::evm::RESURRECT_METHOD; use eam::ext::init::{Exec4Params, Exec4Return, EXEC4_METHOD}; -use eam::{ - compute_address_create, Create2Params, CreateParams, EthAddress, EvmConstructorParams, Return, -}; +use eam::{compute_address_create, Create2Params, CreateParams, EvmConstructorParams, Return}; use fil_actor_eam as eam; use fil_actor_eam::CreateExternalParams; +use fil_actors_evm_shared::address::EthAddress; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::Primitives; use fil_actors_runtime::test_utils::{ @@ -23,7 +22,7 @@ fn call_create_new() { let mut rt = construct_and_verify(); let id_addr = Address::new_id(110); - let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); rt.set_delegated_address(id_addr.id().unwrap(), f4_eth_addr); @@ -84,8 +83,7 @@ fn call_create_external_over_placeholder() { let mut rt = construct_and_verify(); let caller_id_addr = Address::new_id(110); - let caller_eth_addr = - eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let caller_eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); let caller_f4_eth_addr = Address::new_delegated(10, &caller_eth_addr.0).unwrap(); rt.set_delegated_address(caller_id_addr.id().unwrap(), caller_f4_eth_addr); @@ -152,8 +150,7 @@ fn call_resurrect() { let mut rt = construct_and_verify(); let caller_id_addr = Address::new_id(110); - let caller_eth_addr = - eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let caller_eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); let caller_f4_eth_addr = Address::new_delegated(10, &caller_eth_addr.0).unwrap(); rt.set_delegated_address(caller_id_addr.id().unwrap(), caller_f4_eth_addr); @@ -204,7 +201,7 @@ fn call_create2() { let mut rt = construct_and_verify(); let id_addr = Address::new_id(110); - let eth_addr = eam::EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); let f4_eth_addr = Address::new_delegated(10, ð_addr.0).unwrap(); rt.set_delegated_address(id_addr.id().unwrap(), f4_eth_addr); diff --git a/actors/evm/shared/src/address.rs b/actors/evm/shared/src/address.rs index 305038cfc..5cff4e6c5 100644 --- a/actors/evm/shared/src/address.rs +++ b/actors/evm/shared/src/address.rs @@ -44,6 +44,11 @@ impl From<&EthAddress> for Address { } impl EthAddress { + /// Returns a "null" address. + pub const fn null() -> Self { + Self([0u8; 20]) + } + /// Returns an EVM-form ID address from actor ID. pub fn from_id(id: u64) -> EthAddress { let mut bytes = [0u8; 20]; @@ -61,21 +66,39 @@ impl EthAddress { /// 0 1-11 12 /// 0xff \[0x00...] [id address...] pub fn as_id(&self) -> Option { - if (self.0[0] != 0xff) || !self.0[1..12].iter().all(|&byte| byte == 0) { + 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 { From 93c14679548d1cf1f66a2b4290724f57ad5e149c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 1 Feb 2023 16:49:13 -0800 Subject: [PATCH 318/339] EVM: apply EIP150 to CREATE/CREATE2 (#1149) fixes #1499 --- .../src/interpreter/instructions/lifecycle.rs | 6 ++++- actors/evm/tests/create.rs | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 2fa8883cf..b2fc45623 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -96,8 +96,12 @@ fn create_init( 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, None, SendFlags::default()); + let ret = + system.send(&EAM_ACTOR_ADDR, method, params, value, Some(gas_limit), SendFlags::default()); // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 // Normally EVM will do some checks here to ensure that a contract has the capability diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs index c79bfffc8..35b92f50e 100644 --- a/actors/evm/tests/create.rs +++ b/actors/evm/tests/create.rs @@ -7,6 +7,7 @@ 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; @@ -49,6 +50,9 @@ test_create2: #[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); @@ -79,13 +83,17 @@ fn test_create() { { rt.add_balance(TokenAmount::from_atto(1)); - rt.expect_send_simple( + 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); @@ -101,13 +109,17 @@ fn test_create() { rt.add_balance(TokenAmount::from_atto(1)); - rt.expect_send_simple( + 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); @@ -123,13 +135,17 @@ fn test_create() { { rt.add_balance(TokenAmount::from_atto(1)); - rt.expect_send_simple( + 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); From 5eb65d5ad4e296440f3ae2151de82224e4685db1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 2 Feb 2023 19:04:12 +0200 Subject: [PATCH 319/339] EVM: Unit tests for CALL/RETURN data opcodes (#1128) * test callvalue * test calldataload * test calldatasize * test calldatacopy * test returndata{size,copy} * rustfmt * update for macro DSL changes * test edge cases * add some comments for tests * clippy, chief of waste; he who wastes countless cpu and human compute hours. --- .../evm/src/interpreter/instructions/call.rs | 131 ++++++++++++++++++ .../src/interpreter/instructions/context.rs | 20 +++ .../src/interpreter/instructions/control.rs | 88 +++++++++++- 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 1b4876266..d264f9af1 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -337,3 +337,134 @@ fn effective_gas_limit(system: &System, gas: U256) -> u64 { let gas = gas.to_u64_saturating(); std::cmp::min(gas, gas_rsvp) } + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use fil_actors_evm_shared::uints::U256; + + #[test] + fn test_calldataload() { + // happy path + evm_unit_test! { + (m) { + CALLDATALOAD; + } + m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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].into(); + 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).into(); + 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].into(); + 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].into(); + 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.as_ref(), &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].into(); + 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.as_ref(), &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].into(); + 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.as_ref(), &expected); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 1004e6154..816731ef1 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -146,3 +146,23 @@ pub fn base_fee( ) -> Result { Ok(U256::from(&system.rt.base_fee())) } + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use fil_actors_evm_shared::uints::U256; + + #[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)); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 1a70ad177..20ebb5cfe 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -162,7 +162,7 @@ mod tests { use fil_actors_evm_shared::uints::U256; use crate::evm_unit_test; - use crate::EVM_CONTRACT_BAD_JUMPDEST; + use crate::{EVM_CONTRACT_BAD_JUMPDEST, EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS}; #[test] fn test_jump() { @@ -311,4 +311,90 @@ mod tests { 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].into(); + 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].into(); + 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.as_ref(), &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].into(); + 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].into(); + 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].into(); + 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); + }; + } } From 6f2c8e5b91fa82be31f41edebef9604ef87297cc Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 2 Feb 2023 16:04:56 -0500 Subject: [PATCH 320/339] Refactor: Runtime: tipset_cid should return a Result (#1151) --- actors/evm/src/interpreter/instructions/context.rs | 2 +- runtime/src/runtime/fvm.rs | 5 +++-- runtime/src/runtime/mod.rs | 5 +++-- runtime/src/test_utils.rs | 8 +++++--- test_vm/src/lib.rs | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 816731ef1..d86a3af94 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -26,7 +26,7 @@ pub fn blockhash( let curr_epoch = system.rt.curr_epoch(); height >= curr_epoch - 256 && height < curr_epoch }) - .and_then(|height| system.rt.tipset_cid(height)) + .and_then(|height| system.rt.tipset_cid(height).ok()) .map(|cid| { let mut hash = cid.hash().digest(); if hash.len() > EVM_WORD_SIZE { diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index d78f529ae..ef8b592d8 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -361,8 +361,9 @@ where fvm::network::tipset_timestamp() } - fn tipset_cid(&self, epoch: i64) -> Option { - fvm::network::tipset_cid(epoch).ok() + fn tipset_cid(&self, epoch: i64) -> Result { + fvm::network::tipset_cid(epoch) + .map_err(|_| actor_error!(illegal_argument; "invalid epoch to query tipset_cid")) } fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 738d82d97..085b028f5 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -243,8 +243,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The current tipset's timestamp, as UNIX seconds fn tipset_timestamp(&self) -> u64; - /// The hash of on of the last 256 blocks - fn tipset_cid(&self, epoch: i64) -> Option; + /// The CID of the tipset at the specified epoch. + /// 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>; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index d72b6fcab..66c8aa3ac 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1285,11 +1285,13 @@ impl Runtime for MockRuntime { self.tipset_timestamp } - fn tipset_cid(&self, epoch: i64) -> Option { + fn tipset_cid(&self, epoch: i64) -> Result { if epoch > self.epoch || epoch < 0 { - return None; + return Err( + actor_error!(illegal_argument; "invalid epoch to fetch tipset_cid {}", epoch), + ); } - self.tipset_cids.get((self.epoch - epoch) as usize).copied() + Ok(*self.tipset_cids.get((self.epoch - epoch) as usize).unwrap()) } fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 772cb8bd3..2c13f6b2f 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1142,8 +1142,8 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { 0 } - fn tipset_cid(&self, _epoch: i64) -> Option { - Some(Cid::new_v1(IPLD_RAW, Multihash::wrap(0, b"faketipset").unwrap())) + fn tipset_cid(&self, _epoch: i64) -> Result { + Ok(Cid::new_v1(IPLD_RAW, Multihash::wrap(0, b"faketipset").unwrap())) } // TODO No support for events yet. From 7db5cff9e0bac1b3cfd07cca5f9d7bfd32301691 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 2 Feb 2023 15:41:59 -0800 Subject: [PATCH 321/339] EVM: fix mockruntime tipset-cid (#1154) fix mockruntime tipset-cid, add unit test for opcode --- .../src/interpreter/instructions/context.rs | 52 +++++++++++++++++++ actors/evm/tests/misc.rs | 6 +-- runtime/src/test_utils.rs | 9 +++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index d86a3af94..a06d5687d 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -150,7 +150,59 @@ pub fn base_fee( #[cfg(test)] mod tests { use crate::evm_unit_test; + use cid::Cid; use fil_actors_evm_shared::uints::U256; + use fvm_ipld_encoding::{DAG_CBOR, IPLD_RAW}; + + #[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() { diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs index 8ef9cbc83..76e3854ee 100644 --- a/actors/evm/tests/misc.rs +++ b/actors/evm/tests/misc.rs @@ -50,7 +50,7 @@ return let mut rt = util::construct_and_verify(contract); - rt.tipset_cids = (0..900) + rt.tipset_cids = (0..(0xffff + 512)) .map(|i| { Cid::new_v1(DAG_CBOR, Multihash::wrap(0, format!("block-{:026}", i).as_ref()).unwrap()) }) @@ -60,14 +60,14 @@ return let result = util::invoke_contract(&mut rt, &[]); assert_eq!( String::from_utf8_lossy(&result.to_vec()), - String::from_utf8_lossy(rt.tipset_cids[2].hash().digest()) + 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[256].hash().digest()) + String::from_utf8_lossy(rt.tipset_cids[0xffff].hash().digest()) ); rt.epoch = 0xffff; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 3bf89a4eb..58d111a1a 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1285,12 +1285,17 @@ 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> { From 6540db3fc3580ac55105dc64cbf791cd02a7ef59 Mon Sep 17 00:00:00 2001 From: Melanie Riise Date: Thu, 2 Feb 2023 21:24:40 -0800 Subject: [PATCH 322/339] EVM: Test context opcodes (#1155) * add tests for first half of context * remove blockhash from this PR, fmt * add remaining context tests --- .../src/interpreter/instructions/context.rs | 116 +++++++++++++++++- .../evm/src/interpreter/instructions/state.rs | 5 - actors/evm/src/interpreter/test_util.rs | 1 + 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index a06d5687d..077f86b78 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -75,7 +75,7 @@ pub fn coinbase( _state: &mut ExecutionState, _system: &System, ) -> Result { - // TODO do we want to return the zero ID address, or just a plain 0? + // 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()) } @@ -106,6 +106,7 @@ 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())) } @@ -217,4 +218,117 @@ mod tests { 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)); + }; + } + } } diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs index 819130b1e..7571ad345 100644 --- a/actors/evm/src/interpreter/instructions/state.rs +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -55,8 +55,6 @@ mod test { ] { evm_unit_test! { (rt) { - rt.in_call = true; - let id_address = 1111; if has_id { let addr: EthAddress = addr.into(); @@ -100,8 +98,6 @@ mod test { evm_unit_test! { (rt) { - rt.in_call = true; - // 0xff id address gets balance rt.actor_balances.insert(id as u64, TokenAmount::from_atto(balance)); } @@ -130,7 +126,6 @@ mod test { let balance = U256::ONE << i; evm_unit_test! { (rt) { - rt.in_call = true; rt.add_balance(TokenAmount::from(&balance)); } (m) { diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs index 4d78dbe93..2713b24b8 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -20,6 +20,7 @@ macro_rules! evm_unit_test { use $crate::{Bytecode, Bytes, EthAddress, ExecutionState}; let mut $rt = MockRuntime::default(); + $rt.in_call = true; $init let mut state = ExecutionState::new( From d09854992c4b163eddf10297d6124912a671294f Mon Sep 17 00:00:00 2001 From: raulk Date: Sun, 5 Feb 2023 02:04:58 +0000 Subject: [PATCH 323/339] EVM: fix zero curve encoding (#1164) * EVM: reproduce ecMul precompile zero-scalar bug. * EVM: fix encoding of zero --------- Co-authored-by: Steven Allen --- actors/evm/src/interpreter/precompiles/evm.rs | 78 +++++++------------ 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index a20bcf102..79f15105e 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -144,15 +144,13 @@ pub(super) fn modexp( Ok(output) } -pub(super) fn curve_to_vec(curve: G1) -> Result, PrecompileError> { - AffineG1::from_jacobian(curve) - .map(|product| { - let mut output = vec![0; 64]; - product.x().to_big_endian(&mut output[0..32]).unwrap(); - product.y().to_big_endian(&mut output[32..64]).unwrap(); - output - }) - .ok_or(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) +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 @@ -165,7 +163,7 @@ pub(super) fn ec_add( let point1: G1 = input_params.read_value()?; let point2: G1 = input_params.read_value()?; - curve_to_vec(point1 + point2) + Ok(curve_to_vec(point1 + point2)) } /// multiply a point on an elliptic curve by a scalar value @@ -178,7 +176,7 @@ pub(super) fn ec_mul( let point: G1 = input_params.read_value()?; let scalar: Fr = input_params.read_value()?; - curve_to_vec(point * scalar) + Ok(curve_to_vec(point * scalar)) } /// pairs multple groups of twisted bn curves @@ -467,27 +465,14 @@ mod tests { let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); // zero sum test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = ec_add(&mut system, &input, PrecompileContext::default()); - assert!(matches!( - res, - Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) - )); + 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 + // no input test (auto zero extend) let input = []; - let res = ec_add(&mut system, &input, PrecompileContext::default()); - assert!(matches!( - res, - Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) - )); + 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( @@ -526,27 +511,10 @@ mod tests { let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); assert_eq!(res, expected); - // out of gas test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0200000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = ec_mul(&mut system, &input, PrecompileContext::default()); - assert!(matches!( - res, - Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) - )); - // no input test let input = [0u8; 0]; - let res = ec_mul(&mut system, &input, PrecompileContext::default()); - assert!(matches!( - res, - Err(PrecompileError::EcErr(substrate_bn::CurveError::InvalidEncoding)) - )); + 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( @@ -561,6 +529,18 @@ mod tests { 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] From ac264944e5ce6d21db50a7345a0291da2847df39 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 5 Feb 2023 04:34:20 -0800 Subject: [PATCH 324/339] EVM: add precompile conformance tests (#1165) --- Cargo.lock | 3 + README.md | 3 + actors/evm/Cargo.toml | 3 +- actors/evm/precompile-testdata/LICENSE | 165 ++++++++++++++++++ actors/evm/precompile-testdata/blake2F.json | 37 ++++ actors/evm/precompile-testdata/bn256Add.json | 114 ++++++++++++ .../evm/precompile-testdata/bn256Pairing.json | 100 +++++++++++ .../precompile-testdata/bn256ScalarMul.json | 128 ++++++++++++++ actors/evm/precompile-testdata/ecRecover.json | 37 ++++ .../evm/precompile-testdata/fail-blake2f.json | 22 +++ actors/evm/precompile-testdata/modexp.json | 121 +++++++++++++ .../precompile-testdata/modexp_eip2565.json | 121 +++++++++++++ actors/evm/src/interpreter/precompiles/evm.rs | 78 ++++++++- actors/evm/src/reader.rs | 3 +- 14 files changed, 929 insertions(+), 6 deletions(-) create mode 100644 actors/evm/precompile-testdata/LICENSE create mode 100644 actors/evm/precompile-testdata/blake2F.json create mode 100644 actors/evm/precompile-testdata/bn256Add.json create mode 100644 actors/evm/precompile-testdata/bn256Pairing.json create mode 100644 actors/evm/precompile-testdata/bn256ScalarMul.json create mode 100644 actors/evm/precompile-testdata/ecRecover.json create mode 100644 actors/evm/precompile-testdata/fail-blake2f.json create mode 100644 actors/evm/precompile-testdata/modexp.json create mode 100644 actors/evm/precompile-testdata/modexp_eip2565.json diff --git a/Cargo.lock b/Cargo.lock index 2fe411a01..ae0b229e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2812,6 +2812,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" 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/evm/Cargo.toml b/actors/evm/Cargo.toml index 59349029f..ca04342d2 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Protocol Labs", "Filecoin Core Devs"] edition = "2021" 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 @@ -35,7 +36,7 @@ derive_more = "0.99" fixed-hash = { version = "0.7.0", default-features = false } impl-serde = { version = "0.3.2", default-features = false } arrayvec = { version = "0.7.2", features = ["serde"] } -hex = "0.4.3" +hex = { version = "0.4.3", features = ["serde"] } hex-literal = "0.3.4" substrate-bn = { version = "0.6.0", default-features = false } near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } 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/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs index 79f15105e..1fbfab880 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -339,7 +339,7 @@ mod tests { ); // wrong signature let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); - assert_eq!(res, Vec::new()); + assert!(res.is_empty()); let input = &hex!( "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) @@ -350,7 +350,7 @@ mod tests { ); // invalid recovery byte let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); - assert_eq!(res, Vec::new()); + assert!(res.is_empty()); } #[test] @@ -431,15 +431,21 @@ mod tests { ); // no mod is invalid let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); - assert_eq!(res, Vec::new()); + 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, PrecompileContext, PrecompileError}, + precompiles::{ + ec_add, ec_mul, ec_pairing, + evm::{blake2f, ec_recover, modexp}, + PrecompileContext, PrecompileError, PrecompileFn, + }, System, }; @@ -490,6 +496,70 @@ mod tests { )); } + 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(); diff --git a/actors/evm/src/reader.rs b/actors/evm/src/reader.rs index ffb52f91a..b7ca1a246 100644 --- a/actors/evm/src/reader.rs +++ b/actors/evm/src/reader.rs @@ -206,8 +206,9 @@ mod test { #[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>(), []); + 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]); } From c5cbd5bc25b26958b83d43ecabed782132e43773 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 6 Feb 2023 18:37:18 +0200 Subject: [PATCH 325/339] EVM: more unit tests (#1163) * test sload * test sstore * make lint happy in macro expansion * test log0 * test log1 * test log2,3,4 --- .../src/interpreter/instructions/log_event.rs | 244 ++++++++++++++++++ .../src/interpreter/instructions/storage.rs | 54 ++++ actors/evm/src/interpreter/test_util.rs | 2 + 3 files changed, 300 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs index 90e39502c..aa20aa235 100644 --- a/actors/evm/src/interpreter/instructions/log_event.rs +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -62,3 +62,247 @@ pub fn log( Ok(()) } + +#[cfg(test)] +mod tests { + use fil_actors_evm_shared::uints::U256; + use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; + 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(), + value: RawBytes::serialize(BytesSer(&data)).unwrap(), + }]) + ); + } + (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(), + value: to_vec(&t1).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + value: RawBytes::serialize(BytesSer(&data)).unwrap(), + } + ]) + ); + } + (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(), + value: to_vec(&t1).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + value: to_vec(&t2).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + value: RawBytes::serialize(BytesSer(&data)).unwrap(), + } + ]) + ); + } + (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(), + value: to_vec(&t1).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + value: to_vec(&t2).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[2].to_owned(), + value: to_vec(&t3).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + value: RawBytes::serialize(BytesSer(&data)).unwrap(), + } + ]) + ); + } + (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(), + value: to_vec(&t1).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + value: to_vec(&t2).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[2].to_owned(), + value: to_vec(&t3).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[3].to_owned(), + value: to_vec(&t4).unwrap().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + value: RawBytes::serialize(BytesSer(&data)).unwrap(), + } + ]) + ); + } + + (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/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs index d46ed5146..06e66cc8a 100644 --- a/actors/evm/src/interpreter/instructions/storage.rs +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -29,3 +29,57 @@ pub fn sstore( 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/test_util.rs b/actors/evm/src/interpreter/test_util.rs index 2713b24b8..a21e44c9b 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -34,6 +34,7 @@ macro_rules! evm_unit_test { 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, @@ -63,6 +64,7 @@ macro_rules! evm_unit_test { 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, From 5b90c2b8b99b52786d039c4a4728ab5f520f63e9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Feb 2023 07:28:50 -0800 Subject: [PATCH 326/339] feat: update fvm and switch to the new event structure (#1171) --- Cargo.lock | 76 +++++++++---------- actors/account/Cargo.toml | 4 +- actors/cron/Cargo.toml | 2 +- actors/datacap/Cargo.toml | 6 +- actors/eam/Cargo.toml | 2 +- actors/ethaccount/Cargo.toml | 4 +- actors/evm/Cargo.toml | 2 +- actors/evm/shared/Cargo.toml | 2 +- .../src/interpreter/instructions/log_event.rs | 55 +++++++++----- actors/evm/tests/events.rs | 29 ++++--- actors/init/Cargo.toml | 2 +- actors/market/Cargo.toml | 4 +- actors/miner/Cargo.toml | 2 +- actors/multisig/Cargo.toml | 4 +- actors/paych/Cargo.toml | 2 +- actors/power/Cargo.toml | 2 +- actors/reward/Cargo.toml | 2 +- actors/system/Cargo.toml | 2 +- actors/verifreg/Cargo.toml | 6 +- runtime/Cargo.toml | 4 +- state/Cargo.toml | 4 +- test_vm/Cargo.toml | 6 +- 22 files changed, 119 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae0b229e3..dc46f9c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1768,7 +1768,7 @@ dependencies = [ "fvm_actor_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "num-derive", "num-traits", "serde", @@ -1800,7 +1800,7 @@ dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "log", "num-derive", "num-traits", @@ -1819,7 +1819,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "lazy_static", "log", "num-derive", @@ -1838,7 +1838,7 @@ dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "hex-literal", "log", "multihash", @@ -1857,7 +1857,7 @@ dependencies = [ "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "hex-literal", "num-derive", "num-traits", @@ -1882,7 +1882,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_kamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "hex", "hex-literal", "impl-serde 0.3.2", @@ -1914,7 +1914,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "log", "num-derive", "num-traits", @@ -1938,7 +1938,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "integer-encoding", "itertools", "libipld-core 0.13.1", @@ -1968,7 +1968,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "itertools", "lazy_static", "log", @@ -1992,7 +1992,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "indexmap", "integer-encoding", "lazy_static", @@ -2013,7 +2013,7 @@ dependencies = [ "fvm_ipld_amt", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "num-derive", "num-traits", "serde", @@ -2035,7 +2035,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "indexmap", "integer-encoding", "lazy_static", @@ -2052,7 +2052,7 @@ dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "lazy_static", "log", "num", @@ -2070,7 +2070,7 @@ dependencies = [ "fil_actors_runtime", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "num-derive", "num-traits", "serde", @@ -2089,7 +2089,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "lazy_static", "log", "num-derive", @@ -2103,7 +2103,7 @@ version = "10.0.0-alpha.1" dependencies = [ "fil_actors_runtime", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "hex", "serde", "uint", @@ -2124,8 +2124,8 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_sdk 3.0.0-alpha.22", - "fvm_shared 3.0.0-alpha.17", + "fvm_sdk 3.0.0-alpha.24", + "fvm_shared 3.0.0-alpha.20", "hex", "itertools", "lazy_static", @@ -2195,7 +2195,7 @@ dependencies = [ "frc46_token", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "num-derive", "num-traits", "serde", @@ -2252,15 +2252,15 @@ dependencies = [ [[package]] name = "frc42_dispatch" -version = "3.0.1-alpha.1" +version = "3.0.1-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4831f9a09043a3796e88dd6683f5c1ded58bfbb6dbde95708ee675cf3c6b157b" +checksum = "b9d7a133ea05356dbb3115ddb6bb302a9cb82efcc7561602ed687c277d96e55b" dependencies = [ "frc42_hasher", "frc42_macros", "fvm_ipld_encoding 0.3.3", - "fvm_sdk 3.0.0-alpha.22", - "fvm_shared 3.0.0-alpha.17", + "fvm_sdk 3.0.0-alpha.24", + "fvm_shared 3.0.0-alpha.20", "thiserror", ] @@ -2290,9 +2290,9 @@ dependencies = [ [[package]] name = "frc46_token" -version = "4.0.0" +version = "4.0.1-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361570ac004ff4c032db36985790b39a7c3e30c039dcc98661155227eba378f4" +checksum = "d88d59160be82c91c9ba8dd4d670693ab826178a1d94ae792ded0d6b92a723cd" dependencies = [ "anyhow", "cid", @@ -2302,8 +2302,8 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_sdk 3.0.0-alpha.22", - "fvm_shared 3.0.0-alpha.17", + "fvm_sdk 3.0.0-alpha.24", + "fvm_shared 3.0.0-alpha.20", "integer-encoding", "num-traits", "serde", @@ -2440,17 +2440,17 @@ dependencies = [ [[package]] name = "fvm_actor_utils" -version = "4.0.0" +version = "4.0.1-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a584440086c6900c43b131b326751aeb45e1ec547fbf62299db61d8eeb060f8" +checksum = "f2d45c603677e42634b25f52aff45db3d57a7f46c524e6d528489e193564cc38" dependencies = [ "anyhow", "cid", "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", - "fvm_sdk 3.0.0-alpha.22", - "fvm_shared 3.0.0-alpha.17", + "fvm_sdk 3.0.0-alpha.24", + "fvm_shared 3.0.0-alpha.20", "num-traits", "serde", "serde_tuple", @@ -2602,13 +2602,13 @@ dependencies = [ [[package]] name = "fvm_sdk" -version = "3.0.0-alpha.22" +version = "3.0.0-alpha.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51908aab9e7564fbcb278d78e31c12402b68c70517661780feb29adbb234aaf" +checksum = "ad163015237811580399ce929938f3bdca2d1a133672ff01693f555a632ec425" dependencies = [ "cid", "fvm_ipld_encoding 0.3.3", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "lazy_static", "log", "num-traits", @@ -2646,9 +2646,9 @@ dependencies = [ [[package]] name = "fvm_shared" -version = "3.0.0-alpha.17" +version = "3.0.0-alpha.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779876441e390f414161474701404b5641e02a5b9acfece9b212f6d24e482e1" +checksum = "b594b4d4c1393f03e3430f106c09874fcac565e68a560686de37ca2982a61d1a" dependencies = [ "anyhow", "bitflags", @@ -4577,7 +4577,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", - "fvm_shared 3.0.0-alpha.17", + "fvm_shared 3.0.0-alpha.20", "hex", "hex-literal", "indexmap", @@ -5137,4 +5137,4 @@ dependencies = [ name = "zeroize" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" \ No newline at end of file +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index 940be8604..dc9a23f9c 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.1" -fvm_actor_utils = "4.0.0" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_actor_utils = "4.0.1-alpha.1" +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 87b7ad239..97847fe72 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index ae825cf0f..a0cb969b2 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -18,12 +18,12 @@ fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime"} cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } frc42_dispatch = "3.0.1-alpha.1" -frc46_token = "4.0.0" -fvm_actor_utils = "4.0.0" +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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 2fe4bd001..016197509 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.3" multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 815f09f7c..fa1dcf00b 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/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.1" -fvm_actor_utils = "4.0.0" +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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", 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 ca04342d2..4a8f605eb 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fvm_ipld_kamt = { version = "0.2.0" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" diff --git a/actors/evm/shared/Cargo.toml b/actors/evm/shared/Cargo.toml index 22c7ad944..0d49a7d9c 100644 --- a/actors/evm/shared/Cargo.toml +++ b/actors/evm/shared/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["filecoin", "web3", "wasm", "evm"] [dependencies] serde = { version = "1.0.136", features = ["derive"] } -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../../runtime" } fvm_ipld_encoding = "0.3.3" uint = { version = "0.9.3", default-features = false } diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs index aa20aa235..e175f83fa 100644 --- a/actors/evm/src/interpreter/instructions/log_event.rs +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -1,7 +1,7 @@ use crate::interpreter::instructions::memory::get_memory_region; use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::ActorError; -use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; +use fvm_ipld_encoding::IPLD_RAW; use fvm_shared::event::{Entry, Flags}; use { crate::interpreter::{ExecutionState, System}, @@ -42,7 +42,8 @@ pub fn log( let entry = Entry { flags: Flags::FLAG_INDEXED_ALL, key: (*key).to_owned(), - value: to_vec(&topic)?.into(), // U256 serializes as a byte string. + codec: IPLD_RAW, + value: topic.to_bytes().into(), // U256 serializes as a byte string. }; entries.push(entry); } @@ -53,7 +54,8 @@ pub fn log( let entry = Entry { flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data))?, + codec: IPLD_RAW, + value: data, }; entries.push(entry); } @@ -66,7 +68,7 @@ pub fn log( #[cfg(test)] mod tests { use fil_actors_evm_shared::uints::U256; - use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes}; + use fvm_ipld_encoding::IPLD_RAW; use fvm_shared::event::{ActorEvent, Entry, Flags}; use super::{EVENT_DATA_KEY, EVENT_TOPIC_KEYS}; @@ -85,7 +87,8 @@ mod tests { ActorEvent::from(vec![Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data)).unwrap(), + codec: IPLD_RAW, + value: data.into(), }]) ); } @@ -118,12 +121,14 @@ mod tests { Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[0].to_owned(), - value: to_vec(&t1).unwrap().into(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data)).unwrap(), + codec: IPLD_RAW, + value: data.into(), } ]) ); @@ -159,17 +164,20 @@ mod tests { Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[0].to_owned(), - value: to_vec(&t1).unwrap().into(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[1].to_owned(), - value: to_vec(&t2).unwrap().into(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data)).unwrap(), + codec: IPLD_RAW, + value: data.into(), } ]) ); @@ -207,22 +215,26 @@ mod tests { Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[0].to_owned(), - value: to_vec(&t1).unwrap().into(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[1].to_owned(), - value: to_vec(&t2).unwrap().into(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[2].to_owned(), - value: to_vec(&t3).unwrap().into(), + codec: IPLD_RAW, + value: t3.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data)).unwrap(), + codec: IPLD_RAW, + value: data.into(), } ]) ); @@ -262,27 +274,32 @@ mod tests { Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[0].to_owned(), - value: to_vec(&t1).unwrap().into(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[1].to_owned(), - value: to_vec(&t2).unwrap().into(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[2].to_owned(), - value: to_vec(&t3).unwrap().into(), + codec: IPLD_RAW, + value: t3.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_TOPIC_KEYS[3].to_owned(), - value: to_vec(&t4).unwrap().into(), + codec: IPLD_RAW, + value: t4.to_bytes().into(), }, Entry{ flags: Flags::FLAG_INDEXED_ALL, key: EVENT_DATA_KEY.to_owned(), - value: RawBytes::serialize(BytesSer(&data)).unwrap(), + codec: IPLD_RAW, + value: data.into(), } ]) ); diff --git a/actors/evm/tests/events.rs b/actors/evm/tests/events.rs index 54c12a1a4..c8cbe86da 100644 --- a/actors/evm/tests/events.rs +++ b/actors/evm/tests/events.rs @@ -1,6 +1,7 @@ mod asm; -use fvm_ipld_encoding::{to_vec, RawBytes}; +use fil_actors_evm_shared::uints::U256; +use fvm_ipld_encoding::IPLD_RAW; use fvm_shared::event::{ActorEvent, Entry, Flags}; mod util; @@ -78,11 +79,8 @@ fn test_events() { entries: vec![Entry { flags: Flags::FLAG_INDEXED_ALL, key: "d".to_string(), - value: to_vec(&RawBytes::from( - [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), - )) - .unwrap() - .into(), + codec: IPLD_RAW, + value: vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], }], }); util::invoke_contract(&mut rt, &contract_params); @@ -99,31 +97,32 @@ fn test_events() { Entry { flags: Flags::FLAG_INDEXED_ALL, key: "t1".to_string(), - value: to_vec(&RawBytes::from([0x11, 0x11].to_vec())).unwrap().into(), + codec: IPLD_RAW, + value: U256::from(0x1111).to_bytes().into(), }, Entry { flags: Flags::FLAG_INDEXED_ALL, key: "t2".to_string(), - value: to_vec(&RawBytes::from([0x22, 0x22].to_vec())).unwrap().into(), + codec: IPLD_RAW, + value: U256::from(0x2222).to_bytes().into(), }, Entry { flags: Flags::FLAG_INDEXED_ALL, key: "t3".to_string(), - value: to_vec(&RawBytes::from([0x33, 0x33].to_vec())).unwrap().into(), + codec: IPLD_RAW, + value: U256::from(0x3333).to_bytes().into(), }, Entry { flags: Flags::FLAG_INDEXED_ALL, key: "t4".to_string(), - value: to_vec(&RawBytes::from([0x44, 0x44].to_vec())).unwrap().into(), + codec: IPLD_RAW, + value: U256::from(0x4444).to_bytes().into(), }, Entry { flags: Flags::FLAG_INDEXED_ALL, key: "d".to_string(), - value: to_vec(&RawBytes::from( - [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(), - )) - .unwrap() - .into(), + codec: IPLD_RAW, + value: vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], }, ], }); diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 7a96000df..fac69a981 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.1" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fvm_ipld_hamt = "0.6.1" serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 3ba15d43f..06c8e1582 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -19,12 +19,12 @@ 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.1" -frc46_token = "4.0.0" +frc46_token = "4.0.1-alpha.1" fvm_ipld_bitfield = "0.5.2" 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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", 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" diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index f733ae219..913189f14 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.1" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +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" diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index e3685d55e..665eb7f3d 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -19,11 +19,11 @@ 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.1" -fvm_actor_utils = "4.0.0" +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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", 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 8328919cc..ab4f1247b 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.1" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index d6dd29d15..36b028fee 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.1" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fvm_ipld_hamt = "0.6.1" num-traits = "0.2.14" num-derive = "0.3.3" diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index f29f5492b..1a313737e 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 9c1ef698b..9d83a34cd 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fvm_ipld_encoding = "0.3.3" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index d821ab4db..81d00c5c6 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -19,12 +19,12 @@ 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.1" -frc46_token = "4.0.0" -fvm_actor_utils = "4.0.0" +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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 646d366da..6493cadb8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,7 @@ 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.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } num = { version = "0.4", features = ["serde"] } num-traits = "0.2.14" num-derive = "0.3.3" @@ -37,7 +37,7 @@ castaway = "0.2.2" sha2 = "0.10" # fil-actor -fvm_sdk = { version = "3.0.0-alpha.22", optional = true } +fvm_sdk = { version = "3.0.0-alpha.24", optional = true } # test_util rand = { version = "0.8.5", default-features = false, optional = true } diff --git a/state/Cargo.toml b/state/Cargo.toml index d5b3e28e6..9407a9878 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -27,8 +27,8 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"} fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"} fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"} fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"} -frc46_token = "4.0.0" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +frc46_token = "4.0.1-alpha.1" +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } fvm_ipld_encoding = "0.3.3" fvm_ipld_blockstore = "0.1.1" num-traits = "0.2.14" diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 8218edd24..6e37888f0 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -33,13 +33,13 @@ anyhow = "1.0.65" bimap = { version = "0.6.2" } blake2b_simd = "1.0" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } -frc46_token = "4.0.0" -fvm_actor_utils = "4.0.0" +frc46_token = "4.0.1-alpha.1" +fvm_actor_utils = "4.0.1-alpha.1" fvm_ipld_bitfield = "0.5.4" fvm_ipld_blockstore = { version = "0.1.1", default-features = false } fvm_ipld_encoding = { version = "0.3.3", default-features = false } fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.17", default-features = false } +fvm_shared = { version = "3.0.0-alpha.20", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" From 1280bb26f2a5610cc0de758e55a3702d637f46a1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Feb 2023 10:20:58 -0800 Subject: [PATCH 327/339] EVM: remove Bytes (#1175) This was probably intended to reduce copying between EVM contracts, but it makes no difference for us beyond introducing strange types. --- Cargo.lock | 1 - actors/evm/Cargo.toml | 1 - actors/evm/src/interpreter/execution.rs | 7 +++--- .../evm/src/interpreter/instructions/call.rs | 22 +++++++++---------- .../src/interpreter/instructions/control.rs | 17 +++++++------- .../src/interpreter/instructions/lifecycle.rs | 4 +--- actors/evm/src/interpreter/memory.rs | 9 +++----- actors/evm/src/interpreter/output.rs | 4 +--- actors/evm/src/interpreter/test_util.rs | 8 +++---- actors/evm/src/lib.rs | 22 ++++++------------- 10 files changed, 38 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ede324389..3c2d79a40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1870,7 +1870,6 @@ version = "10.0.0-alpha.1" dependencies = [ "anyhow", "arrayvec", - "bytes", "cid", "derive_more", "ethers 1.0.2", diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 82c47b378..e91c06844 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -28,7 +28,6 @@ log = "0.4.14" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.3" rlp = { version = "0.5.1", default-features = false } -bytes = { version = "1.1.0", features = ["serde"], default-features = false } strum = "0.24" strum_macros = "0.24" multihash = { version = "0.16.1", default-features = false } diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 492170662..5fcb8d10b 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -7,7 +7,6 @@ use { super::memory::Memory, super::stack::Stack, super::{Bytecode, Output, System}, - bytes::Bytes, fil_actors_runtime::runtime::Runtime, }; @@ -16,8 +15,8 @@ use { pub struct ExecutionState { pub stack: Stack, pub memory: Memory, - pub input_data: Bytes, - pub return_data: Bytes, + pub input_data: Vec, + pub return_data: Vec, /// The EVM address of the caller. pub caller: EthAddress, /// The EVM address of the receiver. @@ -31,7 +30,7 @@ impl ExecutionState { caller: EthAddress, receiver: EthAddress, value_received: TokenAmount, - input_data: Bytes, + input_data: Vec, ) -> Self { Self { stack: Stack::new(), diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index d264f9af1..9fb396ec4 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -324,7 +324,7 @@ pub fn call_generic( } }; - state.return_data = return_data.into(); + 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)?; @@ -350,7 +350,7 @@ mod tests { (m) { CALLDATALOAD; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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"); @@ -366,7 +366,7 @@ mod tests { (m) { CALLDATALOAD; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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"); @@ -384,7 +384,7 @@ mod tests { } let mut input_data = [0u8;64]; input_data[0] = 0x42; - m.state.input_data = Vec::from(input_data).into(); + 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"); @@ -399,7 +399,7 @@ mod tests { (m) { CALLDATASIZE; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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); @@ -414,7 +414,7 @@ mod tests { (m) { CALLDATACOPY; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -424,7 +424,7 @@ mod tests { let mut expected = [0u8; 32]; expected[0] = 0x01; expected[1] = 0x02; - assert_eq!(m.state.memory.as_ref(), &expected); + assert_eq!(&*m.state.memory, &expected); }; } @@ -435,7 +435,7 @@ mod tests { (m) { CALLDATACOPY; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -445,7 +445,7 @@ mod tests { let mut expected = [0u8; 64]; expected[0] = 0x01; expected[1] = 0x02; - assert_eq!(m.state.memory.as_ref(), &expected); + assert_eq!(&*m.state.memory, &expected); }; } @@ -456,7 +456,7 @@ mod tests { (m) { CALLDATACOPY; } - m.state.input_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -464,7 +464,7 @@ mod tests { assert!(result.is_ok(), "execution step failed"); assert_eq!(m.state.stack.len(), 0); let expected = [0u8; 32]; - assert_eq!(m.state.memory.as_ref(), &expected); + assert_eq!(&*m.state.memory, &expected); }; } } diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 20ebb5cfe..9203d8a38 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -1,4 +1,3 @@ -use bytes::Bytes; use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::{ActorError, AsActorError}; @@ -53,7 +52,7 @@ pub fn stop( _state: &mut ExecutionState, _system: &System, ) -> Result { - Ok(Output { return_data: Bytes::new(), outcome: Outcome::Return }) + Ok(Output { return_data: Vec::new(), outcome: Outcome::Return }) } #[inline] @@ -66,7 +65,7 @@ fn exit( 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().into()) + .map(|region| memory[region.offset..region.offset + region.size.get()].to_vec()) .unwrap_or_default(), }) } @@ -318,7 +317,7 @@ mod tests { (m) { RETURNDATASIZE; } - m.state.return_data = vec![0x00, 0x01, 0x02].into(); + 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); @@ -333,7 +332,7 @@ mod tests { (m) { RETURNDATACOPY; } - m.state.return_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -343,7 +342,7 @@ mod tests { let mut expected = [0u8; 32]; expected[0] = 0x01; expected[1] = 0x02; - assert_eq!(m.state.memory.as_ref(), &expected); + assert_eq!(&*m.state.memory, &expected); }; } @@ -354,7 +353,7 @@ mod tests { (m) { RETURNDATACOPY; } - m.state.return_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -371,7 +370,7 @@ mod tests { (m) { RETURNDATACOPY; } - m.state.return_data = vec![0x00, 0x01, 0x02].into(); + 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 @@ -388,7 +387,7 @@ mod tests { (m) { RETURNDATACOPY; } - m.state.return_data = vec![0x00, 0x01, 0x02].into(); + 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 diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index b2fc45623..d434a0f09 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -1,5 +1,3 @@ -use bytes::Bytes; - use fil_actors_evm_shared::address::EthAddress; use fil_actors_evm_shared::uints::U256; use fil_actors_runtime::ActorError; @@ -170,5 +168,5 @@ pub fn selfdestruct( // // 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: Bytes::new() }) + Ok(Output { outcome: Outcome::Return, return_data: Vec::new() }) } diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs index f5336a6e4..9132c8850 100644 --- a/actors/evm/src/interpreter/memory.rs +++ b/actors/evm/src/interpreter/memory.rs @@ -1,18 +1,15 @@ -use { - bytes::BytesMut, - derive_more::{Deref, DerefMut}, -}; +use derive_more::{Deref, DerefMut}; use crate::EVM_WORD_SIZE; const PAGE_SIZE: usize = 4 * 1024; #[derive(Clone, Debug, Deref, DerefMut)] -pub struct Memory(BytesMut); +pub struct Memory(Vec); impl Default for Memory { fn default() -> Self { - Self(BytesMut::with_capacity(PAGE_SIZE)) + Self(Vec::with_capacity(PAGE_SIZE)) } } diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs index a4b0e158b..b9a1ae078 100644 --- a/actors/evm/src/interpreter/output.rs +++ b/actors/evm/src/interpreter/output.rs @@ -1,7 +1,5 @@ use std::fmt::Debug; -use bytes::Bytes; - #[derive(Debug, Clone, PartialEq, Eq, Default)] pub enum Outcome { #[default] @@ -15,5 +13,5 @@ pub struct Output { /// Indicates the "outcome" of the execution. pub outcome: Outcome, /// The return data. - pub return_data: Bytes, + pub return_data: Vec, } diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs index a21e44c9b..00f1fa358 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -17,7 +17,7 @@ macro_rules! evm_unit_test { use ::fil_actors_runtime::test_utils::MockRuntime; use ::fvm_shared::econ::TokenAmount; use $crate::interpreter::{execution::Machine, system::System, Output}; - use $crate::{Bytecode, Bytes, EthAddress, ExecutionState}; + use $crate::{Bytecode, EthAddress, ExecutionState}; let mut $rt = MockRuntime::default(); $rt.in_call = true; @@ -27,7 +27,7 @@ macro_rules! evm_unit_test { EthAddress::from_id(1000), EthAddress::from_id(1000), TokenAmount::from_atto(0), - Bytes::default(), + Vec::new(), ); let code = vec![$($crate::evm_instruction!($inst)),*]; @@ -50,14 +50,14 @@ macro_rules! evm_unit_test { use ::fil_actors_runtime::test_utils::MockRuntime; use ::fvm_shared::econ::TokenAmount; use $crate::interpreter::{execution::Machine, system::System, Output}; - use $crate::{Bytecode, Bytes, EthAddress, ExecutionState}; + use $crate::{Bytecode, EthAddress, ExecutionState}; let mut rt = MockRuntime::default(); let mut state = ExecutionState::new( EthAddress::from_id(1000), EthAddress::from_id(1000), TokenAmount::from_atto(0), - Bytes::default(), + Vec::new(), ); let code = vec![$($crate::evm_instruction!($inst)),*]; diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index e632e90fa..87aaf6709 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -19,7 +19,6 @@ mod state; use { crate::interpreter::{execute, Bytecode, ExecutionState, System}, - bytes::Bytes, cid::Cid, fil_actors_runtime::{ runtime::{ActorCode, Runtime}, @@ -132,8 +131,7 @@ fn initialize_evm_contract( // 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, Bytes::new()); + let mut exec_state = ExecutionState::new(caller, receiver_eth_addr, value_received, Vec::new()); // identify bytecode valid jump destinations let initcode = Bytecode::new(initcode); @@ -156,7 +154,7 @@ fn initialize_evm_contract( fn invoke_contract_inner( system: &mut System, - input_data: &[u8], + input_data: Vec, bytecode_cid: &Cid, caller: &EthAddress, value_received: TokenAmount, @@ -176,7 +174,7 @@ where 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.to_vec().into()); + ExecutionState::new(*caller, receiver_eth_addr, value_received, input_data); let output = execute(&bytecode, &mut exec_state, system)?; @@ -225,16 +223,10 @@ impl EvmContractActor { let mut system = System::load(rt).map_err(|e| { ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) })?; - invoke_contract_inner( - &mut system, - ¶ms.input, - ¶ms.code, - ¶ms.caller, - params.value, - ) + invoke_contract_inner(&mut system, params.input, ¶ms.code, ¶ms.caller, params.value) } - pub fn invoke_contract(rt: &mut RT, input_data: &[u8]) -> Result, ActorError> + pub fn invoke_contract(rt: &mut RT, input_data: Vec) -> Result, ActorError> where RT: Runtime, RT::Blockstore: Clone, @@ -267,7 +259,7 @@ impl EvmContractActor { { 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)?; + let output = Self::invoke_contract(rt, input)?; handle_filecoin_method_output(&output) } @@ -422,7 +414,7 @@ impl ActorCode for EvmContractActor { p } }; - let value = Self::invoke_contract(rt, ¶ms)?; + let value = Self::invoke_contract(rt, params)?; Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) } Some(Method::GetBytecode) => { From 52d2d4a9d9247c965753bfe0726599635e661509 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Feb 2023 11:21:14 -0800 Subject: [PATCH 328/339] EVM: remove a few more deps (#1176) - Remove OnceCell and replace it with an Option. - Move lazy_static to the dev dependencies. - Manually implement deref instead of importing a crate just for that. - Remove a bunch of unused dependencies. --- Cargo.lock | 18 -------- actors/evm/Cargo.toml | 11 +---- actors/evm/src/interpreter/memory.rs | 21 +++++++-- actors/evm/src/interpreter/system.rs | 64 +++++++++++++--------------- 4 files changed, 48 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c2d79a40..ec1e6eea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,6 @@ name = "arrayvec" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" -dependencies = [ - "serde", -] [[package]] name = "async-channel" @@ -778,12 +775,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.5.0" @@ -1009,10 +1000,8 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version", "syn", ] @@ -1869,14 +1858,11 @@ name = "fil_actor_evm" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "arrayvec", "cid", - "derive_more", "ethers 1.0.2", "etk-asm", "fil_actors_evm_shared", "fil_actors_runtime", - "fixed-hash 0.7.0", "frc42_dispatch", "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", @@ -1884,7 +1870,6 @@ dependencies = [ "fvm_shared 3.0.0-alpha.20", "hex", "hex-literal", - "impl-serde 0.3.2", "lazy_static", "log", "multihash", @@ -1893,12 +1878,9 @@ dependencies = [ "num-traits", "once_cell", "rand", - "rlp", "serde", "serde_json", "serde_tuple", - "strum", - "strum_macros", "substrate-bn", ] diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index e91c06844..4fe06095a 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -27,29 +27,22 @@ anyhow = "1.0.65" log = "0.4.14" fvm_ipld_blockstore = "0.1.1" fvm_ipld_encoding = "0.3.3" -rlp = { version = "0.5.1", default-features = false } -strum = "0.24" -strum_macros = "0.24" multihash = { version = "0.16.1", default-features = false } -derive_more = "0.99" -fixed-hash = { version = "0.7.0", default-features = false } -impl-serde = { version = "0.3.2", default-features = false } -arrayvec = { version = "0.7.2", features = ["serde"] } hex = { version = "0.4.3", features = ["serde"] } hex-literal = "0.3.4" substrate-bn = { version = "0.6.0", default-features = false } near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } -lazy_static = "1.4.0" -once_cell = { version = "1.16.0", default-features = false} frc42_dispatch = "3.0.1-alpha.2" fil_actors_evm_shared = { version = "10.0.0-alpha.1", 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] fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs index 9132c8850..95185ba37 100644 --- a/actors/evm/src/interpreter/memory.rs +++ b/actors/evm/src/interpreter/memory.rs @@ -1,12 +1,25 @@ -use derive_more::{Deref, DerefMut}; - use crate::EVM_WORD_SIZE; +use std::ops::{Deref, DerefMut}; const PAGE_SIZE: usize = 4 * 1024; -#[derive(Clone, Debug, Deref, DerefMut)] +#[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)) @@ -59,6 +72,6 @@ mod tests { let mut mem = Memory::default(); mem.grow(PAGE_SIZE * 2 + 1); assert_eq!(mem.len(), PAGE_SIZE * 2 + EVM_WORD_SIZE); - assert_eq!(mem.capacity(), PAGE_SIZE * 3); + assert_eq!(mem.0.capacity(), PAGE_SIZE * 3); } } diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 4662902a6..c3cb4f0c0 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -17,7 +17,6 @@ use fvm_shared::{ MethodNum, Response, IPLD_RAW, METHOD_SEND, }; use multihash::Code; -use once_cell::unsync::OnceCell; use crate::state::{State, Tombstone}; use crate::BytecodeHash; @@ -29,33 +28,27 @@ use { fvm_ipld_kamt::{AsHashedKey, Config as KamtConfig, Kamt}, }; -lazy_static::lazy_static! { - // 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. - static ref KAMT_CONFIG: KamtConfig = KamtConfig { - min_data_depth: 0, - bit_width: 5, - max_array_width: 1 - }; -} +// 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. +const KAMT_CONFIG: KamtConfig = KamtConfig { min_data_depth: 0, bit_width: 5, max_array_width: 1 }; pub struct StateHashAlgorithm; @@ -109,7 +102,7 @@ pub struct System<'r, RT: Runtime> { /// Read Only context (staticcall) pub readonly: bool, /// Randomness taken from the current epoch of chain randomness - randomness: OnceCell<[u8; 32]>, + 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 @@ -129,7 +122,7 @@ impl<'r, RT: Runtime> System<'r, RT> { saved_state_root: None, bytecode: None, readonly, - randomness: OnceCell::new(), + randomness: None, tombstone: None, } } @@ -197,7 +190,7 @@ impl<'r, RT: Runtime> System<'r, RT> { saved_state_root: Some(state_root), bytecode: Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)), readonly: read_only, - randomness: OnceCell::new(), + randomness: None, tombstone: state.tombstone, }) } @@ -441,14 +434,15 @@ impl<'r, RT: Runtime> System<'r, RT> { /// 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"; - self.randomness.get_or_try_init(|| { + match &mut self.randomness { + Some(rand) => Ok(&*rand), // get randomness from current beacon epoch with entropy of "prevrandao" - self.rt.get_randomness_from_beacon( + 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". From 7de375940d2a72f165e003dc2bf99975749d9a08 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Feb 2023 12:25:59 -0800 Subject: [PATCH 329/339] EVM: address some TODOs (#1177) Fixes https://github.com/filecoin-project/ref-fvm/issues/1407 Fixes https://github.com/filecoin-project/builtin-actors/pull/1084 --- actors/evm/shared/src/address.rs | 2 -- .../src/interpreter/instructions/context.rs | 7 ++--- .../src/interpreter/instructions/lifecycle.rs | 31 +++++++------------ actors/evm/src/interpreter/precompiles/fvm.rs | 2 +- actors/evm/tests/precompile.rs | 2 -- 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/actors/evm/shared/src/address.rs b/actors/evm/shared/src/address.rs index 5cff4e6c5..47b860ab5 100644 --- a/actors/evm/shared/src/address.rs +++ b/actors/evm/shared/src/address.rs @@ -5,8 +5,6 @@ use fvm_shared::address::Address; use fvm_shared::ActorID; /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). -/// -/// TODO this type will eventually handle f4 address detection. #[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone, Copy)] pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 077f86b78..831dfd24e 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -19,10 +19,9 @@ pub fn blockhash( .try_into() .ok() .filter(|&height: &ChainEpoch| { - // The EVM allows fetching blockhashes from the 256 _previous_ blocks. - // TODO: we can consider extending this to allow the full range. - // Also relates to https://github.com/filecoin-project/ref-fvm/issues/1023 (we might - // want to keep some of these restrictions). + // 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 }) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index d434a0f09..05acf0694 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -45,6 +45,7 @@ pub fn create( &[] }; + // 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) @@ -64,7 +65,6 @@ pub fn create2( let ExecutionState { stack: _, memory, .. } = state; - // see `create()` overall TODOs let endowment = TokenAmount::from(&endowment); if endowment > system.rt.current_balance() { return Ok(U256::zero()); @@ -82,6 +82,16 @@ pub fn create2( }; 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) } @@ -101,25 +111,6 @@ fn create_init( let ret = system.send(&EAM_ACTOR_ADDR, method, params, value, Some(gas_limit), SendFlags::default()); - // https://github.com/ethereum/go-ethereum/blob/fb75f11e87420ec25ff72f7eeeb741fa8974e87e/core/vm/evm.go#L406-L496 - // Normally EVM will do some checks here to ensure that a contract has the capability - // to create an actor, but here FVM does these checks for us, including: - // - execution depth, equal to FVM's max call depth (FVM) - // - account has enough value to send (FVM) - // - ensuring there isn't an existing account at the generated f4 address (INIT) - // - constructing smart contract on chain (INIT) - // - checks if max code size is exceeded (EAM & EVM) - // - gas cost of deployment (FVM) - // - EIP-3541 (EVM) - // - // However these errors are flattened to a 0 pushed on the stack. - - // TODO revert state if error was returned (revert nonce bump) - // https://github.com/filecoin-project/ref-fvm/issues/956 - - // TODO Exit with revert if sys out of gas when subcall gas limits are introduced - // https://github.com/filecoin-project/ref-fvm/issues/966 - Ok(match ret { Ok(eam_ret) => { let ret: eam::CreateReturn = deserialize_block(eam_ret)?; diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs index a7ed15eda..a9121e384 100644 --- a/actors/evm/src/interpreter/precompiles/fvm.rs +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -166,7 +166,7 @@ pub(super) fn call_actor_shared( // ------ Begin Call ------- let result = { - // TODO only CBOR or "nothing" for now + // 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")] diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 235bacdee..8ce9b72ca 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -282,8 +282,6 @@ fn test_precompile_transfer_nothing() { #[test] fn test_precompile_failure() { - // TODO: refactor these to be more clear - let bytecode = resolve_address_contract(); let mut rt = util::construct_and_verify(bytecode); From 267e8332e1c1657cc97b6c984465bbd6bcde0649 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 7 Feb 2023 17:44:39 -0500 Subject: [PATCH 330/339] runtime: drop unused exit() (#1162) --- runtime/src/runtime/fvm.rs | 4 ---- runtime/src/runtime/mod.rs | 4 ---- runtime/src/test_utils.rs | 36 +--------------------------------- test_vm/src/lib.rs | 40 ++------------------------------------ 4 files changed, 3 insertions(+), 81 deletions(-) diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 40a9602ff..d4aa52bc6 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -371,10 +371,6 @@ where .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to emit event") } - fn exit(&self, code: u32, data: Option, msg: Option<&str>) -> ! { - fvm::vm::exit(code, data, msg) - } - fn read_only(&self) -> bool { fvm::vm::read_only() } diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index 6648a52c2..5167c9187 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -251,10 +251,6 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// Emits an event denoting that something externally noteworthy has ocurred. fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError>; - /// Exit the current computation with an error code and optionally data and a debugging - /// message. - fn exit(&self, code: u32, data: Option, msg: Option<&str>) -> !; - /// 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; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 58d111a1a..2ee7a8fc3 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -134,12 +134,6 @@ pub fn init_logging() -> Result<(), log::SetLoggerError> { pretty_env_logger::try_init() } -pub struct ActorExit { - code: u32, - data: Option, - msg: Option, -} - pub struct MockRuntime { pub epoch: ChainEpoch, pub miner: Address, @@ -187,9 +181,6 @@ pub struct MockRuntime { pub actor_balances: HashMap, pub tipset_timestamp: u64, pub tipset_cids: Vec, - - // actor exits - pub actor_exit: RefCell>, } #[derive(Default)] @@ -365,7 +356,6 @@ impl MockRuntime { actor_balances: Default::default(), tipset_timestamp: Default::default(), tipset_cids: Default::default(), - actor_exit: Default::default(), } } } @@ -563,26 +553,7 @@ impl MockRuntime { ) -> Result, ActorError> { self.in_call = true; let prev_state = self.state; - let res: Result, ActorError> = - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - A::invoke_method(self, method_num, params) - })) - .unwrap_or_else(|panic| { - if self.actor_exit.borrow().is_some() { - let exit = self.actor_exit.take().unwrap(); - if exit.code == 0 { - Ok(exit.data) - } else { - Err(ActorError::unchecked_with_data( - ExitCode::new(exit.code), - exit.msg.unwrap_or_else(|| "actor exited".to_owned()), - exit.data, - )) - } - } else { - std::panic::resume_unwind(panic) - } - }); + let res = A::invoke_method(self, method_num, params); if res.is_err() { self.state = prev_state; @@ -1311,11 +1282,6 @@ impl Runtime for MockRuntime { Ok(()) } - fn exit(&self, code: u32, data: Option, msg: Option<&str>) -> ! { - self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); - std::panic::panic_any("actor exit"); - } - fn chain_id(&self) -> ChainID { self.chain_id } diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 8a1f79bd5..c3045e4f0 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -466,9 +466,8 @@ impl<'bs> VM<'bs> { read_only: false, policy: &Policy::default(), subinvocations: RefCell::new(vec![]), - actor_exit: RefCell::new(None), }; - let res = new_ctx.invoke_actor(); + let res = new_ctx.invoke(); let invoc = new_ctx.gather_trace(res.clone()); RefMut::map(self.invocations.borrow_mut(), |invocs| { @@ -608,13 +607,6 @@ pub struct InvocationCtx<'invocation, 'bs> { read_only: bool, policy: &'invocation Policy, subinvocations: RefCell>, - actor_exit: RefCell>, -} - -struct ActorExit { - code: u32, - data: Option, - msg: Option, } impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { @@ -676,7 +668,6 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { read_only: false, policy: self.policy, subinvocations: RefCell::new(vec![]), - actor_exit: RefCell::new(None), }; if is_account { new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, None).unwrap(); @@ -714,27 +705,6 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { self.resolve_target(&self.msg.to).unwrap().1 } - fn invoke_actor(&mut self) -> Result, ActorError> { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.invoke())).unwrap_or_else( - |panic| { - if self.actor_exit.borrow().is_some() { - let exit = self.actor_exit.take().unwrap(); - if exit.code == 0 { - Ok(exit.data) - } else { - Err(ActorError::unchecked_with_data( - ExitCode::new(exit.code), - exit.msg.unwrap_or_else(|| "actor exited".to_owned()), - exit.data, - )) - } - } else { - std::panic::resume_unwind(panic) - } - }, - ) - } - fn invoke(&mut self) -> Result, ActorError> { let prior_root = self.v.checkpoint(); @@ -1019,9 +989,8 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { read_only: send_flags.read_only(), policy: self.policy, subinvocations: RefCell::new(vec![]), - actor_exit: RefCell::new(None), }; - let res = new_ctx.invoke_actor(); + let res = new_ctx.invoke(); let invoc = new_ctx.gather_trace(res.clone()); RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { subinvocs.push(invoc); @@ -1149,11 +1118,6 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { Ok(()) } - fn exit(&self, code: u32, data: Option, msg: Option<&str>) -> ! { - self.actor_exit.replace(Some(ActorExit { code, data, msg: msg.map(|s| s.to_owned()) })); - std::panic::panic_any("actor exit"); - } - fn read_only(&self) -> bool { self.read_only } From 3c52ee8fa94a2bdbb65fa86a4caa7b6abd0f48c6 Mon Sep 17 00:00:00 2001 From: sleepy ramen Date: Wed, 8 Feb 2023 16:51:35 +0100 Subject: [PATCH 331/339] fix: added hyperspace feature for build (#1173) * fix: added hyperspace feature for build * rustfmt --- actors/evm/Cargo.toml | 1 + build.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 4fe06095a..79a8d2e70 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -45,5 +45,6 @@ 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/build.rs b/build.rs index 42c010789..4b88ecd39 100644 --- a/build.rs +++ b/build.rs @@ -36,7 +36,8 @@ const ACTORS: &[(&Package, &ID)] = &[ 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"])]; +const EXTRA_CARGO_FEATURES: &[(&str, &[&str])] = + &[("devnet-wasm", &["m2-native"]), ("hyperspace", &["hyperspace"])]; const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; From 1dd0bb0bcd27cceb77c3e525f7e9b78a68691519 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 8 Feb 2023 20:28:55 +0200 Subject: [PATCH 332/339] EVM: Code tests (#1181) * test codesize * test codecopy * test extcode* * rustfmt --- .../evm/src/interpreter/instructions/call.rs | 73 ++++++ .../evm/src/interpreter/instructions/ext.rs | 242 ++++++++++++++++++ 2 files changed, 315 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 9fb396ec4..3990d1fe6 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -467,4 +467,77 @@ mod tests { 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]); + }; + } } diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs index 290d1bd45..0394e4b30 100644 --- a/actors/evm/src/interpreter/instructions/ext.rs +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -150,3 +150,245 @@ pub fn get_evm_bytecode( 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)); + }; + } +} From 9b1f6675e264f2cb5dee5e39fa0fe5989bdfd8ac Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 8 Feb 2023 20:30:33 +0200 Subject: [PATCH 333/339] EVM: Context tests (#1182) * test address * test origin * test caller * test gas * test gasprice * rustfmt --- .../src/interpreter/instructions/context.rs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs index 831dfd24e..8001e502d 100644 --- a/actors/evm/src/interpreter/instructions/context.rs +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -152,7 +152,9 @@ 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() { @@ -330,4 +332,105 @@ mod tests { }; } } + + #[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)); + }; + } } From e97b8e4d3ecfb4bf5efd7635f68bda0fded2ce1f Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 8 Feb 2023 20:49:44 +0200 Subject: [PATCH 334/339] EVM: call tests (#1183) * test call happy path * test call alternative paths * test delegatecall * test staticcall * test invalid * rustfmt * add correctness check sled to test return/revert * clippy --- .../evm/src/interpreter/instructions/call.rs | 309 ++++++++++++++++++ .../src/interpreter/instructions/control.rs | 56 ++++ 2 files changed, 365 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs index 3990d1fe6..1cfee4518 100644 --- a/actors/evm/src/interpreter/instructions/call.rs +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -341,7 +341,16 @@ fn effective_gas_limit(system: &System, gas: U256) -> u64 { #[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() { @@ -540,4 +549,304 @@ mod tests { 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/control.rs b/actors/evm/src/interpreter/instructions/control.rs index 9203d8a38..334d64838 100644 --- a/actors/evm/src/interpreter/instructions/control.rs +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -396,4 +396,60 @@ mod tests { 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); + }; + } } From 0964aa8a81152a1df8f93e4110298610c6a11cf7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 8 Feb 2023 22:37:22 +0200 Subject: [PATCH 335/339] EVM: lifecycle tests (#1185) * test create * test create edge cases * test create2 * test create2 variants * test selfdestruct * rustfmt * check nonce on create tests * test read only conditions * rustfmt * add check for tombstone contents --- .../src/interpreter/instructions/lifecycle.rs | 421 ++++++++++++++++++ actors/evm/src/interpreter/system.rs | 4 +- 2 files changed, 423 insertions(+), 2 deletions(-) diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs index 05acf0694..4cad01b86 100644 --- a/actors/evm/src/interpreter/instructions/lifecycle.rs +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -161,3 +161,424 @@ pub fn selfdestruct( // 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/system.rs b/actors/evm/src/interpreter/system.rs index c3cb4f0c0..6e0815031 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -96,7 +96,7 @@ pub struct System<'r, RT: Runtime> { /// The contract's EVM storage slots. slots: StateKamt, /// The contracts "nonce" (incremented when creating new actors). - nonce: u64, + 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) @@ -106,7 +106,7 @@ pub struct System<'r, RT: Runtime> { /// 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 - tombstone: Option, + pub(crate) tombstone: Option, } impl<'r, RT: Runtime> System<'r, RT> { From e2db47ce57f9af494fa1d887dc4eb6a8f95b1e8c Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 9 Feb 2023 21:04:12 +0200 Subject: [PATCH 336/339] EVM: memory read tests (#1187) test memory reads --- .../src/interpreter/instructions/memory.rs | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs index 2773dca69..8e410fe27 100644 --- a/actors/evm/src/interpreter/instructions/memory.rs +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -479,6 +479,7 @@ mod tests { assert_eq!(m.state.stack.pop().unwrap(), U256::from(64)); }; } + #[test] fn test_msize_basic() { // Demonstrate that MSIZE depends on memory.len() @@ -493,4 +494,107 @@ mod tests { 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); + } } From ec5761700cb586fbb25a80e5a041f6be049b413e Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 9 Feb 2023 21:32:59 +0200 Subject: [PATCH 337/339] EVM: check all instructions for stack underflow behaviour (#1188) check all instructions for stack underflow behaviour none should panic. --- actors/evm/src/interpreter/execution.rs | 228 ++++++++++++++++++++++++ actors/evm/src/interpreter/test_util.rs | 1 + 2 files changed, 229 insertions(+) diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs index 5fcb8d10b..5fcc105fc 100644 --- a/actors/evm/src/interpreter/execution.rs +++ b/actors/evm/src/interpreter/execution.rs @@ -284,3 +284,231 @@ pub fn execute( ) -> 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/test_util.rs b/actors/evm/src/interpreter/test_util.rs index 00f1fa358..6bdebb258 100644 --- a/actors/evm/src/interpreter/test_util.rs +++ b/actors/evm/src/interpreter/test_util.rs @@ -53,6 +53,7 @@ macro_rules! evm_unit_test { 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), From 2fc00ef1c7b64390d152a89f61d6fddf492d1025 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 9 Feb 2023 14:59:45 -0800 Subject: [PATCH 338/339] EVM: use blake2f from parity's frontier (#1191) That way, we: 1. Don't depend on a fork of an unmaintained library. 2. Use a much simpler blake2f implementation. --- Cargo.lock | 11 --- actors/evm/Cargo.toml | 1 - .../interpreter/precompiles/blake2f_impl.rs | 92 +++++++++++++++++++ actors/evm/src/interpreter/precompiles/evm.rs | 9 +- actors/evm/src/interpreter/precompiles/mod.rs | 1 + 5 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 actors/evm/src/interpreter/precompiles/blake2f_impl.rs diff --git a/Cargo.lock b/Cargo.lock index ec1e6eea9..b1c334739 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1873,7 +1873,6 @@ dependencies = [ "lazy_static", "log", "multihash", - "near-blake2", "num-derive", "num-traits", "once_cell", @@ -3267,16 +3266,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "near-blake2" -version = "0.9.1" -source = "git+https://github.com/filecoin-project/near-blake2.git#47a58e5061ba6d6c1132f535188b7fde2f67bcb2" -dependencies = [ - "crypto-mac", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - [[package]] name = "num" version = "0.4.0" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index 79a8d2e70..a2ab59b78 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -31,7 +31,6 @@ 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 } -near-blake2 = { version = "0.9.1", git = "https://github.com/filecoin-project/near-blake2.git" } frc42_dispatch = "3.0.1-alpha.2" fil_actors_evm_shared = { version = "10.0.0-alpha.1", path = "shared" } 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 index 1fbfab880..e47f6979c 100644 --- a/actors/evm/src/interpreter/precompiles/evm.rs +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -252,7 +252,6 @@ pub(super) fn blake2f( if input.len() != 213 { return Err(PrecompileError::IncorrectInputSize); } - let mut hasher = near_blake2::VarBlake2b::default(); let mut rounds = [0u8; 4]; let mut start = 0; @@ -278,7 +277,7 @@ pub(super) fn blake2f( }?; let rounds = u32::from_be_bytes(rounds); - let h = { + let mut h = { let mut ret = [0u64; 8]; LE::read_u64_into(h, &mut ret); ret @@ -294,8 +293,10 @@ pub(super) fn blake2f( ret }; - hasher.blake2_f(rounds, h, m, t, f); - let output = hasher.output().to_vec(); + 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) } diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index b2c56302f..5faf7f68d 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -8,6 +8,7 @@ use substrate_bn::{CurveError, FieldError, GroupError}; use crate::reader::OverflowError; use super::{CallKind, System}; +mod blake2f_impl; mod evm; mod fvm; From 50c199cd8765403f4443cf2ca78216306ae59acc Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 10 Feb 2023 17:25:19 +0200 Subject: [PATCH 339/339] hackety hack: make compliance test runner compile --- Cargo.lock | 17 +---------------- Cargo.toml | 19 ++++++++++--------- actors/account/Cargo.toml | 6 +++--- actors/cron/Cargo.toml | 6 +++--- actors/datacap/Cargo.toml | 8 ++++---- actors/eam/Cargo.toml | 6 +++--- actors/ethaccount/Cargo.toml | 4 ++-- actors/evm/Cargo.toml | 10 +++++----- actors/evm/shared/Cargo.toml | 6 +++--- actors/evm/src/interpreter/mod.rs | 2 +- actors/evm/src/interpreter/system.rs | 2 +- actors/init/Cargo.toml | 8 ++++---- actors/market/Cargo.toml | 10 +++++----- actors/miner/Cargo.toml | 12 ++++++------ actors/multisig/Cargo.toml | 8 ++++---- actors/paych/Cargo.toml | 6 +++--- actors/power/Cargo.toml | 8 ++++---- actors/reward/Cargo.toml | 6 +++--- actors/system/Cargo.toml | 6 +++--- actors/verifreg/Cargo.toml | 8 ++++---- build.rs | 2 +- runtime/Cargo.toml | 12 ++++++------ 22 files changed, 79 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1c334739..834044bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2136,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", @@ -2440,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", @@ -2456,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", @@ -2468,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", @@ -2512,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", @@ -2529,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", @@ -2549,8 +2540,6 @@ dependencies = [ [[package]] name = "fvm_ipld_kamt" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab54acc8b19c5029ceefb3a1aa5708e1513a6ef7b17cdfeb6674c042b70d163" dependencies = [ "anyhow", "byteorder", @@ -2583,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", @@ -2627,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", diff --git a/Cargo.toml b/Cargo.toml index 2e343e7e1..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"] } @@ -67,15 +68,15 @@ members = ["actors/*", "state", "runtime", "test_vm", "actors/evm/shared"] ## 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_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"} +##[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. 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/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 5711ea62d..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" 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/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 a2ab59b78..c12423ecd 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -16,8 +16,8 @@ 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_kamt = { version = "0.2.0" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_kamt = { path = "../../../ref-fvm/ipld/kamt" } serde = { version = "1.0.136", features = ["derive"] } serde_tuple = "0.5" num-traits = "0.2.14" @@ -25,14 +25,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"} 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 = { version = "10.0.0-alpha.1", path = "shared" } +fil_actors_evm_shared = { path = "shared" } [dev-dependencies] lazy_static = "1.4.0" diff --git a/actors/evm/shared/Cargo.toml b/actors/evm/shared/Cargo.toml index 0d49a7d9c..a0a212fd4 100644 --- a/actors/evm/shared/Cargo.toml +++ b/actors/evm/shared/Cargo.toml @@ -10,8 +10,8 @@ keywords = ["filecoin", "web3", "wasm", "evm"] [dependencies] serde = { version = "1.0.136", features = ["derive"] } -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../../runtime" } -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/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs index 908f05d45..a79fca0e5 100644 --- a/actors/evm/src/interpreter/mod.rs +++ b/actors/evm/src/interpreter/mod.rs @@ -5,7 +5,7 @@ mod memory; mod output; mod precompiles; mod stack; -mod system; +pub mod system; #[cfg(test)] pub mod test_util; diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs index 6e0815031..aedc39d49 100644 --- a/actors/evm/src/interpreter/system.rs +++ b/actors/evm/src/interpreter/system.rs @@ -48,7 +48,7 @@ use { // // 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. -const KAMT_CONFIG: KamtConfig = KamtConfig { min_data_depth: 0, bit_width: 5, max_array_width: 1 }; +pub const KAMT_CONFIG: KamtConfig = KamtConfig { min_data_depth: 0, bit_width: 5, max_array_width: 1 }; pub struct StateHashAlgorithm; diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index 82649d997..53ff969a3 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -16,16 +16,16 @@ 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 = { path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 88889d2eb..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" 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/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/power/Cargo.toml b/actors/power/Cargo.toml index 1ba94d969..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,8 +27,8 @@ 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 = { path = "../../runtime", features = ["test_utils", "sector-default"] } 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 bf2d420f7..1cd2543f6 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -21,10 +21,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" log = "0.4.14" num-derive = "0.3.3" diff --git a/build.rs b/build.rs index 4b88ecd39..d41462f18 100644 --- a/build.rs +++ b/build.rs @@ -136,7 +136,7 @@ fn main() -> Result<(), Box> { .args(packages.iter().map(|pkg| "-p=".to_owned() + pkg)) .arg("--target=wasm32-unknown-unknown") .arg("--profile=wasm") - .arg("--locked") + .arg("--offline") .arg("--features=".to_owned() + &features.join(",")) .arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap()) .env(NETWORK_ENV, network_name) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 154588f60..4e054ffdc 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,9 +8,9 @@ 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" @@ -23,9 +23,9 @@ log = "0.4.14" thiserror = "1.0.30" anyhow = "1.0.65" fvm_sdk = { version = "3.0.0-alpha.24", optional = true } -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 } serde_repr = "0.1.8" regex = "1"

C-2YtlmZG5h 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 WmQsxh^Ajd#vgUogdqrySzy24YH_(3o diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline index cc6deeabe..67a6ca654 100644 --- a/actors/evm/tests/measurements/inc_after_fill.jsonline +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2358,"get_count":3,"put_bytes":460,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":2,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":3,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":4,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":5,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":6,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":7,"series":1,"stats":{"get_bytes":2466,"get_count":4,"put_bytes":458,"put_count":3}} -{"i":8,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":9,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":10,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":11,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":12,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":13,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":14,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":15,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":16,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":17,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":18,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":19,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":20,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":21,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":22,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":23,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":24,"series":1,"stats":{"get_bytes":2464,"get_count":4,"put_bytes":456,"put_count":3}} -{"i":25,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":26,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":27,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":28,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":29,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":30,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":31,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":32,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":33,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":34,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":35,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":36,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":37,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":38,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":39,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":40,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":41,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":42,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":43,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":44,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":45,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":46,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":47,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":48,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":49,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":50,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":51,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":52,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":53,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":54,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":55,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":56,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":57,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":58,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":59,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":60,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":61,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":62,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":63,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":64,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":65,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":66,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":67,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":68,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":69,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":70,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":71,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":72,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":73,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":74,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":75,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":76,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":77,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":78,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":79,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":80,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":81,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":82,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":83,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":84,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":85,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":86,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":87,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":88,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":89,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":90,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":91,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":92,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":93,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":94,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":95,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":96,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":97,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":98,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":99,"series":1,"stats":{"get_bytes":2465,"get_count":4,"put_bytes":457,"put_count":3}} -{"i":0,"series":2,"stats":{"get_bytes":2595,"get_count":3,"put_bytes":595,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2943,"get_count":3,"put_bytes":935,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":3245,"get_count":3,"put_bytes":1237,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":3522,"get_count":3,"put_bytes":1514,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":3648,"get_count":3,"put_bytes":1640,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":3835,"get_count":3,"put_bytes":1827,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":3983,"get_count":4,"put_bytes":1975,"put_count":3}} -{"i":7,"series":2,"stats":{"get_bytes":4093,"get_count":4,"put_bytes":2085,"put_count":3}} -{"i":8,"series":2,"stats":{"get_bytes":4185,"get_count":4,"put_bytes":2177,"put_count":3}} -{"i":9,"series":2,"stats":{"get_bytes":4231,"get_count":4,"put_bytes":2223,"put_count":3}} -{"i":10,"series":2,"stats":{"get_bytes":4277,"get_count":4,"put_bytes":2269,"put_count":3}} -{"i":11,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2309,"put_count":3}} -{"i":12,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2309,"put_count":3}} -{"i":13,"series":2,"stats":{"get_bytes":4317,"get_count":4,"put_bytes":2309,"put_count":3}} -{"i":14,"series":2,"stats":{"get_bytes":4340,"get_count":4,"put_bytes":2332,"put_count":3}} -{"i":15,"series":2,"stats":{"get_bytes":4340,"get_count":4,"put_bytes":2332,"put_count":3}} -{"i":16,"series":2,"stats":{"get_bytes":4448,"get_count":4,"put_bytes":2440,"put_count":3}} -{"i":17,"series":2,"stats":{"get_bytes":4448,"get_count":4,"put_bytes":2440,"put_count":3}} -{"i":18,"series":2,"stats":{"get_bytes":4448,"get_count":4,"put_bytes":2440,"put_count":3}} -{"i":19,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":20,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":21,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":22,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":23,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":24,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":25,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":26,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":27,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":28,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":29,"series":2,"stats":{"get_bytes":4489,"get_count":4,"put_bytes":2481,"put_count":3}} -{"i":30,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":31,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":32,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":33,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":34,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":35,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":36,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":37,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":38,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":39,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":40,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":41,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":42,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":43,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":44,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":45,"series":2,"stats":{"get_bytes":4530,"get_count":4,"put_bytes":2522,"put_count":3}} -{"i":46,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":47,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":48,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":49,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":50,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":51,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":52,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":53,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":54,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":55,"series":2,"stats":{"get_bytes":4571,"get_count":4,"put_bytes":2563,"put_count":3}} -{"i":56,"series":2,"stats":{"get_bytes":4612,"get_count":4,"put_bytes":2604,"put_count":3}} -{"i":57,"series":2,"stats":{"get_bytes":4653,"get_count":4,"put_bytes":2645,"put_count":3}} -{"i":58,"series":2,"stats":{"get_bytes":4653,"get_count":4,"put_bytes":2645,"put_count":3}} -{"i":59,"series":2,"stats":{"get_bytes":4653,"get_count":4,"put_bytes":2645,"put_count":3}} -{"i":60,"series":2,"stats":{"get_bytes":4653,"get_count":4,"put_bytes":2645,"put_count":3}} -{"i":61,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":62,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":63,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":64,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":65,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":66,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":67,"series":2,"stats":{"get_bytes":4694,"get_count":4,"put_bytes":2686,"put_count":3}} -{"i":68,"series":2,"stats":{"get_bytes":4735,"get_count":4,"put_bytes":2727,"put_count":3}} -{"i":69,"series":2,"stats":{"get_bytes":4799,"get_count":4,"put_bytes":2791,"put_count":3}} -{"i":70,"series":2,"stats":{"get_bytes":4799,"get_count":4,"put_bytes":2791,"put_count":3}} -{"i":71,"series":2,"stats":{"get_bytes":4799,"get_count":4,"put_bytes":2791,"put_count":3}} -{"i":72,"series":2,"stats":{"get_bytes":4799,"get_count":4,"put_bytes":2791,"put_count":3}} -{"i":73,"series":2,"stats":{"get_bytes":4799,"get_count":4,"put_bytes":2791,"put_count":3}} -{"i":74,"series":2,"stats":{"get_bytes":4822,"get_count":4,"put_bytes":2814,"put_count":3}} -{"i":75,"series":2,"stats":{"get_bytes":4863,"get_count":4,"put_bytes":2855,"put_count":3}} -{"i":76,"series":2,"stats":{"get_bytes":4863,"get_count":4,"put_bytes":2855,"put_count":3}} -{"i":77,"series":2,"stats":{"get_bytes":4863,"get_count":4,"put_bytes":2855,"put_count":3}} -{"i":78,"series":2,"stats":{"get_bytes":4886,"get_count":4,"put_bytes":2878,"put_count":3}} -{"i":79,"series":2,"stats":{"get_bytes":4886,"get_count":4,"put_bytes":2878,"put_count":3}} -{"i":80,"series":2,"stats":{"get_bytes":4994,"get_count":5,"put_bytes":2986,"put_count":4}} -{"i":81,"series":2,"stats":{"get_bytes":4994,"get_count":5,"put_bytes":2986,"put_count":4}} -{"i":82,"series":2,"stats":{"get_bytes":5035,"get_count":5,"put_bytes":3027,"put_count":4}} -{"i":83,"series":2,"stats":{"get_bytes":5035,"get_count":5,"put_bytes":3027,"put_count":4}} -{"i":84,"series":2,"stats":{"get_bytes":5035,"get_count":5,"put_bytes":3027,"put_count":4}} -{"i":85,"series":2,"stats":{"get_bytes":5035,"get_count":5,"put_bytes":3027,"put_count":4}} -{"i":86,"series":2,"stats":{"get_bytes":5035,"get_count":5,"put_bytes":3027,"put_count":4}} -{"i":87,"series":2,"stats":{"get_bytes":5076,"get_count":5,"put_bytes":3068,"put_count":4}} -{"i":88,"series":2,"stats":{"get_bytes":5076,"get_count":5,"put_bytes":3068,"put_count":4}} -{"i":89,"series":2,"stats":{"get_bytes":5076,"get_count":5,"put_bytes":3068,"put_count":4}} -{"i":90,"series":2,"stats":{"get_bytes":5076,"get_count":5,"put_bytes":3068,"put_count":4}} -{"i":91,"series":2,"stats":{"get_bytes":5117,"get_count":5,"put_bytes":3109,"put_count":4}} -{"i":92,"series":2,"stats":{"get_bytes":5117,"get_count":5,"put_bytes":3109,"put_count":4}} -{"i":93,"series":2,"stats":{"get_bytes":5117,"get_count":5,"put_bytes":3109,"put_count":4}} -{"i":94,"series":2,"stats":{"get_bytes":5117,"get_count":5,"put_bytes":3109,"put_count":4}} -{"i":95,"series":2,"stats":{"get_bytes":5117,"get_count":5,"put_bytes":3109,"put_count":4}} -{"i":96,"series":2,"stats":{"get_bytes":5157,"get_count":5,"put_bytes":3149,"put_count":4}} -{"i":97,"series":2,"stats":{"get_bytes":5157,"get_count":5,"put_bytes":3149,"put_count":4}} -{"i":98,"series":2,"stats":{"get_bytes":5220,"get_count":5,"put_bytes":3212,"put_count":4}} -{"i":99,"series":2,"stats":{"get_bytes":5243,"get_count":5,"put_bytes":3235,"put_count":4}} +{"i":0,"series":1,"stats":{"get_bytes":2275,"get_count":3,"put_bytes":377,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":2,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":3,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":4,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":5,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":6,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2383,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":8,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":373,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":30,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":31,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":32,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":45,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":57,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":58,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":61,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":62,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":63,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":74,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":2382,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":2512,"get_count":3,"put_bytes":512,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2860,"get_count":3,"put_bytes":852,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":3162,"get_count":3,"put_bytes":1154,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3439,"get_count":3,"put_bytes":1431,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3565,"get_count":3,"put_bytes":1557,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3752,"get_count":3,"put_bytes":1744,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":1892,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":4010,"get_count":4,"put_bytes":2002,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":4102,"get_count":4,"put_bytes":2094,"put_count":3}} +{"i":9,"series":2,"stats":{"get_bytes":4148,"get_count":4,"put_bytes":2140,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":4194,"get_count":4,"put_bytes":2186,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":4234,"get_count":4,"put_bytes":2226,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":4234,"get_count":4,"put_bytes":2226,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":4234,"get_count":4,"put_bytes":2226,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2249,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":4257,"get_count":4,"put_bytes":2249,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":4365,"get_count":4,"put_bytes":2357,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":4365,"get_count":4,"put_bytes":2357,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":4365,"get_count":4,"put_bytes":2357,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":28,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":4406,"get_count":4,"put_bytes":2398,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":38,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":43,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":4447,"get_count":4,"put_bytes":2439,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":52,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":55,"series":2,"stats":{"get_bytes":4488,"get_count":4,"put_bytes":2480,"put_count":3}} +{"i":56,"series":2,"stats":{"get_bytes":4529,"get_count":4,"put_bytes":2521,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":58,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":60,"series":2,"stats":{"get_bytes":4570,"get_count":4,"put_bytes":2562,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":63,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":64,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":65,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":67,"series":2,"stats":{"get_bytes":4611,"get_count":4,"put_bytes":2603,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4652,"get_count":4,"put_bytes":2644,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4716,"get_count":4,"put_bytes":2708,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4716,"get_count":4,"put_bytes":2708,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4716,"get_count":4,"put_bytes":2708,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4716,"get_count":4,"put_bytes":2708,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4716,"get_count":4,"put_bytes":2708,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4739,"get_count":4,"put_bytes":2731,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4780,"get_count":4,"put_bytes":2772,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4780,"get_count":4,"put_bytes":2772,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4780,"get_count":4,"put_bytes":2772,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4803,"get_count":4,"put_bytes":2795,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4803,"get_count":4,"put_bytes":2795,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4911,"get_count":5,"put_bytes":2903,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":4911,"get_count":5,"put_bytes":2903,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":4952,"get_count":5,"put_bytes":2944,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":4952,"get_count":5,"put_bytes":2944,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4952,"get_count":5,"put_bytes":2944,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":4952,"get_count":5,"put_bytes":2944,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":4952,"get_count":5,"put_bytes":2944,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":4993,"get_count":5,"put_bytes":2985,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":5034,"get_count":5,"put_bytes":3026,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":5074,"get_count":5,"put_bytes":3066,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":5074,"get_count":5,"put_bytes":3066,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":5137,"get_count":5,"put_bytes":3129,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":5160,"get_count":5,"put_bytes":3152,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_after_fill.png b/actors/evm/tests/measurements/inc_after_fill.png index a632d49f6f8d6aa44cdde36d36043072f78e12f2..4d542b554a66efff75fc6599e0a00ace9ad74f23 100644 GIT binary patch literal 14604 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 literal 14587 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 E2VIVba{vGU diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline index e6276442e..7b3c508dd 100644 --- a/actors/evm/tests/measurements/inc_one_vs_all.jsonline +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -1,20 +1,20 @@ -{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":2435,"put_count":5}} -{"i":0,"series":2,"stats":{"get_bytes":2226,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":1,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":1,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":2,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":2,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":3,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":3,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":4,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":4,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":5,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":5,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":6,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":6,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":7,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":7,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":8,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":8,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":9,"series":1,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} -{"i":9,"series":2,"stats":{"get_bytes":2238,"get_count":3,"put_bytes":230,"put_count":2}} +{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2269,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2143,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2155,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2155,"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 index e4e598000317fde2da51cef031192ee9797eb205..de6cd86c625153774c916e12ee513107d102017d 100644 GIT binary patch literal 14346 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 literal 19949 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 diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline index d5b654e83..cb6ff04d9 100644 --- a/actors/evm/tests/measurements/mapping_add_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":2217,"put_count":3}} -{"i":1,"series":1,"stats":{"get_bytes":2217,"get_count":3,"put_bytes":6637,"put_count":35}} -{"i":2,"series":1,"stats":{"get_bytes":7860,"get_count":28,"put_bytes":11089,"put_count":43}} -{"i":3,"series":1,"stats":{"get_bytes":11395,"get_count":32,"put_bytes":14546,"put_count":46}} -{"i":4,"series":1,"stats":{"get_bytes":15979,"get_count":37,"put_bytes":20316,"put_count":68}} -{"i":5,"series":1,"stats":{"get_bytes":19426,"get_count":43,"put_bytes":23068,"put_count":64}} -{"i":6,"series":1,"stats":{"get_bytes":21051,"get_count":38,"put_bytes":25676,"put_count":73}} -{"i":7,"series":1,"stats":{"get_bytes":25780,"get_count":47,"put_bytes":30336,"put_count":81}} -{"i":8,"series":1,"stats":{"get_bytes":30262,"get_count":48,"put_bytes":34808,"put_count":82}} -{"i":9,"series":1,"stats":{"get_bytes":32222,"get_count":55,"put_bytes":37015,"put_count":93}} -{"i":10,"series":1,"stats":{"get_bytes":35735,"get_count":49,"put_bytes":40149,"put_count":81}} -{"i":11,"series":1,"stats":{"get_bytes":38945,"get_count":54,"put_bytes":43914,"put_count":94}} -{"i":12,"series":1,"stats":{"get_bytes":41865,"get_count":70,"put_bytes":46664,"put_count":108}} -{"i":13,"series":1,"stats":{"get_bytes":41416,"get_count":68,"put_bytes":45962,"put_count":102}} -{"i":14,"series":1,"stats":{"get_bytes":45932,"get_count":67,"put_bytes":51026,"put_count":109}} -{"i":15,"series":1,"stats":{"get_bytes":47361,"get_count":71,"put_bytes":52060,"put_count":107}} -{"i":16,"series":1,"stats":{"get_bytes":49962,"get_count":72,"put_bytes":54992,"put_count":113}} -{"i":17,"series":1,"stats":{"get_bytes":51023,"get_count":74,"put_bytes":56005,"put_count":114}} -{"i":18,"series":1,"stats":{"get_bytes":49657,"get_count":75,"put_bytes":54552,"put_count":114}} -{"i":19,"series":1,"stats":{"get_bytes":59322,"get_count":92,"put_bytes":63458,"put_count":120}} -{"i":20,"series":1,"stats":{"get_bytes":54114,"get_count":81,"put_bytes":58540,"put_count":113}} -{"i":21,"series":1,"stats":{"get_bytes":56996,"get_count":80,"put_bytes":62056,"put_count":121}} -{"i":22,"series":1,"stats":{"get_bytes":57329,"get_count":83,"put_bytes":61893,"put_count":117}} -{"i":23,"series":1,"stats":{"get_bytes":65947,"get_count":108,"put_bytes":69003,"put_count":121}} -{"i":24,"series":1,"stats":{"get_bytes":65038,"get_count":104,"put_bytes":68739,"put_count":126}} -{"i":25,"series":1,"stats":{"get_bytes":65474,"get_count":102,"put_bytes":69674,"put_count":131}} -{"i":26,"series":1,"stats":{"get_bytes":64491,"get_count":94,"put_bytes":69128,"put_count":129}} -{"i":27,"series":1,"stats":{"get_bytes":67596,"get_count":107,"put_bytes":71039,"put_count":125}} -{"i":28,"series":1,"stats":{"get_bytes":67987,"get_count":106,"put_bytes":72130,"put_count":134}} -{"i":29,"series":1,"stats":{"get_bytes":68663,"get_count":106,"put_bytes":72038,"put_count":123}} -{"i":30,"series":1,"stats":{"get_bytes":73729,"get_count":113,"put_bytes":77789,"put_count":140}} -{"i":31,"series":1,"stats":{"get_bytes":67887,"get_count":107,"put_bytes":71723,"put_count":131}} -{"i":32,"series":1,"stats":{"get_bytes":74993,"get_count":111,"put_bytes":78830,"put_count":135}} -{"i":33,"series":1,"stats":{"get_bytes":72515,"get_count":114,"put_bytes":76559,"put_count":141}} -{"i":34,"series":1,"stats":{"get_bytes":74846,"get_count":113,"put_bytes":78162,"put_count":130}} -{"i":35,"series":1,"stats":{"get_bytes":74368,"get_count":116,"put_bytes":77556,"put_count":131}} -{"i":36,"series":1,"stats":{"get_bytes":75388,"get_count":113,"put_bytes":78663,"put_count":129}} -{"i":37,"series":1,"stats":{"get_bytes":76406,"get_count":117,"put_bytes":80063,"put_count":138}} -{"i":38,"series":1,"stats":{"get_bytes":78590,"get_count":120,"put_bytes":82434,"put_count":144}} -{"i":39,"series":1,"stats":{"get_bytes":73109,"get_count":115,"put_bytes":76344,"put_count":130}} -{"i":40,"series":1,"stats":{"get_bytes":78601,"get_count":121,"put_bytes":81681,"put_count":134}} -{"i":41,"series":1,"stats":{"get_bytes":78161,"get_count":118,"put_bytes":81467,"put_count":134}} -{"i":42,"series":1,"stats":{"get_bytes":80637,"get_count":118,"put_bytes":84426,"put_count":141}} -{"i":43,"series":1,"stats":{"get_bytes":79120,"get_count":121,"put_bytes":82402,"put_count":137}} -{"i":44,"series":1,"stats":{"get_bytes":84748,"get_count":127,"put_bytes":87703,"put_count":138}} -{"i":45,"series":1,"stats":{"get_bytes":82011,"get_count":123,"put_bytes":85630,"put_count":144}} -{"i":46,"series":1,"stats":{"get_bytes":83935,"get_count":128,"put_bytes":87436,"put_count":147}} -{"i":47,"series":1,"stats":{"get_bytes":83671,"get_count":128,"put_bytes":87038,"put_count":145}} -{"i":48,"series":1,"stats":{"get_bytes":87270,"get_count":125,"put_bytes":90434,"put_count":139}} -{"i":49,"series":1,"stats":{"get_bytes":85174,"get_count":127,"put_bytes":88610,"put_count":145}} -{"i":50,"series":1,"stats":{"get_bytes":81723,"get_count":117,"put_bytes":84876,"put_count":131}} -{"i":51,"series":1,"stats":{"get_bytes":85408,"get_count":119,"put_bytes":88494,"put_count":132}} -{"i":52,"series":1,"stats":{"get_bytes":86192,"get_count":128,"put_bytes":89621,"put_count":146}} -{"i":53,"series":1,"stats":{"get_bytes":82935,"get_count":122,"put_bytes":85805,"put_count":132}} -{"i":54,"series":1,"stats":{"get_bytes":87899,"get_count":130,"put_bytes":91199,"put_count":146}} -{"i":55,"series":1,"stats":{"get_bytes":85332,"get_count":120,"put_bytes":88391,"put_count":133}} -{"i":56,"series":1,"stats":{"get_bytes":85791,"get_count":127,"put_bytes":89017,"put_count":142}} -{"i":57,"series":1,"stats":{"get_bytes":90095,"get_count":132,"put_bytes":93028,"put_count":143}} -{"i":58,"series":1,"stats":{"get_bytes":87429,"get_count":129,"put_bytes":90638,"put_count":144}} -{"i":59,"series":1,"stats":{"get_bytes":89760,"get_count":135,"put_bytes":92487,"put_count":143}} -{"i":60,"series":1,"stats":{"get_bytes":88270,"get_count":129,"put_bytes":91564,"put_count":145}} -{"i":61,"series":1,"stats":{"get_bytes":92828,"get_count":131,"put_bytes":95981,"put_count":145}} -{"i":62,"series":1,"stats":{"get_bytes":85549,"get_count":127,"put_bytes":88503,"put_count":138}} -{"i":63,"series":1,"stats":{"get_bytes":93831,"get_count":134,"put_bytes":97067,"put_count":150}} -{"i":64,"series":1,"stats":{"get_bytes":93700,"get_count":131,"put_bytes":96930,"put_count":146}} -{"i":65,"series":1,"stats":{"get_bytes":86405,"get_count":126,"put_bytes":89839,"put_count":144}} -{"i":66,"series":1,"stats":{"get_bytes":92236,"get_count":128,"put_bytes":95737,"put_count":147}} -{"i":67,"series":1,"stats":{"get_bytes":92993,"get_count":134,"put_bytes":95937,"put_count":145}} -{"i":68,"series":1,"stats":{"get_bytes":94574,"get_count":130,"put_bytes":97728,"put_count":144}} -{"i":69,"series":1,"stats":{"get_bytes":94358,"get_count":131,"put_bytes":97645,"put_count":147}} -{"i":70,"series":1,"stats":{"get_bytes":95458,"get_count":131,"put_bytes":98880,"put_count":149}} -{"i":71,"series":1,"stats":{"get_bytes":91971,"get_count":126,"put_bytes":95473,"put_count":145}} -{"i":72,"series":1,"stats":{"get_bytes":91963,"get_count":131,"put_bytes":95253,"put_count":147}} -{"i":73,"series":1,"stats":{"get_bytes":93562,"get_count":132,"put_bytes":96427,"put_count":142}} -{"i":74,"series":1,"stats":{"get_bytes":90722,"get_count":127,"put_bytes":94085,"put_count":144}} -{"i":75,"series":1,"stats":{"get_bytes":97984,"get_count":134,"put_bytes":101418,"put_count":152}} -{"i":76,"series":1,"stats":{"get_bytes":96190,"get_count":131,"put_bytes":99483,"put_count":147}} -{"i":77,"series":1,"stats":{"get_bytes":93967,"get_count":132,"put_bytes":96698,"put_count":140}} -{"i":78,"series":1,"stats":{"get_bytes":95495,"get_count":130,"put_bytes":98574,"put_count":143}} -{"i":79,"series":1,"stats":{"get_bytes":95392,"get_count":132,"put_bytes":99174,"put_count":155}} -{"i":80,"series":1,"stats":{"get_bytes":96246,"get_count":133,"put_bytes":99673,"put_count":151}} -{"i":81,"series":1,"stats":{"get_bytes":99827,"get_count":138,"put_bytes":103255,"put_count":156}} -{"i":82,"series":1,"stats":{"get_bytes":95949,"get_count":127,"put_bytes":99802,"put_count":151}} -{"i":83,"series":1,"stats":{"get_bytes":92833,"get_count":123,"put_bytes":95983,"put_count":137}} -{"i":84,"series":1,"stats":{"get_bytes":102242,"get_count":134,"put_bytes":105732,"put_count":153}} -{"i":85,"series":1,"stats":{"get_bytes":97209,"get_count":129,"put_bytes":100287,"put_count":142}} -{"i":86,"series":1,"stats":{"get_bytes":99566,"get_count":131,"put_bytes":102719,"put_count":145}} -{"i":87,"series":1,"stats":{"get_bytes":97477,"get_count":129,"put_bytes":100908,"put_count":147}} -{"i":88,"series":1,"stats":{"get_bytes":98375,"get_count":131,"put_bytes":102219,"put_count":155}} -{"i":89,"series":1,"stats":{"get_bytes":101938,"get_count":133,"put_bytes":105851,"put_count":158}} -{"i":90,"series":1,"stats":{"get_bytes":105194,"get_count":135,"put_bytes":108905,"put_count":157}} -{"i":91,"series":1,"stats":{"get_bytes":99040,"get_count":135,"put_bytes":102540,"put_count":154}} -{"i":92,"series":1,"stats":{"get_bytes":105100,"get_count":132,"put_bytes":108951,"put_count":156}} -{"i":93,"series":1,"stats":{"get_bytes":99294,"get_count":130,"put_bytes":103704,"put_count":162}} -{"i":94,"series":1,"stats":{"get_bytes":102331,"get_count":130,"put_bytes":105892,"put_count":150}} -{"i":95,"series":1,"stats":{"get_bytes":102508,"get_count":132,"put_bytes":106363,"put_count":156}} -{"i":96,"series":1,"stats":{"get_bytes":99990,"get_count":134,"put_bytes":103068,"put_count":147}} -{"i":97,"series":1,"stats":{"get_bytes":101058,"get_count":135,"put_bytes":104776,"put_count":157}} -{"i":98,"series":1,"stats":{"get_bytes":99946,"get_count":133,"put_bytes":103439,"put_count":152}} -{"i":99,"series":1,"stats":{"get_bytes":103382,"get_count":135,"put_bytes":106744,"put_count":152}} -{"i":0,"series":2,"stats":{"get_bytes":106355,"get_count":140,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":104222,"get_count":131,"put_bytes":107859,"put_count":152}} -{"i":2,"series":2,"stats":{"get_bytes":103597,"get_count":132,"put_bytes":107584,"put_count":158}} -{"i":3,"series":2,"stats":{"get_bytes":105514,"get_count":133,"put_bytes":109579,"put_count":160}} -{"i":4,"series":2,"stats":{"get_bytes":99404,"get_count":130,"put_bytes":103320,"put_count":155}} -{"i":5,"series":2,"stats":{"get_bytes":104910,"get_count":131,"put_bytes":108971,"put_count":158}} -{"i":6,"series":2,"stats":{"get_bytes":108434,"get_count":133,"put_bytes":112360,"put_count":158}} -{"i":7,"series":2,"stats":{"get_bytes":108340,"get_count":134,"put_bytes":112408,"put_count":161}} -{"i":8,"series":2,"stats":{"get_bytes":110856,"get_count":138,"put_bytes":114775,"put_count":163}} -{"i":9,"series":2,"stats":{"get_bytes":109485,"get_count":134,"put_bytes":113266,"put_count":157}} -{"i":10,"series":2,"stats":{"get_bytes":105189,"get_count":131,"put_bytes":108969,"put_count":154}} -{"i":11,"series":2,"stats":{"get_bytes":108510,"get_count":137,"put_bytes":112079,"put_count":157}} -{"i":12,"series":2,"stats":{"get_bytes":106199,"get_count":130,"put_bytes":110034,"put_count":154}} -{"i":13,"series":2,"stats":{"get_bytes":105971,"get_count":132,"put_bytes":109825,"put_count":156}} -{"i":14,"series":2,"stats":{"get_bytes":110076,"get_count":135,"put_bytes":114120,"put_count":162}} -{"i":15,"series":2,"stats":{"get_bytes":97966,"get_count":127,"put_bytes":101322,"put_count":144}} -{"i":16,"series":2,"stats":{"get_bytes":109674,"get_count":134,"put_bytes":113392,"put_count":156}} -{"i":17,"series":2,"stats":{"get_bytes":111051,"get_count":136,"put_bytes":114617,"put_count":156}} -{"i":18,"series":2,"stats":{"get_bytes":111647,"get_count":135,"put_bytes":115781,"put_count":163}} -{"i":19,"series":2,"stats":{"get_bytes":108266,"get_count":129,"put_bytes":111559,"put_count":145}} -{"i":20,"series":2,"stats":{"get_bytes":108955,"get_count":132,"put_bytes":112802,"put_count":156}} -{"i":21,"series":2,"stats":{"get_bytes":115364,"get_count":139,"put_bytes":119358,"put_count":165}} -{"i":22,"series":2,"stats":{"get_bytes":112082,"get_count":135,"put_bytes":116002,"put_count":160}} -{"i":23,"series":2,"stats":{"get_bytes":109507,"get_count":131,"put_bytes":113219,"put_count":153}} -{"i":24,"series":2,"stats":{"get_bytes":111926,"get_count":131,"put_bytes":115908,"put_count":157}} -{"i":25,"series":2,"stats":{"get_bytes":113046,"get_count":129,"put_bytes":116876,"put_count":153}} -{"i":26,"series":2,"stats":{"get_bytes":115095,"get_count":139,"put_bytes":118601,"put_count":158}} -{"i":27,"series":2,"stats":{"get_bytes":116309,"get_count":138,"put_bytes":120066,"put_count":161}} -{"i":28,"series":2,"stats":{"get_bytes":114434,"get_count":135,"put_bytes":118694,"put_count":165}} -{"i":29,"series":2,"stats":{"get_bytes":108071,"get_count":133,"put_bytes":111579,"put_count":152}} -{"i":30,"series":2,"stats":{"get_bytes":112853,"get_count":136,"put_bytes":116985,"put_count":164}} -{"i":31,"series":2,"stats":{"get_bytes":115611,"get_count":137,"put_bytes":119809,"put_count":166}} -{"i":32,"series":2,"stats":{"get_bytes":116422,"get_count":139,"put_bytes":120901,"put_count":172}} -{"i":33,"series":2,"stats":{"get_bytes":109848,"get_count":131,"put_bytes":114106,"put_count":161}} -{"i":34,"series":2,"stats":{"get_bytes":115986,"get_count":136,"put_bytes":119970,"put_count":162}} -{"i":35,"series":2,"stats":{"get_bytes":117282,"get_count":133,"put_bytes":121324,"put_count":160}} -{"i":36,"series":2,"stats":{"get_bytes":116518,"get_count":134,"put_bytes":121212,"put_count":170}} -{"i":37,"series":2,"stats":{"get_bytes":114893,"get_count":132,"put_bytes":118817,"put_count":157}} -{"i":38,"series":2,"stats":{"get_bytes":112875,"get_count":131,"put_bytes":117506,"put_count":166}} -{"i":39,"series":2,"stats":{"get_bytes":110427,"get_count":131,"put_bytes":113989,"put_count":151}} -{"i":40,"series":2,"stats":{"get_bytes":111460,"get_count":131,"put_bytes":115516,"put_count":158}} -{"i":41,"series":2,"stats":{"get_bytes":115827,"get_count":143,"put_bytes":119676,"put_count":167}} -{"i":42,"series":2,"stats":{"get_bytes":110760,"get_count":127,"put_bytes":115033,"put_count":157}} -{"i":43,"series":2,"stats":{"get_bytes":117791,"get_count":131,"put_bytes":121353,"put_count":151}} -{"i":44,"series":2,"stats":{"get_bytes":117817,"get_count":137,"put_bytes":121599,"put_count":160}} -{"i":45,"series":2,"stats":{"get_bytes":121471,"get_count":142,"put_bytes":125613,"put_count":170}} -{"i":46,"series":2,"stats":{"get_bytes":120304,"get_count":140,"put_bytes":123939,"put_count":161}} -{"i":47,"series":2,"stats":{"get_bytes":118566,"get_count":135,"put_bytes":123043,"put_count":168}} -{"i":48,"series":2,"stats":{"get_bytes":118587,"get_count":137,"put_bytes":123069,"put_count":170}} -{"i":49,"series":2,"stats":{"get_bytes":119879,"get_count":136,"put_bytes":123935,"put_count":163}} -{"i":50,"series":2,"stats":{"get_bytes":117534,"get_count":136,"put_bytes":121319,"put_count":159}} -{"i":51,"series":2,"stats":{"get_bytes":120198,"get_count":139,"put_bytes":123552,"put_count":156}} -{"i":52,"series":2,"stats":{"get_bytes":122314,"get_count":144,"put_bytes":126790,"put_count":177}} -{"i":53,"series":2,"stats":{"get_bytes":115918,"get_count":129,"put_bytes":119903,"put_count":155}} -{"i":54,"series":2,"stats":{"get_bytes":118538,"get_count":142,"put_bytes":122675,"put_count":170}} -{"i":55,"series":2,"stats":{"get_bytes":118149,"get_count":130,"put_bytes":122137,"put_count":156}} -{"i":56,"series":2,"stats":{"get_bytes":120483,"get_count":140,"put_bytes":124892,"put_count":172}} -{"i":57,"series":2,"stats":{"get_bytes":120444,"get_count":139,"put_bytes":124693,"put_count":169}} -{"i":58,"series":2,"stats":{"get_bytes":126947,"get_count":139,"put_bytes":131504,"put_count":173}} -{"i":59,"series":2,"stats":{"get_bytes":125847,"get_count":133,"put_bytes":130950,"put_count":175}} -{"i":60,"series":2,"stats":{"get_bytes":127281,"get_count":136,"put_bytes":131896,"put_count":171}} -{"i":61,"series":2,"stats":{"get_bytes":126740,"get_count":134,"put_bytes":131581,"put_count":172}} -{"i":62,"series":2,"stats":{"get_bytes":120494,"get_count":131,"put_bytes":124840,"put_count":162}} -{"i":63,"series":2,"stats":{"get_bytes":118110,"get_count":138,"put_bytes":122647,"put_count":172}} -{"i":64,"series":2,"stats":{"get_bytes":124798,"get_count":139,"put_bytes":129208,"put_count":171}} -{"i":65,"series":2,"stats":{"get_bytes":125599,"get_count":137,"put_bytes":130079,"put_count":170}} -{"i":66,"series":2,"stats":{"get_bytes":125446,"get_count":138,"put_bytes":129653,"put_count":167}} -{"i":67,"series":2,"stats":{"get_bytes":127863,"get_count":144,"put_bytes":131990,"put_count":172}} -{"i":68,"series":2,"stats":{"get_bytes":128332,"get_count":140,"put_bytes":132592,"put_count":170}} -{"i":69,"series":2,"stats":{"get_bytes":124627,"get_count":140,"put_bytes":129168,"put_count":174}} -{"i":70,"series":2,"stats":{"get_bytes":128459,"get_count":142,"put_bytes":132237,"put_count":165}} -{"i":71,"series":2,"stats":{"get_bytes":125441,"get_count":134,"put_bytes":129706,"put_count":164}} -{"i":72,"series":2,"stats":{"get_bytes":123939,"get_count":135,"put_bytes":128210,"put_count":165}} -{"i":73,"series":2,"stats":{"get_bytes":125453,"get_count":140,"put_bytes":130357,"put_count":179}} -{"i":74,"series":2,"stats":{"get_bytes":122901,"get_count":134,"put_bytes":127387,"put_count":167}} -{"i":75,"series":2,"stats":{"get_bytes":124532,"get_count":141,"put_bytes":128591,"put_count":168}} -{"i":76,"series":2,"stats":{"get_bytes":126906,"get_count":142,"put_bytes":131532,"put_count":177}} -{"i":77,"series":2,"stats":{"get_bytes":131303,"get_count":135,"put_bytes":135772,"put_count":168}} -{"i":78,"series":2,"stats":{"get_bytes":129289,"get_count":140,"put_bytes":133640,"put_count":171}} -{"i":79,"series":2,"stats":{"get_bytes":128852,"get_count":134,"put_bytes":133485,"put_count":169}} -{"i":80,"series":2,"stats":{"get_bytes":131313,"get_count":143,"put_bytes":135997,"put_count":179}} -{"i":81,"series":2,"stats":{"get_bytes":125548,"get_count":137,"put_bytes":129755,"put_count":166}} -{"i":82,"series":2,"stats":{"get_bytes":132583,"get_count":145,"put_bytes":136925,"put_count":176}} -{"i":83,"series":2,"stats":{"get_bytes":129143,"get_count":136,"put_bytes":134052,"put_count":175}} -{"i":84,"series":2,"stats":{"get_bytes":128184,"get_count":136,"put_bytes":132367,"put_count":165}} -{"i":85,"series":2,"stats":{"get_bytes":128729,"get_count":137,"put_bytes":133212,"put_count":170}} -{"i":86,"series":2,"stats":{"get_bytes":126071,"get_count":142,"put_bytes":130701,"put_count":177}} -{"i":87,"series":2,"stats":{"get_bytes":134394,"get_count":140,"put_bytes":138954,"put_count":174}} -{"i":88,"series":2,"stats":{"get_bytes":128717,"get_count":138,"put_bytes":133121,"put_count":170}} -{"i":89,"series":2,"stats":{"get_bytes":126196,"get_count":140,"put_bytes":130265,"put_count":167}} -{"i":90,"series":2,"stats":{"get_bytes":128002,"get_count":141,"put_bytes":132062,"put_count":168}} -{"i":91,"series":2,"stats":{"get_bytes":134351,"get_count":143,"put_bytes":138362,"put_count":170}} -{"i":92,"series":2,"stats":{"get_bytes":128807,"get_count":140,"put_bytes":133128,"put_count":171}} -{"i":93,"series":2,"stats":{"get_bytes":129930,"get_count":140,"put_bytes":134797,"put_count":179}} -{"i":94,"series":2,"stats":{"get_bytes":132487,"get_count":143,"put_bytes":136686,"put_count":172}} -{"i":95,"series":2,"stats":{"get_bytes":129874,"get_count":142,"put_bytes":134072,"put_count":171}} -{"i":96,"series":2,"stats":{"get_bytes":133941,"get_count":142,"put_bytes":138474,"put_count":176}} -{"i":97,"series":2,"stats":{"get_bytes":133508,"get_count":141,"put_bytes":138066,"put_count":175}} -{"i":98,"series":2,"stats":{"get_bytes":136883,"get_count":146,"put_bytes":140738,"put_count":170}} -{"i":99,"series":2,"stats":{"get_bytes":126538,"get_count":139,"put_bytes":131019,"put_count":172}} +{"i":0,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":2134,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2134,"get_count":3,"put_bytes":6554,"put_count":35}} +{"i":2,"series":1,"stats":{"get_bytes":7777,"get_count":28,"put_bytes":11006,"put_count":43}} +{"i":3,"series":1,"stats":{"get_bytes":11312,"get_count":32,"put_bytes":14463,"put_count":46}} +{"i":4,"series":1,"stats":{"get_bytes":15896,"get_count":37,"put_bytes":20233,"put_count":68}} +{"i":5,"series":1,"stats":{"get_bytes":19343,"get_count":43,"put_bytes":22985,"put_count":64}} +{"i":6,"series":1,"stats":{"get_bytes":20968,"get_count":38,"put_bytes":25593,"put_count":73}} +{"i":7,"series":1,"stats":{"get_bytes":25697,"get_count":47,"put_bytes":30253,"put_count":81}} +{"i":8,"series":1,"stats":{"get_bytes":30179,"get_count":48,"put_bytes":34725,"put_count":82}} +{"i":9,"series":1,"stats":{"get_bytes":32139,"get_count":55,"put_bytes":36932,"put_count":93}} +{"i":10,"series":1,"stats":{"get_bytes":35652,"get_count":49,"put_bytes":40066,"put_count":81}} +{"i":11,"series":1,"stats":{"get_bytes":38862,"get_count":54,"put_bytes":43831,"put_count":94}} +{"i":12,"series":1,"stats":{"get_bytes":41782,"get_count":70,"put_bytes":46581,"put_count":108}} +{"i":13,"series":1,"stats":{"get_bytes":41333,"get_count":68,"put_bytes":45879,"put_count":102}} +{"i":14,"series":1,"stats":{"get_bytes":45849,"get_count":67,"put_bytes":50943,"put_count":109}} +{"i":15,"series":1,"stats":{"get_bytes":47278,"get_count":71,"put_bytes":51977,"put_count":107}} +{"i":16,"series":1,"stats":{"get_bytes":49879,"get_count":72,"put_bytes":54909,"put_count":113}} +{"i":17,"series":1,"stats":{"get_bytes":50940,"get_count":74,"put_bytes":55922,"put_count":114}} +{"i":18,"series":1,"stats":{"get_bytes":49574,"get_count":75,"put_bytes":54469,"put_count":114}} +{"i":19,"series":1,"stats":{"get_bytes":59239,"get_count":92,"put_bytes":63375,"put_count":120}} +{"i":20,"series":1,"stats":{"get_bytes":54031,"get_count":81,"put_bytes":58457,"put_count":113}} +{"i":21,"series":1,"stats":{"get_bytes":56913,"get_count":80,"put_bytes":61973,"put_count":121}} +{"i":22,"series":1,"stats":{"get_bytes":57246,"get_count":83,"put_bytes":61810,"put_count":117}} +{"i":23,"series":1,"stats":{"get_bytes":65864,"get_count":108,"put_bytes":68920,"put_count":121}} +{"i":24,"series":1,"stats":{"get_bytes":64955,"get_count":104,"put_bytes":68656,"put_count":126}} +{"i":25,"series":1,"stats":{"get_bytes":65391,"get_count":102,"put_bytes":69591,"put_count":131}} +{"i":26,"series":1,"stats":{"get_bytes":64408,"get_count":94,"put_bytes":69045,"put_count":129}} +{"i":27,"series":1,"stats":{"get_bytes":67513,"get_count":107,"put_bytes":70956,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":67904,"get_count":106,"put_bytes":72047,"put_count":134}} +{"i":29,"series":1,"stats":{"get_bytes":68580,"get_count":106,"put_bytes":71955,"put_count":123}} +{"i":30,"series":1,"stats":{"get_bytes":73646,"get_count":113,"put_bytes":77706,"put_count":140}} +{"i":31,"series":1,"stats":{"get_bytes":67804,"get_count":107,"put_bytes":71640,"put_count":131}} +{"i":32,"series":1,"stats":{"get_bytes":74910,"get_count":111,"put_bytes":78747,"put_count":135}} +{"i":33,"series":1,"stats":{"get_bytes":72432,"get_count":114,"put_bytes":76476,"put_count":141}} +{"i":34,"series":1,"stats":{"get_bytes":74763,"get_count":113,"put_bytes":78079,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":74285,"get_count":116,"put_bytes":77473,"put_count":131}} +{"i":36,"series":1,"stats":{"get_bytes":75305,"get_count":113,"put_bytes":78580,"put_count":129}} +{"i":37,"series":1,"stats":{"get_bytes":76323,"get_count":117,"put_bytes":79980,"put_count":138}} +{"i":38,"series":1,"stats":{"get_bytes":78507,"get_count":120,"put_bytes":82351,"put_count":144}} +{"i":39,"series":1,"stats":{"get_bytes":73026,"get_count":115,"put_bytes":76261,"put_count":130}} +{"i":40,"series":1,"stats":{"get_bytes":78518,"get_count":121,"put_bytes":81598,"put_count":134}} +{"i":41,"series":1,"stats":{"get_bytes":78078,"get_count":118,"put_bytes":81384,"put_count":134}} +{"i":42,"series":1,"stats":{"get_bytes":80554,"get_count":118,"put_bytes":84343,"put_count":141}} +{"i":43,"series":1,"stats":{"get_bytes":79037,"get_count":121,"put_bytes":82319,"put_count":137}} +{"i":44,"series":1,"stats":{"get_bytes":84665,"get_count":127,"put_bytes":87620,"put_count":138}} +{"i":45,"series":1,"stats":{"get_bytes":81928,"get_count":123,"put_bytes":85547,"put_count":144}} +{"i":46,"series":1,"stats":{"get_bytes":83852,"get_count":128,"put_bytes":87353,"put_count":147}} +{"i":47,"series":1,"stats":{"get_bytes":83588,"get_count":128,"put_bytes":86955,"put_count":145}} +{"i":48,"series":1,"stats":{"get_bytes":87187,"get_count":125,"put_bytes":90351,"put_count":139}} +{"i":49,"series":1,"stats":{"get_bytes":85091,"get_count":127,"put_bytes":88527,"put_count":145}} +{"i":50,"series":1,"stats":{"get_bytes":81640,"get_count":117,"put_bytes":84793,"put_count":131}} +{"i":51,"series":1,"stats":{"get_bytes":85325,"get_count":119,"put_bytes":88411,"put_count":132}} +{"i":52,"series":1,"stats":{"get_bytes":86109,"get_count":128,"put_bytes":89538,"put_count":146}} +{"i":53,"series":1,"stats":{"get_bytes":82852,"get_count":122,"put_bytes":85722,"put_count":132}} +{"i":54,"series":1,"stats":{"get_bytes":87816,"get_count":130,"put_bytes":91116,"put_count":146}} +{"i":55,"series":1,"stats":{"get_bytes":85249,"get_count":120,"put_bytes":88308,"put_count":133}} +{"i":56,"series":1,"stats":{"get_bytes":85708,"get_count":127,"put_bytes":88934,"put_count":142}} +{"i":57,"series":1,"stats":{"get_bytes":90012,"get_count":132,"put_bytes":92945,"put_count":143}} +{"i":58,"series":1,"stats":{"get_bytes":87346,"get_count":129,"put_bytes":90555,"put_count":144}} +{"i":59,"series":1,"stats":{"get_bytes":89677,"get_count":135,"put_bytes":92404,"put_count":143}} +{"i":60,"series":1,"stats":{"get_bytes":88187,"get_count":129,"put_bytes":91481,"put_count":145}} +{"i":61,"series":1,"stats":{"get_bytes":92745,"get_count":131,"put_bytes":95898,"put_count":145}} +{"i":62,"series":1,"stats":{"get_bytes":85466,"get_count":127,"put_bytes":88420,"put_count":138}} +{"i":63,"series":1,"stats":{"get_bytes":93748,"get_count":134,"put_bytes":96984,"put_count":150}} +{"i":64,"series":1,"stats":{"get_bytes":93617,"get_count":131,"put_bytes":96847,"put_count":146}} +{"i":65,"series":1,"stats":{"get_bytes":86322,"get_count":126,"put_bytes":89756,"put_count":144}} +{"i":66,"series":1,"stats":{"get_bytes":92153,"get_count":128,"put_bytes":95654,"put_count":147}} +{"i":67,"series":1,"stats":{"get_bytes":92910,"get_count":134,"put_bytes":95854,"put_count":145}} +{"i":68,"series":1,"stats":{"get_bytes":94491,"get_count":130,"put_bytes":97645,"put_count":144}} +{"i":69,"series":1,"stats":{"get_bytes":94275,"get_count":131,"put_bytes":97562,"put_count":147}} +{"i":70,"series":1,"stats":{"get_bytes":95375,"get_count":131,"put_bytes":98797,"put_count":149}} +{"i":71,"series":1,"stats":{"get_bytes":91888,"get_count":126,"put_bytes":95390,"put_count":145}} +{"i":72,"series":1,"stats":{"get_bytes":91880,"get_count":131,"put_bytes":95170,"put_count":147}} +{"i":73,"series":1,"stats":{"get_bytes":93479,"get_count":132,"put_bytes":96344,"put_count":142}} +{"i":74,"series":1,"stats":{"get_bytes":90639,"get_count":127,"put_bytes":94002,"put_count":144}} +{"i":75,"series":1,"stats":{"get_bytes":97901,"get_count":134,"put_bytes":101335,"put_count":152}} +{"i":76,"series":1,"stats":{"get_bytes":96107,"get_count":131,"put_bytes":99400,"put_count":147}} +{"i":77,"series":1,"stats":{"get_bytes":93884,"get_count":132,"put_bytes":96615,"put_count":140}} +{"i":78,"series":1,"stats":{"get_bytes":95412,"get_count":130,"put_bytes":98491,"put_count":143}} +{"i":79,"series":1,"stats":{"get_bytes":95309,"get_count":132,"put_bytes":99091,"put_count":155}} +{"i":80,"series":1,"stats":{"get_bytes":96163,"get_count":133,"put_bytes":99590,"put_count":151}} +{"i":81,"series":1,"stats":{"get_bytes":99744,"get_count":138,"put_bytes":103172,"put_count":156}} +{"i":82,"series":1,"stats":{"get_bytes":95866,"get_count":127,"put_bytes":99719,"put_count":151}} +{"i":83,"series":1,"stats":{"get_bytes":92750,"get_count":123,"put_bytes":95900,"put_count":137}} +{"i":84,"series":1,"stats":{"get_bytes":102159,"get_count":134,"put_bytes":105649,"put_count":153}} +{"i":85,"series":1,"stats":{"get_bytes":97126,"get_count":129,"put_bytes":100204,"put_count":142}} +{"i":86,"series":1,"stats":{"get_bytes":99483,"get_count":131,"put_bytes":102636,"put_count":145}} +{"i":87,"series":1,"stats":{"get_bytes":97394,"get_count":129,"put_bytes":100825,"put_count":147}} +{"i":88,"series":1,"stats":{"get_bytes":98292,"get_count":131,"put_bytes":102136,"put_count":155}} +{"i":89,"series":1,"stats":{"get_bytes":101855,"get_count":133,"put_bytes":105768,"put_count":158}} +{"i":90,"series":1,"stats":{"get_bytes":105111,"get_count":135,"put_bytes":108822,"put_count":157}} +{"i":91,"series":1,"stats":{"get_bytes":98957,"get_count":135,"put_bytes":102457,"put_count":154}} +{"i":92,"series":1,"stats":{"get_bytes":105017,"get_count":132,"put_bytes":108868,"put_count":156}} +{"i":93,"series":1,"stats":{"get_bytes":99211,"get_count":130,"put_bytes":103621,"put_count":162}} +{"i":94,"series":1,"stats":{"get_bytes":102248,"get_count":130,"put_bytes":105809,"put_count":150}} +{"i":95,"series":1,"stats":{"get_bytes":102425,"get_count":132,"put_bytes":106280,"put_count":156}} +{"i":96,"series":1,"stats":{"get_bytes":99907,"get_count":134,"put_bytes":102985,"put_count":147}} +{"i":97,"series":1,"stats":{"get_bytes":100975,"get_count":135,"put_bytes":104693,"put_count":157}} +{"i":98,"series":1,"stats":{"get_bytes":99863,"get_count":133,"put_bytes":103356,"put_count":152}} +{"i":99,"series":1,"stats":{"get_bytes":103299,"get_count":135,"put_bytes":106661,"put_count":152}} +{"i":0,"series":2,"stats":{"get_bytes":106272,"get_count":140,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":104139,"get_count":131,"put_bytes":107776,"put_count":152}} +{"i":2,"series":2,"stats":{"get_bytes":103514,"get_count":132,"put_bytes":107501,"put_count":158}} +{"i":3,"series":2,"stats":{"get_bytes":105431,"get_count":133,"put_bytes":109496,"put_count":160}} +{"i":4,"series":2,"stats":{"get_bytes":99321,"get_count":130,"put_bytes":103237,"put_count":155}} +{"i":5,"series":2,"stats":{"get_bytes":104827,"get_count":131,"put_bytes":108888,"put_count":158}} +{"i":6,"series":2,"stats":{"get_bytes":108351,"get_count":133,"put_bytes":112277,"put_count":158}} +{"i":7,"series":2,"stats":{"get_bytes":108257,"get_count":134,"put_bytes":112325,"put_count":161}} +{"i":8,"series":2,"stats":{"get_bytes":110773,"get_count":138,"put_bytes":114692,"put_count":163}} +{"i":9,"series":2,"stats":{"get_bytes":109402,"get_count":134,"put_bytes":113183,"put_count":157}} +{"i":10,"series":2,"stats":{"get_bytes":105106,"get_count":131,"put_bytes":108886,"put_count":154}} +{"i":11,"series":2,"stats":{"get_bytes":108427,"get_count":137,"put_bytes":111996,"put_count":157}} +{"i":12,"series":2,"stats":{"get_bytes":106116,"get_count":130,"put_bytes":109951,"put_count":154}} +{"i":13,"series":2,"stats":{"get_bytes":105888,"get_count":132,"put_bytes":109742,"put_count":156}} +{"i":14,"series":2,"stats":{"get_bytes":109993,"get_count":135,"put_bytes":114037,"put_count":162}} +{"i":15,"series":2,"stats":{"get_bytes":97883,"get_count":127,"put_bytes":101239,"put_count":144}} +{"i":16,"series":2,"stats":{"get_bytes":109591,"get_count":134,"put_bytes":113309,"put_count":156}} +{"i":17,"series":2,"stats":{"get_bytes":110968,"get_count":136,"put_bytes":114534,"put_count":156}} +{"i":18,"series":2,"stats":{"get_bytes":111564,"get_count":135,"put_bytes":115698,"put_count":163}} +{"i":19,"series":2,"stats":{"get_bytes":108183,"get_count":129,"put_bytes":111476,"put_count":145}} +{"i":20,"series":2,"stats":{"get_bytes":108872,"get_count":132,"put_bytes":112719,"put_count":156}} +{"i":21,"series":2,"stats":{"get_bytes":115281,"get_count":139,"put_bytes":119275,"put_count":165}} +{"i":22,"series":2,"stats":{"get_bytes":111999,"get_count":135,"put_bytes":115919,"put_count":160}} +{"i":23,"series":2,"stats":{"get_bytes":109424,"get_count":131,"put_bytes":113136,"put_count":153}} +{"i":24,"series":2,"stats":{"get_bytes":111843,"get_count":131,"put_bytes":115825,"put_count":157}} +{"i":25,"series":2,"stats":{"get_bytes":112963,"get_count":129,"put_bytes":116793,"put_count":153}} +{"i":26,"series":2,"stats":{"get_bytes":115012,"get_count":139,"put_bytes":118518,"put_count":158}} +{"i":27,"series":2,"stats":{"get_bytes":116226,"get_count":138,"put_bytes":119983,"put_count":161}} +{"i":28,"series":2,"stats":{"get_bytes":114351,"get_count":135,"put_bytes":118611,"put_count":165}} +{"i":29,"series":2,"stats":{"get_bytes":107988,"get_count":133,"put_bytes":111496,"put_count":152}} +{"i":30,"series":2,"stats":{"get_bytes":112770,"get_count":136,"put_bytes":116902,"put_count":164}} +{"i":31,"series":2,"stats":{"get_bytes":115528,"get_count":137,"put_bytes":119726,"put_count":166}} +{"i":32,"series":2,"stats":{"get_bytes":116339,"get_count":139,"put_bytes":120818,"put_count":172}} +{"i":33,"series":2,"stats":{"get_bytes":109765,"get_count":131,"put_bytes":114023,"put_count":161}} +{"i":34,"series":2,"stats":{"get_bytes":115903,"get_count":136,"put_bytes":119887,"put_count":162}} +{"i":35,"series":2,"stats":{"get_bytes":117199,"get_count":133,"put_bytes":121241,"put_count":160}} +{"i":36,"series":2,"stats":{"get_bytes":116435,"get_count":134,"put_bytes":121129,"put_count":170}} +{"i":37,"series":2,"stats":{"get_bytes":114810,"get_count":132,"put_bytes":118734,"put_count":157}} +{"i":38,"series":2,"stats":{"get_bytes":112792,"get_count":131,"put_bytes":117423,"put_count":166}} +{"i":39,"series":2,"stats":{"get_bytes":110344,"get_count":131,"put_bytes":113906,"put_count":151}} +{"i":40,"series":2,"stats":{"get_bytes":111377,"get_count":131,"put_bytes":115433,"put_count":158}} +{"i":41,"series":2,"stats":{"get_bytes":115744,"get_count":143,"put_bytes":119593,"put_count":167}} +{"i":42,"series":2,"stats":{"get_bytes":110677,"get_count":127,"put_bytes":114950,"put_count":157}} +{"i":43,"series":2,"stats":{"get_bytes":117708,"get_count":131,"put_bytes":121270,"put_count":151}} +{"i":44,"series":2,"stats":{"get_bytes":117734,"get_count":137,"put_bytes":121516,"put_count":160}} +{"i":45,"series":2,"stats":{"get_bytes":121388,"get_count":142,"put_bytes":125530,"put_count":170}} +{"i":46,"series":2,"stats":{"get_bytes":120221,"get_count":140,"put_bytes":123856,"put_count":161}} +{"i":47,"series":2,"stats":{"get_bytes":118483,"get_count":135,"put_bytes":122960,"put_count":168}} +{"i":48,"series":2,"stats":{"get_bytes":118504,"get_count":137,"put_bytes":122986,"put_count":170}} +{"i":49,"series":2,"stats":{"get_bytes":119796,"get_count":136,"put_bytes":123852,"put_count":163}} +{"i":50,"series":2,"stats":{"get_bytes":117451,"get_count":136,"put_bytes":121236,"put_count":159}} +{"i":51,"series":2,"stats":{"get_bytes":120115,"get_count":139,"put_bytes":123469,"put_count":156}} +{"i":52,"series":2,"stats":{"get_bytes":122231,"get_count":144,"put_bytes":126707,"put_count":177}} +{"i":53,"series":2,"stats":{"get_bytes":115835,"get_count":129,"put_bytes":119820,"put_count":155}} +{"i":54,"series":2,"stats":{"get_bytes":118455,"get_count":142,"put_bytes":122592,"put_count":170}} +{"i":55,"series":2,"stats":{"get_bytes":118066,"get_count":130,"put_bytes":122054,"put_count":156}} +{"i":56,"series":2,"stats":{"get_bytes":120400,"get_count":140,"put_bytes":124809,"put_count":172}} +{"i":57,"series":2,"stats":{"get_bytes":120361,"get_count":139,"put_bytes":124610,"put_count":169}} +{"i":58,"series":2,"stats":{"get_bytes":126864,"get_count":139,"put_bytes":131421,"put_count":173}} +{"i":59,"series":2,"stats":{"get_bytes":125764,"get_count":133,"put_bytes":130867,"put_count":175}} +{"i":60,"series":2,"stats":{"get_bytes":127198,"get_count":136,"put_bytes":131813,"put_count":171}} +{"i":61,"series":2,"stats":{"get_bytes":126657,"get_count":134,"put_bytes":131498,"put_count":172}} +{"i":62,"series":2,"stats":{"get_bytes":120411,"get_count":131,"put_bytes":124757,"put_count":162}} +{"i":63,"series":2,"stats":{"get_bytes":118027,"get_count":138,"put_bytes":122564,"put_count":172}} +{"i":64,"series":2,"stats":{"get_bytes":124715,"get_count":139,"put_bytes":129125,"put_count":171}} +{"i":65,"series":2,"stats":{"get_bytes":125516,"get_count":137,"put_bytes":129996,"put_count":170}} +{"i":66,"series":2,"stats":{"get_bytes":125363,"get_count":138,"put_bytes":129570,"put_count":167}} +{"i":67,"series":2,"stats":{"get_bytes":127780,"get_count":144,"put_bytes":131907,"put_count":172}} +{"i":68,"series":2,"stats":{"get_bytes":128249,"get_count":140,"put_bytes":132509,"put_count":170}} +{"i":69,"series":2,"stats":{"get_bytes":124544,"get_count":140,"put_bytes":129085,"put_count":174}} +{"i":70,"series":2,"stats":{"get_bytes":128376,"get_count":142,"put_bytes":132154,"put_count":165}} +{"i":71,"series":2,"stats":{"get_bytes":125358,"get_count":134,"put_bytes":129623,"put_count":164}} +{"i":72,"series":2,"stats":{"get_bytes":123856,"get_count":135,"put_bytes":128127,"put_count":165}} +{"i":73,"series":2,"stats":{"get_bytes":125370,"get_count":140,"put_bytes":130274,"put_count":179}} +{"i":74,"series":2,"stats":{"get_bytes":122818,"get_count":134,"put_bytes":127304,"put_count":167}} +{"i":75,"series":2,"stats":{"get_bytes":124449,"get_count":141,"put_bytes":128508,"put_count":168}} +{"i":76,"series":2,"stats":{"get_bytes":126823,"get_count":142,"put_bytes":131449,"put_count":177}} +{"i":77,"series":2,"stats":{"get_bytes":131220,"get_count":135,"put_bytes":135689,"put_count":168}} +{"i":78,"series":2,"stats":{"get_bytes":129206,"get_count":140,"put_bytes":133557,"put_count":171}} +{"i":79,"series":2,"stats":{"get_bytes":128769,"get_count":134,"put_bytes":133402,"put_count":169}} +{"i":80,"series":2,"stats":{"get_bytes":131230,"get_count":143,"put_bytes":135914,"put_count":179}} +{"i":81,"series":2,"stats":{"get_bytes":125465,"get_count":137,"put_bytes":129672,"put_count":166}} +{"i":82,"series":2,"stats":{"get_bytes":132500,"get_count":145,"put_bytes":136842,"put_count":176}} +{"i":83,"series":2,"stats":{"get_bytes":129060,"get_count":136,"put_bytes":133969,"put_count":175}} +{"i":84,"series":2,"stats":{"get_bytes":128101,"get_count":136,"put_bytes":132284,"put_count":165}} +{"i":85,"series":2,"stats":{"get_bytes":128646,"get_count":137,"put_bytes":133129,"put_count":170}} +{"i":86,"series":2,"stats":{"get_bytes":125988,"get_count":142,"put_bytes":130618,"put_count":177}} +{"i":87,"series":2,"stats":{"get_bytes":134311,"get_count":140,"put_bytes":138871,"put_count":174}} +{"i":88,"series":2,"stats":{"get_bytes":128634,"get_count":138,"put_bytes":133038,"put_count":170}} +{"i":89,"series":2,"stats":{"get_bytes":126113,"get_count":140,"put_bytes":130182,"put_count":167}} +{"i":90,"series":2,"stats":{"get_bytes":127919,"get_count":141,"put_bytes":131979,"put_count":168}} +{"i":91,"series":2,"stats":{"get_bytes":134268,"get_count":143,"put_bytes":138279,"put_count":170}} +{"i":92,"series":2,"stats":{"get_bytes":128724,"get_count":140,"put_bytes":133045,"put_count":171}} +{"i":93,"series":2,"stats":{"get_bytes":129847,"get_count":140,"put_bytes":134714,"put_count":179}} +{"i":94,"series":2,"stats":{"get_bytes":132404,"get_count":143,"put_bytes":136603,"put_count":172}} +{"i":95,"series":2,"stats":{"get_bytes":129791,"get_count":142,"put_bytes":133989,"put_count":171}} +{"i":96,"series":2,"stats":{"get_bytes":133858,"get_count":142,"put_bytes":138391,"put_count":176}} +{"i":97,"series":2,"stats":{"get_bytes":133425,"get_count":141,"put_bytes":137983,"put_count":175}} +{"i":98,"series":2,"stats":{"get_bytes":136800,"get_count":146,"put_bytes":140655,"put_count":170}} +{"i":99,"series":2,"stats":{"get_bytes":126455,"get_count":139,"put_bytes":130936,"put_count":172}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.png b/actors/evm/tests/measurements/mapping_add_n100.png index 769923461e8be49724ef93cabb92337e35f4737a..8e61e90e3097ba6611654c4456751dee91bbe569 100644 GIT binary patch delta 19983 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 delta 19922 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 bF--aK+k;K7BPY_615w=B-p#I-9+dJwahpOP diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline index d876ff2d4..6888c5b7d 100644 --- a/actors/evm/tests/measurements/mapping_overwrite.jsonline +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -1,200 +1,200 @@ -{"i":0,"series":1,"stats":{"get_bytes":13366,"get_count":20,"put_bytes":11358,"put_count":19}} -{"i":1,"series":1,"stats":{"get_bytes":14284,"get_count":15,"put_bytes":12276,"put_count":14}} -{"i":2,"series":1,"stats":{"get_bytes":12589,"get_count":14,"put_bytes":10581,"put_count":13}} -{"i":3,"series":1,"stats":{"get_bytes":14173,"get_count":20,"put_bytes":12165,"put_count":19}} -{"i":4,"series":1,"stats":{"get_bytes":13509,"get_count":18,"put_bytes":11501,"put_count":17}} -{"i":5,"series":1,"stats":{"get_bytes":14337,"get_count":16,"put_bytes":12329,"put_count":15}} -{"i":6,"series":1,"stats":{"get_bytes":14545,"get_count":20,"put_bytes":12537,"put_count":19}} -{"i":7,"series":1,"stats":{"get_bytes":14203,"get_count":17,"put_bytes":12195,"put_count":16}} -{"i":8,"series":1,"stats":{"get_bytes":14492,"get_count":21,"put_bytes":12484,"put_count":20}} -{"i":9,"series":1,"stats":{"get_bytes":13887,"get_count":16,"put_bytes":11879,"put_count":15}} -{"i":10,"series":1,"stats":{"get_bytes":15382,"get_count":20,"put_bytes":13374,"put_count":19}} -{"i":11,"series":1,"stats":{"get_bytes":12833,"get_count":14,"put_bytes":10825,"put_count":13}} -{"i":12,"series":1,"stats":{"get_bytes":13325,"get_count":18,"put_bytes":11317,"put_count":17}} -{"i":13,"series":1,"stats":{"get_bytes":14874,"get_count":18,"put_bytes":12866,"put_count":17}} -{"i":14,"series":1,"stats":{"get_bytes":14161,"get_count":18,"put_bytes":12153,"put_count":17}} -{"i":15,"series":1,"stats":{"get_bytes":13431,"get_count":17,"put_bytes":11423,"put_count":16}} -{"i":16,"series":1,"stats":{"get_bytes":11520,"get_count":13,"put_bytes":9512,"put_count":12}} -{"i":17,"series":1,"stats":{"get_bytes":12385,"get_count":17,"put_bytes":10377,"put_count":16}} -{"i":18,"series":1,"stats":{"get_bytes":15139,"get_count":18,"put_bytes":13131,"put_count":17}} -{"i":19,"series":1,"stats":{"get_bytes":14079,"get_count":19,"put_bytes":12071,"put_count":18}} -{"i":20,"series":1,"stats":{"get_bytes":14698,"get_count":20,"put_bytes":12690,"put_count":19}} -{"i":21,"series":1,"stats":{"get_bytes":13166,"get_count":17,"put_bytes":11158,"put_count":16}} -{"i":22,"series":1,"stats":{"get_bytes":15290,"get_count":19,"put_bytes":13282,"put_count":18}} -{"i":23,"series":1,"stats":{"get_bytes":12502,"get_count":19,"put_bytes":10494,"put_count":18}} -{"i":24,"series":1,"stats":{"get_bytes":14282,"get_count":18,"put_bytes":12274,"put_count":17}} -{"i":25,"series":1,"stats":{"get_bytes":14545,"get_count":18,"put_bytes":12537,"put_count":17}} -{"i":26,"series":1,"stats":{"get_bytes":13656,"get_count":18,"put_bytes":11648,"put_count":17}} -{"i":27,"series":1,"stats":{"get_bytes":13085,"get_count":14,"put_bytes":11077,"put_count":13}} -{"i":28,"series":1,"stats":{"get_bytes":14860,"get_count":19,"put_bytes":12852,"put_count":18}} -{"i":29,"series":1,"stats":{"get_bytes":14523,"get_count":18,"put_bytes":12515,"put_count":17}} -{"i":30,"series":1,"stats":{"get_bytes":15425,"get_count":21,"put_bytes":13417,"put_count":20}} -{"i":31,"series":1,"stats":{"get_bytes":13403,"get_count":16,"put_bytes":11395,"put_count":15}} -{"i":32,"series":1,"stats":{"get_bytes":14722,"get_count":19,"put_bytes":12714,"put_count":18}} -{"i":33,"series":1,"stats":{"get_bytes":14087,"get_count":19,"put_bytes":12079,"put_count":18}} -{"i":34,"series":1,"stats":{"get_bytes":13567,"get_count":15,"put_bytes":11559,"put_count":14}} -{"i":35,"series":1,"stats":{"get_bytes":14193,"get_count":19,"put_bytes":12185,"put_count":18}} -{"i":36,"series":1,"stats":{"get_bytes":15228,"get_count":19,"put_bytes":13220,"put_count":18}} -{"i":37,"series":1,"stats":{"get_bytes":13067,"get_count":16,"put_bytes":11059,"put_count":15}} -{"i":38,"series":1,"stats":{"get_bytes":12405,"get_count":16,"put_bytes":10397,"put_count":15}} -{"i":39,"series":1,"stats":{"get_bytes":14263,"get_count":18,"put_bytes":12255,"put_count":17}} -{"i":40,"series":1,"stats":{"get_bytes":14016,"get_count":22,"put_bytes":12008,"put_count":21}} -{"i":41,"series":1,"stats":{"get_bytes":15827,"get_count":20,"put_bytes":13819,"put_count":19}} -{"i":42,"series":1,"stats":{"get_bytes":14507,"get_count":20,"put_bytes":12499,"put_count":19}} -{"i":43,"series":1,"stats":{"get_bytes":14744,"get_count":17,"put_bytes":12736,"put_count":16}} -{"i":44,"series":1,"stats":{"get_bytes":15571,"get_count":21,"put_bytes":13563,"put_count":20}} -{"i":45,"series":1,"stats":{"get_bytes":13034,"get_count":17,"put_bytes":11026,"put_count":16}} -{"i":46,"series":1,"stats":{"get_bytes":14354,"get_count":18,"put_bytes":12346,"put_count":17}} -{"i":47,"series":1,"stats":{"get_bytes":15288,"get_count":19,"put_bytes":13280,"put_count":18}} -{"i":48,"series":1,"stats":{"get_bytes":14553,"get_count":20,"put_bytes":12545,"put_count":19}} -{"i":49,"series":1,"stats":{"get_bytes":14037,"get_count":20,"put_bytes":12029,"put_count":19}} -{"i":50,"series":1,"stats":{"get_bytes":15066,"get_count":19,"put_bytes":13058,"put_count":18}} -{"i":51,"series":1,"stats":{"get_bytes":12913,"get_count":15,"put_bytes":10905,"put_count":14}} -{"i":52,"series":1,"stats":{"get_bytes":13188,"get_count":17,"put_bytes":11180,"put_count":16}} -{"i":53,"series":1,"stats":{"get_bytes":14565,"get_count":18,"put_bytes":12557,"put_count":17}} -{"i":54,"series":1,"stats":{"get_bytes":14820,"get_count":22,"put_bytes":12812,"put_count":21}} -{"i":55,"series":1,"stats":{"get_bytes":12411,"get_count":17,"put_bytes":10403,"put_count":16}} -{"i":56,"series":1,"stats":{"get_bytes":13456,"get_count":17,"put_bytes":11448,"put_count":16}} -{"i":57,"series":1,"stats":{"get_bytes":15150,"get_count":17,"put_bytes":13142,"put_count":16}} -{"i":58,"series":1,"stats":{"get_bytes":15452,"get_count":21,"put_bytes":13444,"put_count":20}} -{"i":59,"series":1,"stats":{"get_bytes":14582,"get_count":16,"put_bytes":12574,"put_count":15}} -{"i":60,"series":1,"stats":{"get_bytes":14558,"get_count":18,"put_bytes":12550,"put_count":17}} -{"i":61,"series":1,"stats":{"get_bytes":16006,"get_count":23,"put_bytes":13998,"put_count":22}} -{"i":62,"series":1,"stats":{"get_bytes":14259,"get_count":18,"put_bytes":12251,"put_count":17}} -{"i":63,"series":1,"stats":{"get_bytes":15329,"get_count":19,"put_bytes":13321,"put_count":18}} -{"i":64,"series":1,"stats":{"get_bytes":13647,"get_count":19,"put_bytes":11639,"put_count":18}} -{"i":65,"series":1,"stats":{"get_bytes":13970,"get_count":19,"put_bytes":11962,"put_count":18}} -{"i":66,"series":1,"stats":{"get_bytes":12562,"get_count":16,"put_bytes":10554,"put_count":15}} -{"i":67,"series":1,"stats":{"get_bytes":14494,"get_count":20,"put_bytes":12486,"put_count":19}} -{"i":68,"series":1,"stats":{"get_bytes":14075,"get_count":17,"put_bytes":12067,"put_count":16}} -{"i":69,"series":1,"stats":{"get_bytes":12469,"get_count":17,"put_bytes":10461,"put_count":16}} -{"i":70,"series":1,"stats":{"get_bytes":15471,"get_count":18,"put_bytes":13463,"put_count":17}} -{"i":71,"series":1,"stats":{"get_bytes":11744,"get_count":16,"put_bytes":9736,"put_count":15}} -{"i":72,"series":1,"stats":{"get_bytes":13891,"get_count":15,"put_bytes":11883,"put_count":14}} -{"i":73,"series":1,"stats":{"get_bytes":14421,"get_count":19,"put_bytes":12413,"put_count":18}} -{"i":74,"series":1,"stats":{"get_bytes":14685,"get_count":20,"put_bytes":12677,"put_count":19}} -{"i":75,"series":1,"stats":{"get_bytes":13023,"get_count":18,"put_bytes":11015,"put_count":17}} -{"i":76,"series":1,"stats":{"get_bytes":14428,"get_count":19,"put_bytes":12420,"put_count":18}} -{"i":77,"series":1,"stats":{"get_bytes":15152,"get_count":18,"put_bytes":13144,"put_count":17}} -{"i":78,"series":1,"stats":{"get_bytes":14288,"get_count":18,"put_bytes":12280,"put_count":17}} -{"i":79,"series":1,"stats":{"get_bytes":14565,"get_count":21,"put_bytes":12557,"put_count":20}} -{"i":80,"series":1,"stats":{"get_bytes":13800,"get_count":18,"put_bytes":11792,"put_count":17}} -{"i":81,"series":1,"stats":{"get_bytes":13218,"get_count":18,"put_bytes":11210,"put_count":17}} -{"i":82,"series":1,"stats":{"get_bytes":13143,"get_count":19,"put_bytes":11135,"put_count":18}} -{"i":83,"series":1,"stats":{"get_bytes":15617,"get_count":17,"put_bytes":13609,"put_count":16}} -{"i":84,"series":1,"stats":{"get_bytes":14168,"get_count":18,"put_bytes":12160,"put_count":17}} -{"i":85,"series":1,"stats":{"get_bytes":13721,"get_count":17,"put_bytes":11713,"put_count":16}} -{"i":86,"series":1,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":12070,"put_count":17}} -{"i":87,"series":1,"stats":{"get_bytes":13789,"get_count":16,"put_bytes":11781,"put_count":15}} -{"i":88,"series":1,"stats":{"get_bytes":15237,"get_count":20,"put_bytes":13229,"put_count":19}} -{"i":89,"series":1,"stats":{"get_bytes":15435,"get_count":18,"put_bytes":13427,"put_count":17}} -{"i":90,"series":1,"stats":{"get_bytes":14758,"get_count":19,"put_bytes":12750,"put_count":18}} -{"i":91,"series":1,"stats":{"get_bytes":14740,"get_count":18,"put_bytes":12732,"put_count":17}} -{"i":92,"series":1,"stats":{"get_bytes":13024,"get_count":16,"put_bytes":11016,"put_count":15}} -{"i":93,"series":1,"stats":{"get_bytes":14536,"get_count":18,"put_bytes":12528,"put_count":17}} -{"i":94,"series":1,"stats":{"get_bytes":13221,"get_count":18,"put_bytes":11213,"put_count":17}} -{"i":95,"series":1,"stats":{"get_bytes":12965,"get_count":17,"put_bytes":10957,"put_count":16}} -{"i":96,"series":1,"stats":{"get_bytes":13004,"get_count":19,"put_bytes":10996,"put_count":18}} -{"i":97,"series":1,"stats":{"get_bytes":13260,"get_count":16,"put_bytes":11252,"put_count":15}} -{"i":98,"series":1,"stats":{"get_bytes":14322,"get_count":20,"put_bytes":12314,"put_count":19}} -{"i":99,"series":1,"stats":{"get_bytes":14216,"get_count":21,"put_bytes":12208,"put_count":20}} -{"i":0,"series":2,"stats":{"get_bytes":13366,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":1,"series":2,"stats":{"get_bytes":14284,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":2,"series":2,"stats":{"get_bytes":12589,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":3,"series":2,"stats":{"get_bytes":14173,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":4,"series":2,"stats":{"get_bytes":13509,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":5,"series":2,"stats":{"get_bytes":14337,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":6,"series":2,"stats":{"get_bytes":14545,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":7,"series":2,"stats":{"get_bytes":14203,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":8,"series":2,"stats":{"get_bytes":14492,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":9,"series":2,"stats":{"get_bytes":13887,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":10,"series":2,"stats":{"get_bytes":15382,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":11,"series":2,"stats":{"get_bytes":12833,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":12,"series":2,"stats":{"get_bytes":13325,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":13,"series":2,"stats":{"get_bytes":14874,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":14,"series":2,"stats":{"get_bytes":14161,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":15,"series":2,"stats":{"get_bytes":13431,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":16,"series":2,"stats":{"get_bytes":11520,"get_count":13,"put_bytes":0,"put_count":0}} -{"i":17,"series":2,"stats":{"get_bytes":12385,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":18,"series":2,"stats":{"get_bytes":15139,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":19,"series":2,"stats":{"get_bytes":14079,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":20,"series":2,"stats":{"get_bytes":14698,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":21,"series":2,"stats":{"get_bytes":13166,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":22,"series":2,"stats":{"get_bytes":15290,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":23,"series":2,"stats":{"get_bytes":12502,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":24,"series":2,"stats":{"get_bytes":14282,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":25,"series":2,"stats":{"get_bytes":14545,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":26,"series":2,"stats":{"get_bytes":13656,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":27,"series":2,"stats":{"get_bytes":13085,"get_count":14,"put_bytes":0,"put_count":0}} -{"i":28,"series":2,"stats":{"get_bytes":14860,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":29,"series":2,"stats":{"get_bytes":14523,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":30,"series":2,"stats":{"get_bytes":15425,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":31,"series":2,"stats":{"get_bytes":13403,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":32,"series":2,"stats":{"get_bytes":14722,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":33,"series":2,"stats":{"get_bytes":14087,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":34,"series":2,"stats":{"get_bytes":13567,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":35,"series":2,"stats":{"get_bytes":14193,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":36,"series":2,"stats":{"get_bytes":15228,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":37,"series":2,"stats":{"get_bytes":13067,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":38,"series":2,"stats":{"get_bytes":12405,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":39,"series":2,"stats":{"get_bytes":14263,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":40,"series":2,"stats":{"get_bytes":14016,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":41,"series":2,"stats":{"get_bytes":15827,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":42,"series":2,"stats":{"get_bytes":14507,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":43,"series":2,"stats":{"get_bytes":14744,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":44,"series":2,"stats":{"get_bytes":15571,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":45,"series":2,"stats":{"get_bytes":13034,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":46,"series":2,"stats":{"get_bytes":14354,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":47,"series":2,"stats":{"get_bytes":15288,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":48,"series":2,"stats":{"get_bytes":14553,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":49,"series":2,"stats":{"get_bytes":14037,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":50,"series":2,"stats":{"get_bytes":15066,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":51,"series":2,"stats":{"get_bytes":12913,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":52,"series":2,"stats":{"get_bytes":13188,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":53,"series":2,"stats":{"get_bytes":14565,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":54,"series":2,"stats":{"get_bytes":14820,"get_count":22,"put_bytes":0,"put_count":0}} -{"i":55,"series":2,"stats":{"get_bytes":12411,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":56,"series":2,"stats":{"get_bytes":13456,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":57,"series":2,"stats":{"get_bytes":15150,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":58,"series":2,"stats":{"get_bytes":15452,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":59,"series":2,"stats":{"get_bytes":14582,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":60,"series":2,"stats":{"get_bytes":14558,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":61,"series":2,"stats":{"get_bytes":16006,"get_count":23,"put_bytes":0,"put_count":0}} -{"i":62,"series":2,"stats":{"get_bytes":14259,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":63,"series":2,"stats":{"get_bytes":15329,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":64,"series":2,"stats":{"get_bytes":13647,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":65,"series":2,"stats":{"get_bytes":13970,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":66,"series":2,"stats":{"get_bytes":12562,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":67,"series":2,"stats":{"get_bytes":14494,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":68,"series":2,"stats":{"get_bytes":14075,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":69,"series":2,"stats":{"get_bytes":12469,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":70,"series":2,"stats":{"get_bytes":15471,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":71,"series":2,"stats":{"get_bytes":11744,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":72,"series":2,"stats":{"get_bytes":13891,"get_count":15,"put_bytes":0,"put_count":0}} -{"i":73,"series":2,"stats":{"get_bytes":14421,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":74,"series":2,"stats":{"get_bytes":14685,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":75,"series":2,"stats":{"get_bytes":13023,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":76,"series":2,"stats":{"get_bytes":14428,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":77,"series":2,"stats":{"get_bytes":15152,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":78,"series":2,"stats":{"get_bytes":14288,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":79,"series":2,"stats":{"get_bytes":14565,"get_count":21,"put_bytes":0,"put_count":0}} -{"i":80,"series":2,"stats":{"get_bytes":13800,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":81,"series":2,"stats":{"get_bytes":13218,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":82,"series":2,"stats":{"get_bytes":13143,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":83,"series":2,"stats":{"get_bytes":15617,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":84,"series":2,"stats":{"get_bytes":14168,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":85,"series":2,"stats":{"get_bytes":13721,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":86,"series":2,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":87,"series":2,"stats":{"get_bytes":13789,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":88,"series":2,"stats":{"get_bytes":15237,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":89,"series":2,"stats":{"get_bytes":15435,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":90,"series":2,"stats":{"get_bytes":14758,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":91,"series":2,"stats":{"get_bytes":14740,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":92,"series":2,"stats":{"get_bytes":13024,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":93,"series":2,"stats":{"get_bytes":14536,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":94,"series":2,"stats":{"get_bytes":13221,"get_count":18,"put_bytes":0,"put_count":0}} -{"i":95,"series":2,"stats":{"get_bytes":12965,"get_count":17,"put_bytes":0,"put_count":0}} -{"i":96,"series":2,"stats":{"get_bytes":13004,"get_count":19,"put_bytes":0,"put_count":0}} -{"i":97,"series":2,"stats":{"get_bytes":13260,"get_count":16,"put_bytes":0,"put_count":0}} -{"i":98,"series":2,"stats":{"get_bytes":14322,"get_count":20,"put_bytes":0,"put_count":0}} -{"i":99,"series":2,"stats":{"get_bytes":14216,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":13283,"get_count":20,"put_bytes":11275,"put_count":19}} +{"i":1,"series":1,"stats":{"get_bytes":14201,"get_count":15,"put_bytes":12193,"put_count":14}} +{"i":2,"series":1,"stats":{"get_bytes":12506,"get_count":14,"put_bytes":10498,"put_count":13}} +{"i":3,"series":1,"stats":{"get_bytes":14090,"get_count":20,"put_bytes":12082,"put_count":19}} +{"i":4,"series":1,"stats":{"get_bytes":13426,"get_count":18,"put_bytes":11418,"put_count":17}} +{"i":5,"series":1,"stats":{"get_bytes":14254,"get_count":16,"put_bytes":12246,"put_count":15}} +{"i":6,"series":1,"stats":{"get_bytes":14462,"get_count":20,"put_bytes":12454,"put_count":19}} +{"i":7,"series":1,"stats":{"get_bytes":14120,"get_count":17,"put_bytes":12112,"put_count":16}} +{"i":8,"series":1,"stats":{"get_bytes":14409,"get_count":21,"put_bytes":12401,"put_count":20}} +{"i":9,"series":1,"stats":{"get_bytes":13804,"get_count":16,"put_bytes":11796,"put_count":15}} +{"i":10,"series":1,"stats":{"get_bytes":15299,"get_count":20,"put_bytes":13291,"put_count":19}} +{"i":11,"series":1,"stats":{"get_bytes":12750,"get_count":14,"put_bytes":10742,"put_count":13}} +{"i":12,"series":1,"stats":{"get_bytes":13242,"get_count":18,"put_bytes":11234,"put_count":17}} +{"i":13,"series":1,"stats":{"get_bytes":14791,"get_count":18,"put_bytes":12783,"put_count":17}} +{"i":14,"series":1,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":12070,"put_count":17}} +{"i":15,"series":1,"stats":{"get_bytes":13348,"get_count":17,"put_bytes":11340,"put_count":16}} +{"i":16,"series":1,"stats":{"get_bytes":11437,"get_count":13,"put_bytes":9429,"put_count":12}} +{"i":17,"series":1,"stats":{"get_bytes":12302,"get_count":17,"put_bytes":10294,"put_count":16}} +{"i":18,"series":1,"stats":{"get_bytes":15056,"get_count":18,"put_bytes":13048,"put_count":17}} +{"i":19,"series":1,"stats":{"get_bytes":13996,"get_count":19,"put_bytes":11988,"put_count":18}} +{"i":20,"series":1,"stats":{"get_bytes":14615,"get_count":20,"put_bytes":12607,"put_count":19}} +{"i":21,"series":1,"stats":{"get_bytes":13083,"get_count":17,"put_bytes":11075,"put_count":16}} +{"i":22,"series":1,"stats":{"get_bytes":15207,"get_count":19,"put_bytes":13199,"put_count":18}} +{"i":23,"series":1,"stats":{"get_bytes":12419,"get_count":19,"put_bytes":10411,"put_count":18}} +{"i":24,"series":1,"stats":{"get_bytes":14199,"get_count":18,"put_bytes":12191,"put_count":17}} +{"i":25,"series":1,"stats":{"get_bytes":14462,"get_count":18,"put_bytes":12454,"put_count":17}} +{"i":26,"series":1,"stats":{"get_bytes":13573,"get_count":18,"put_bytes":11565,"put_count":17}} +{"i":27,"series":1,"stats":{"get_bytes":13002,"get_count":14,"put_bytes":10994,"put_count":13}} +{"i":28,"series":1,"stats":{"get_bytes":14777,"get_count":19,"put_bytes":12769,"put_count":18}} +{"i":29,"series":1,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":12432,"put_count":17}} +{"i":30,"series":1,"stats":{"get_bytes":15342,"get_count":21,"put_bytes":13334,"put_count":20}} +{"i":31,"series":1,"stats":{"get_bytes":13320,"get_count":16,"put_bytes":11312,"put_count":15}} +{"i":32,"series":1,"stats":{"get_bytes":14639,"get_count":19,"put_bytes":12631,"put_count":18}} +{"i":33,"series":1,"stats":{"get_bytes":14004,"get_count":19,"put_bytes":11996,"put_count":18}} +{"i":34,"series":1,"stats":{"get_bytes":13484,"get_count":15,"put_bytes":11476,"put_count":14}} +{"i":35,"series":1,"stats":{"get_bytes":14110,"get_count":19,"put_bytes":12102,"put_count":18}} +{"i":36,"series":1,"stats":{"get_bytes":15145,"get_count":19,"put_bytes":13137,"put_count":18}} +{"i":37,"series":1,"stats":{"get_bytes":12984,"get_count":16,"put_bytes":10976,"put_count":15}} +{"i":38,"series":1,"stats":{"get_bytes":12322,"get_count":16,"put_bytes":10314,"put_count":15}} +{"i":39,"series":1,"stats":{"get_bytes":14180,"get_count":18,"put_bytes":12172,"put_count":17}} +{"i":40,"series":1,"stats":{"get_bytes":13933,"get_count":22,"put_bytes":11925,"put_count":21}} +{"i":41,"series":1,"stats":{"get_bytes":15744,"get_count":20,"put_bytes":13736,"put_count":19}} +{"i":42,"series":1,"stats":{"get_bytes":14424,"get_count":20,"put_bytes":12416,"put_count":19}} +{"i":43,"series":1,"stats":{"get_bytes":14661,"get_count":17,"put_bytes":12653,"put_count":16}} +{"i":44,"series":1,"stats":{"get_bytes":15488,"get_count":21,"put_bytes":13480,"put_count":20}} +{"i":45,"series":1,"stats":{"get_bytes":12951,"get_count":17,"put_bytes":10943,"put_count":16}} +{"i":46,"series":1,"stats":{"get_bytes":14271,"get_count":18,"put_bytes":12263,"put_count":17}} +{"i":47,"series":1,"stats":{"get_bytes":15205,"get_count":19,"put_bytes":13197,"put_count":18}} +{"i":48,"series":1,"stats":{"get_bytes":14470,"get_count":20,"put_bytes":12462,"put_count":19}} +{"i":49,"series":1,"stats":{"get_bytes":13954,"get_count":20,"put_bytes":11946,"put_count":19}} +{"i":50,"series":1,"stats":{"get_bytes":14983,"get_count":19,"put_bytes":12975,"put_count":18}} +{"i":51,"series":1,"stats":{"get_bytes":12830,"get_count":15,"put_bytes":10822,"put_count":14}} +{"i":52,"series":1,"stats":{"get_bytes":13105,"get_count":17,"put_bytes":11097,"put_count":16}} +{"i":53,"series":1,"stats":{"get_bytes":14482,"get_count":18,"put_bytes":12474,"put_count":17}} +{"i":54,"series":1,"stats":{"get_bytes":14737,"get_count":22,"put_bytes":12729,"put_count":21}} +{"i":55,"series":1,"stats":{"get_bytes":12328,"get_count":17,"put_bytes":10320,"put_count":16}} +{"i":56,"series":1,"stats":{"get_bytes":13373,"get_count":17,"put_bytes":11365,"put_count":16}} +{"i":57,"series":1,"stats":{"get_bytes":15067,"get_count":17,"put_bytes":13059,"put_count":16}} +{"i":58,"series":1,"stats":{"get_bytes":15369,"get_count":21,"put_bytes":13361,"put_count":20}} +{"i":59,"series":1,"stats":{"get_bytes":14499,"get_count":16,"put_bytes":12491,"put_count":15}} +{"i":60,"series":1,"stats":{"get_bytes":14475,"get_count":18,"put_bytes":12467,"put_count":17}} +{"i":61,"series":1,"stats":{"get_bytes":15923,"get_count":23,"put_bytes":13915,"put_count":22}} +{"i":62,"series":1,"stats":{"get_bytes":14176,"get_count":18,"put_bytes":12168,"put_count":17}} +{"i":63,"series":1,"stats":{"get_bytes":15246,"get_count":19,"put_bytes":13238,"put_count":18}} +{"i":64,"series":1,"stats":{"get_bytes":13564,"get_count":19,"put_bytes":11556,"put_count":18}} +{"i":65,"series":1,"stats":{"get_bytes":13887,"get_count":19,"put_bytes":11879,"put_count":18}} +{"i":66,"series":1,"stats":{"get_bytes":12479,"get_count":16,"put_bytes":10471,"put_count":15}} +{"i":67,"series":1,"stats":{"get_bytes":14411,"get_count":20,"put_bytes":12403,"put_count":19}} +{"i":68,"series":1,"stats":{"get_bytes":13992,"get_count":17,"put_bytes":11984,"put_count":16}} +{"i":69,"series":1,"stats":{"get_bytes":12386,"get_count":17,"put_bytes":10378,"put_count":16}} +{"i":70,"series":1,"stats":{"get_bytes":15388,"get_count":18,"put_bytes":13380,"put_count":17}} +{"i":71,"series":1,"stats":{"get_bytes":11661,"get_count":16,"put_bytes":9653,"put_count":15}} +{"i":72,"series":1,"stats":{"get_bytes":13808,"get_count":15,"put_bytes":11800,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":14338,"get_count":19,"put_bytes":12330,"put_count":18}} +{"i":74,"series":1,"stats":{"get_bytes":14602,"get_count":20,"put_bytes":12594,"put_count":19}} +{"i":75,"series":1,"stats":{"get_bytes":12940,"get_count":18,"put_bytes":10932,"put_count":17}} +{"i":76,"series":1,"stats":{"get_bytes":14345,"get_count":19,"put_bytes":12337,"put_count":18}} +{"i":77,"series":1,"stats":{"get_bytes":15069,"get_count":18,"put_bytes":13061,"put_count":17}} +{"i":78,"series":1,"stats":{"get_bytes":14205,"get_count":18,"put_bytes":12197,"put_count":17}} +{"i":79,"series":1,"stats":{"get_bytes":14482,"get_count":21,"put_bytes":12474,"put_count":20}} +{"i":80,"series":1,"stats":{"get_bytes":13717,"get_count":18,"put_bytes":11709,"put_count":17}} +{"i":81,"series":1,"stats":{"get_bytes":13135,"get_count":18,"put_bytes":11127,"put_count":17}} +{"i":82,"series":1,"stats":{"get_bytes":13060,"get_count":19,"put_bytes":11052,"put_count":18}} +{"i":83,"series":1,"stats":{"get_bytes":15534,"get_count":17,"put_bytes":13526,"put_count":16}} +{"i":84,"series":1,"stats":{"get_bytes":14085,"get_count":18,"put_bytes":12077,"put_count":17}} +{"i":85,"series":1,"stats":{"get_bytes":13638,"get_count":17,"put_bytes":11630,"put_count":16}} +{"i":86,"series":1,"stats":{"get_bytes":13995,"get_count":18,"put_bytes":11987,"put_count":17}} +{"i":87,"series":1,"stats":{"get_bytes":13706,"get_count":16,"put_bytes":11698,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":15154,"get_count":20,"put_bytes":13146,"put_count":19}} +{"i":89,"series":1,"stats":{"get_bytes":15352,"get_count":18,"put_bytes":13344,"put_count":17}} +{"i":90,"series":1,"stats":{"get_bytes":14675,"get_count":19,"put_bytes":12667,"put_count":18}} +{"i":91,"series":1,"stats":{"get_bytes":14657,"get_count":18,"put_bytes":12649,"put_count":17}} +{"i":92,"series":1,"stats":{"get_bytes":12941,"get_count":16,"put_bytes":10933,"put_count":15}} +{"i":93,"series":1,"stats":{"get_bytes":14453,"get_count":18,"put_bytes":12445,"put_count":17}} +{"i":94,"series":1,"stats":{"get_bytes":13138,"get_count":18,"put_bytes":11130,"put_count":17}} +{"i":95,"series":1,"stats":{"get_bytes":12882,"get_count":17,"put_bytes":10874,"put_count":16}} +{"i":96,"series":1,"stats":{"get_bytes":12921,"get_count":19,"put_bytes":10913,"put_count":18}} +{"i":97,"series":1,"stats":{"get_bytes":13177,"get_count":16,"put_bytes":11169,"put_count":15}} +{"i":98,"series":1,"stats":{"get_bytes":14239,"get_count":20,"put_bytes":12231,"put_count":19}} +{"i":99,"series":1,"stats":{"get_bytes":14133,"get_count":21,"put_bytes":12125,"put_count":20}} +{"i":0,"series":2,"stats":{"get_bytes":13283,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":14201,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":2,"series":2,"stats":{"get_bytes":12506,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":3,"series":2,"stats":{"get_bytes":14090,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":4,"series":2,"stats":{"get_bytes":13426,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":5,"series":2,"stats":{"get_bytes":14254,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":6,"series":2,"stats":{"get_bytes":14462,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":7,"series":2,"stats":{"get_bytes":14120,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":8,"series":2,"stats":{"get_bytes":14409,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":9,"series":2,"stats":{"get_bytes":13804,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":10,"series":2,"stats":{"get_bytes":15299,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":11,"series":2,"stats":{"get_bytes":12750,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":2,"stats":{"get_bytes":13242,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":13,"series":2,"stats":{"get_bytes":14791,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":14,"series":2,"stats":{"get_bytes":14078,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":15,"series":2,"stats":{"get_bytes":13348,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":16,"series":2,"stats":{"get_bytes":11437,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":17,"series":2,"stats":{"get_bytes":12302,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":18,"series":2,"stats":{"get_bytes":15056,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":19,"series":2,"stats":{"get_bytes":13996,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":20,"series":2,"stats":{"get_bytes":14615,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":21,"series":2,"stats":{"get_bytes":13083,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":2,"stats":{"get_bytes":15207,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":23,"series":2,"stats":{"get_bytes":12419,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":24,"series":2,"stats":{"get_bytes":14199,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":25,"series":2,"stats":{"get_bytes":14462,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":26,"series":2,"stats":{"get_bytes":13573,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":27,"series":2,"stats":{"get_bytes":13002,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":28,"series":2,"stats":{"get_bytes":14777,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":29,"series":2,"stats":{"get_bytes":14440,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":30,"series":2,"stats":{"get_bytes":15342,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":31,"series":2,"stats":{"get_bytes":13320,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":32,"series":2,"stats":{"get_bytes":14639,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":33,"series":2,"stats":{"get_bytes":14004,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":34,"series":2,"stats":{"get_bytes":13484,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":35,"series":2,"stats":{"get_bytes":14110,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":36,"series":2,"stats":{"get_bytes":15145,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":37,"series":2,"stats":{"get_bytes":12984,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":38,"series":2,"stats":{"get_bytes":12322,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":2,"stats":{"get_bytes":14180,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":40,"series":2,"stats":{"get_bytes":13933,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":41,"series":2,"stats":{"get_bytes":15744,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":42,"series":2,"stats":{"get_bytes":14424,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":43,"series":2,"stats":{"get_bytes":14661,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":44,"series":2,"stats":{"get_bytes":15488,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":45,"series":2,"stats":{"get_bytes":12951,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":46,"series":2,"stats":{"get_bytes":14271,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":47,"series":2,"stats":{"get_bytes":15205,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":48,"series":2,"stats":{"get_bytes":14470,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":49,"series":2,"stats":{"get_bytes":13954,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":50,"series":2,"stats":{"get_bytes":14983,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":51,"series":2,"stats":{"get_bytes":12830,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":2,"stats":{"get_bytes":13105,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":53,"series":2,"stats":{"get_bytes":14482,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":54,"series":2,"stats":{"get_bytes":14737,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":55,"series":2,"stats":{"get_bytes":12328,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":56,"series":2,"stats":{"get_bytes":13373,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":57,"series":2,"stats":{"get_bytes":15067,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":58,"series":2,"stats":{"get_bytes":15369,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":59,"series":2,"stats":{"get_bytes":14499,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":60,"series":2,"stats":{"get_bytes":14475,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":61,"series":2,"stats":{"get_bytes":15923,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":62,"series":2,"stats":{"get_bytes":14176,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":63,"series":2,"stats":{"get_bytes":15246,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":64,"series":2,"stats":{"get_bytes":13564,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":65,"series":2,"stats":{"get_bytes":13887,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":66,"series":2,"stats":{"get_bytes":12479,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":67,"series":2,"stats":{"get_bytes":14411,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":68,"series":2,"stats":{"get_bytes":13992,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":69,"series":2,"stats":{"get_bytes":12386,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":70,"series":2,"stats":{"get_bytes":15388,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":71,"series":2,"stats":{"get_bytes":11661,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":72,"series":2,"stats":{"get_bytes":13808,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":73,"series":2,"stats":{"get_bytes":14338,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":74,"series":2,"stats":{"get_bytes":14602,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":75,"series":2,"stats":{"get_bytes":12940,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":76,"series":2,"stats":{"get_bytes":14345,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":77,"series":2,"stats":{"get_bytes":15069,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":78,"series":2,"stats":{"get_bytes":14205,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":79,"series":2,"stats":{"get_bytes":14482,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":80,"series":2,"stats":{"get_bytes":13717,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":81,"series":2,"stats":{"get_bytes":13135,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":82,"series":2,"stats":{"get_bytes":13060,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":83,"series":2,"stats":{"get_bytes":15534,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":84,"series":2,"stats":{"get_bytes":14085,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":85,"series":2,"stats":{"get_bytes":13638,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":86,"series":2,"stats":{"get_bytes":13995,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":87,"series":2,"stats":{"get_bytes":13706,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":2,"stats":{"get_bytes":15154,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":89,"series":2,"stats":{"get_bytes":15352,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":90,"series":2,"stats":{"get_bytes":14675,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":91,"series":2,"stats":{"get_bytes":14657,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":92,"series":2,"stats":{"get_bytes":12941,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":93,"series":2,"stats":{"get_bytes":14453,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":94,"series":2,"stats":{"get_bytes":13138,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":95,"series":2,"stats":{"get_bytes":12882,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":96,"series":2,"stats":{"get_bytes":12921,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":97,"series":2,"stats":{"get_bytes":13177,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":98,"series":2,"stats":{"get_bytes":14239,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":99,"series":2,"stats":{"get_bytes":14133,"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 index 4c46fb00cffa9cb4cd9e31bcc4e056ee1ec7f407..32d0de480ac46c4a2a04a26e2a050c5f844a1789 100644 GIT binary patch literal 22805 zcmcHhc|28L^goW@40RD1uatR6xQNViBq3zFj=5vZoH;`%_ZlK0t~pWWi(@Qv9T_hw z!xfS;W)4Z3d{3|U_w)JwevilZ_jvsN_iiar`I5f?|AYJGZ(v^#U=%*Bfrjf{-k z+}xt0qjPd{8X6j2zI-_}G_W;nnWR^ zp!^P$9-&6w9kD+;ioX6eCgw;N^)n`5l^FhY zIl1Gx*NZrY{rwM5V)&0rP+Rx5GEif)Pi9lEYgu|p$FQSf*x6&Wknfn78!uyS9L4w4NOCcXoC zX=-W~78cIU&7mF#;u=?%3sGMewNU}}-QC@l#-gE!6zFbUwGKp~m|p+;IhHBGbQXo; zN9kX_WN|xxv%sUCRF1h4%ssM_Os#v-wXIEwa|E%yP6PEz-~b zzh5z=FS)`+;SQ5LUVRn*+OW8S$;6*VV{%*Tf~0jg3g!2d@2bxJwYG!_l8>Ip1BH2e z39d@;w<^8CHTr4)f}+fI?&8zhRH&Cmr^kjjR}9$QwSiwEv&|ZL80as%E*~rEt6*zq zMW2@(F7zqtIAtKZs+FtkJ(PEPlFMq-Q^WXFMP@vGrdStl0= zv)uP4=NHZ-5AgX}NL$#0*)oqFmWx1*yv z>_ZT4PISxTt)*NsB58vQeVx9&Khu85;nH(k`CMEp7D${?t389=r-7l=14yZ=c>)76 z?r&{T0`!$cA5}Aii@o*WNy`=)p4X9=I;;84zLN?Spl#vlLR(%P>YHlG^Eu6@IHy$a z=;SW!!$EnLQVi z#%i1l2>wAIlO@PEMQ3_42~x^iX@2VZoh=}|oz>fw(M^8XMMXfoZs~lZPu$l8642D1 zb&&lm)D85hlDr15i5hW9)AdN@?U)6=r!&0Pf67^snG=s{C&ogTV~@Q{y|H5vkqzJ{ zq+!pc!7;tMl=5p2il$q9<<@J~<6Aue5nnl@ix^O{s=8lg@@;d64&WqdI(PP|w+Du= z5^Rw^j=C)n0X|^Ou96%*wP~j(OM#A0jo+vedmw;s=;eyN!88JBQd(P!ct>CxoUOI6 z{0oAJ*KOwv$>B%`*BkQU)~NPJ9niDHMMlX#>LhERGy;IR>U~~?OBndR3w3gvm405^ z903>@l^1Nc&Ugakg0PRn)3&2nZ6-5I&l{+h5=!IHn%m8mn^{ky4U`zj-!iUiFlG$1 z_g1|mHYoqN(4+$RIU2#NZ@v>JC~k}k?CUd+^^b;3=rZ%RNWQ%-cF)3+ce~_=1zl_p zZX2LqMks&iUTe#JxKHxUZ&T=+ZtAwdi%f(2wt?j(Jj4Xk+KcZ4XaRqL)KwK94%|%e zX6y7h(rQy)ca`#3eYZIr%){bp)Npd@DGvr!=zM3^2a+v>(5$ zejJrbn{o!DGjr_!R_A2nhijiVM@B6?b4`Sfn=|?I!YGz8A#1$3d=ae3ON4 zd`qm-Fb#@dVeH4%NKIo_r3?ERwg)6k$Nfe_&i1v}Pb+9XvE9PydtCy~t(D9@pZ#JQ zc+l3Dcw!9wQt@V~hP^GC+>gcL20CZ?u_)fBX*|Y(xJ$qlq9JVHaB4oCIi@T-{QhRo z6HjJh;%aGx`l-Z~z_E_wsOz_|_X8IM6r{)ZQ!vS2dVjuWRIm-b@Q|-~Gj15qzB+!p zA*^)iWrlSAo5Zv~VdNr~jKtui(-r51-DK$g1PnQ`h9|dtlaT0h_EJfIr49^Fhh*T| z80BR9Sr$~JL}~9s;cm&IHP0!%c^}#xu)WWcK-0_GfW!&sotsKF%d4sW*I#4#Z~o49 z*1gH|BH>FqR48GZZ@FA2$^pvYa+vd~A1Kaz6FN6M@mk=^M82#^d~&Ry+p$Ye`@Gki zF5oQSwGhrL7|#Si+tx{+#e1fUnR=#3!j$1RABcusu2m&{c-brOB$wV6VPD^;1DDPQ zz7Zxxjbn9V1ox0m;F!F zr@792$ALyhxi(^w{&V*mW*M7`re9x{A$CZBol=(QkUl+y{fMoM6&CT|){%;^|q^73jQu(qUPru5x~ z68dX|l^rEMgwTW?28KTH z2%T9g`gjH&kjT3fz>gJV&3JZtmORv*b_sZL5_z7_`u9xgPV?Ujw>}2y7PB*I0hcdJ z>zlS_S;q@?8y!3}zkeJdEY5lcAPo=de_({&`AJtZijN_qIJIR>#R*9qLrbuh+(f~G z#qYOVOF#D;w4B33f~E<_qk5jnh2GzEa66N-L7pYYRAyrqnK2wCA<_-sK7ReY<*G#s z-bvAJL&kbGH3>xteE4DD&0FX0Yu;@1j0aC;QG#CYN-TS{b9`{aq0~`6^Di&OH=(Y= zXi6b1BwPCWP=_}2KFGt=3LV)#i)T7cuz{w>!KWO}%gK7Q=X z^?m)Z10H@-ZJNElcu_=-J|LfW!p#2MbW4$Lo3Xd8TI!%Q3cT}R$iR#XnXNolqwDR9 zYu79*>%yD0*d9&EE7 zu|Ok5_sBc}auFwAtuONcwspqusQjMz`_i@7YU6Uflp{Gs7|G1evE?fFBR1D*&ZM0C zkK=(9yY%1TS)jdYLht%m*RTU%m}E9Ww9LYr+#OTowvRvkXp>y}w>3jC;H#(c8Gh0i z7vjbjcz4y_&B3ESZ^PA%15-=cI-6p9_~7?%zxl1n7mRq06KDyQoQ~?8%RtIUS7M)s zZM^LMXC6+|r1`yRueMQZAZ5$-@8(|(I)wBov@7n$jNz&?OgGt=m7iVz2N?71!x(u*#Rp3G-YiP*8+?hmUU=$2q3%Akbx^fPiIi4A+NgXe>#&E@y1Mk!vfUHFkS z4xmvj;-rLI7)ka&MU#W2a#;5Sz(2-ESF|Mc*H4ofU;Qb%Jxi8*w8Sl~*#!U|(*fq_ zq~E_gL`hW@{`rQ>gM{YOI>6+01G9$l#9Rp30MaQyiwBrv$JN!4DOdG>i{w z1%su>YAb$isgvYZHZQ^f-nYyF&aOv)?CR|pR|j{_686W8Sp%{9Rp@X*N8L4tQfEW% zK67pK`gaGkpo<8Je0W8Iw64qm`j7{bqvgJPG2Ps^Wx zW$D-5lUVcs&EX8$GA)px=4T{LvZPTJTI^jw%*2&~#~&|_p47{BvV%`CtT);Sf``Jo zZI0-q)cJ6)+wBoK!Q-n<1j5g#nB8I&p2NBJ5KXqh31GGFv*DO#$=n3_p;L{XO(LGek)zv+xB>Q%Vox(T@J2NE@zP=bpHR>w1=LKM&80LDQdUm63|x1ardEg~?S zIV?eNZRLzsR{tyZZc|Ydv{z!E5CL^BV(`It3!tCjUt3peqU$~#XU5<&Z) zHXkPg$1aQnv#nzmJ@{FPLcKm9ZT{EK5h57MgIap<63|kgtfHPUvgWEd`cRIW0M#&% zo4>G9W)zt*BFwn+>*Zs+1w!3tm3JJ6#q@!@m)g5*-odTys%VDGCt&)SUN7nbx{$&~ z8ShmAR^qF|_yKo(@aOy%ED7kn2c@Gxf)6z~6%wKY6wPP@bHe7J@|!=b1lJeWgGyI$ zoR-TbDxiI9!*klZkd{lQICgzgn0$qq=;K~N;0Rax{haOuY@p2!&T5Jxm7Dcf;h!F* zbLUBWD$KY67YVH1Ni>-OZP;MU?R*K)kAYx?@XNqtmlqe@3Qfi=oB%uMcD*};2e+Rt ziemjZ9GQuCxYD3+1EwKI$q@XIu%f_B$vbZWbO!U-#>{H|o%z5_F;U!8tGxl*Yo24q zd|mQYkf7#&Zya`XQRFTJ#~%}5Ci<6|5`xdnO{6yO&#e%aG!$~sURl@C5_FENlmHd+&`Dgv zd8~PFIK%pFOI!-1eJcLo%402{;xGeZ;&iAWh?L2)QcN0__3*mxShR$^*bGRk_V^(d zo5x4`)h>Y@GxlMag=O7HQp6~02e|mdmFLIpIlQP5f!ly#g<>-JrkUgL&lgbC!HIcw zYEa}DOh5hi7}(NPsqC19$SDW?{!1>j80myK`ua~s+-xgG8~|hKADfnE&HZGC^UGn` z%Aa3V_fjD^T73&GaVy@KpLFZJf)qGuNru=-21;n%W!P*qD<1M@C~%ypr^3^MONKK$ zmVl8Yp=UZxVCjNm9ezI$dtfwe4%ct5Ao8lLD_N8~fmqfL?FI!t+KAoJ61U@OkzxgDX)vb7IATsx@RT0wtR6FFEa4TP2xO8^>W4Kc^$oFPG^Gdl_b;7cF|- z`WXbM%gp(2ufGZzyL0zEEe4!QdVI%H8{~0zJ&(1&lXM-36AJpV>yIyS6sU*n=tZdE z)|iZO{+pNO4K2#9L|a<+_0o-mB39$ISb`9#!C;GyY;idOU-G5T;VKVbb<6Vk=Rd1U zt;_;AiM2QRNlsk75}l<5wclThe7@d*`xs>M@s^w(=#!cDkik&O)Hk}1llb?EM<+>- zm7m0Up(8E4Tvh*$bBph&GA6xRi>!PY_2shYbC1wP~@zMW^D}8!o2I}V; z@7eKw5R?;@C#WcbQ}h|Z%}xF4!dS1;ignsDm#!L#{5t|A5J{7#uQ~vOanGFIWwBf7 zI{I7HbmvdriDTEZ@_Acx6g#A_f39*~k#JxqHc7FaBgwyt`v9R0yF4^tSzgCBC@Riq zUo$ELu4lUGZwj<#S3rRj0CV=QW0pcXp~RIK}R?vJ8}Dk zu?CF9k}rgx30nY~9jZ=CTH-rRVLYpk)axCk8B7wl=JN z`r!%-rHJ*}mZaPP7Ooc-ybOfY&>5caH5YlMnfE3>H(R(nRLBv-yM3PT*AYohGRL;$|og*{ty*d4ELe zf05YBL=knQ!R{;lx24;$fJ<@*`Az0}J2-7Pi1*!ToNR)s3`E1r+ zn<^;MW&rw|gm=|n(VclqXQ*$N05M7E0QxV4u(ky&fpERmTkHV6r`KQeA1B@|(m&bUrO#b`{KdgXDA+1$3wwGB zV1t#k$6nyA@FmvXVmFShtO_JcxwwFb1J>5BuBTS3HjxV3pl^bL97K7)b|)=>YwTb` zkW}QR4XnN889dFK1fA5(E`3PfHD=I4M{e)4BLtV*YXg%AX;=d~NpY-p64$ux(Dqt&=StY=BvM#e^FEoCXgb-q9xoXo&E4)U8 zNW2r(RN$faiIl70{_J&H2Ubsdn<&^!J<|aA{;-d~2(Hgmh?t7916qHFx<@8JCnes$ zk?a!bUDN{T(qN~+1JZ*@qtF4r-`nhK?h>z%xN+4HO=c18gxV`>|4*tjG!M$%s)a(z z5evRsYjaU4v;7$yV2{=U94hWX?Yo~M&-0TkPA+gbj*VDF7+vo+Er8$=>#! zP&PLoxzW^hv-C7CT<-=`;W_9XPpp-@fni;_dn#Z>TRarW&q=ARKYb6`de>y|`wqOU zMEM&wpyZ0!11M6yC_~Ko=O8Ol%5s3WV9y8@l z{%N0+=&JKB3P%)8iLSxhp1F{Q6&^Zy_A(oV!!c7FD`FbQU}2vu0gv_&p!83o*xE;$ z?%>pM)ZYSJ3=~Nyd6X4?<`NLYgeHrTnzk~tAUoxsrXY_+ur84MfS(lEZv(^~8i1jF zp<5&M9p)Sp12ulf1MpT{&IfBDpr|jS2dIaWZHcr3^3ZTdE zjn5J={ZH9uWEjMM%V_>300)de6hyDz_FWXm_IjRM{V0ju*$Qx+ln2%8Z-R$bvHYYP zh_wEG=(hyas}5hcNz;W1%^mlC5n<_gS;1r~+7a+XE%e>PySDxPO6ND_(NBCsN1lJO zfe$uFwc^b;M|?$RQzn}>i(YzSxeFuyl<-MdWu!G=D@zJHlXa8|WjHw|L*uPQ&ZqPV z`;1qtjqWtD#gX{2f9P}QB!KHv^b(}`EeY)Yg^0 zOP(KAztQ*!(`KW=tWLH+_A;>hKXV@MS`#6`$IFRZ;;tO2i+u)% zA2xluX5J9Rj&TnX=n}yG;{2g5N{V5mCInAfIl!aryZQK~J|TEb_3zyXpt$z>yT-d; z-)UECMla=>CB@@iPv&Jp zjkZzRKpu*og)&k5I|1r^W!JstXxfdKyr;)Q^?^2(IFUxZ$T8z?g-Bz7Nc#N@`bHMO zW|sO+w{EJA5v233MAhWh5ZAADn?Zvb=pI8tNo^umu{O}kgE>b^Lj2N}3%ogwB8%Id z!rTu;^sY`wd#h?lI@!;CcS>&vak+JJj}{5_rg_4zeqn@d=F{lCtWM7qStf0aT6eek zwLWaDuH(SXy`ioc6lXs>+u;_Uey^ z)%PKtubFX{VRY+DJU^7%#O&}B&CW(azx!XbNBJ&&sq~qxVQWimHJ40;)O?;`@AG4w z4fsjsSaWfHY%G*p$Diw1>NOWx_3>#QtP8Xgt;csdeR{*pGd)QT)kpvz0*0MryWx~r zbkZF%KJ9bkLzU@&HZ1R^(;SA9X_sr5cvN~{JmZzDcNL%McTbbJEJ!ZaHGA~d$GmXQ z{48$FZ){P1Y}idVEIeAMDt-yB%>G`pH}2WE`tgG_QBRRzJJQILSC%_%4zyNC=jSav{42Rh1pha0JKMr7 z2gsoBCWBX&-a_Ul7bzS$<?r(M|0MLJlczdow6WtB?Jr4#V#r zJF>$z_s*BS!muIX98;mus)}0Pk16dXK~mF&GY-Q!3<67DuX1GN+Lw8(;|WZlWsUjV zy5lw0Ew-yC*Ge$%Fhx1e6RNze^6P?N?^Iu)w_^hSXmlNZVazLC33 zfcj&<$M@OLpH0El6&-+Ab;3tKKw$g5d;ugCeeeT`Veef5CfQ?K#-0`3bXkKZ zJxF`SM4SY62J6}!^fOZSjfP#{^u5-ZsC~^!lyQKd%EIqM$X1Vgky5wI2ujkRy#N)D z8-BbdmU8l5E;Y=j_L_E7z+r|?QS6$wkJ`m?Y80qE^>Z&`&s^JIK&j$>{ba2)I_dAp zcqn2cSK)tQ0gM@(7$lZM4W&t&_Ky^w2gO6BCCo&REEBY0b+LZxi5c{7zLwVElqC>(!hYPHP-063hHZQc zgm>v9GMPaxPFi%8roQ|&U%BW(ce zvSI>xICX$yWIrt|c7pk%*lhxI`FT)N)vp^*bVRYm7X=AWX%mksvRO`&Nru4U+-W#K zimts*?WDBt#WQKo8P>IRd*kB?C3CXheRzF7EyDM2$rUnVUi8Z3Wj1VMc&)Nu!K06@ znTeCTs%lc$`Cl9>?>k}HZ5||FLIo-Z&C8JMPs6*8Q76#krcks*zAJM|(_BRG2AwPO zeH~OZczE*hCuZE2tLSNYS!PPfMyr}H;mrkx$4|xV-XCnWt@Xw$k(@Ne6$!fOMa0G5 z-((~!;vj7g)z0J;$+*MOl&uo3Wv^5CB_zpU2aq=n(d&}m{-}jKC21!r!mjd$a;27{ z7zb(6SEiYcw+_@fhpA9|&1?;N{TAfRU^sFiN}dNvONK1S1~^m*jeEsaYI^hO0KGSv zasKdhwB>^FJe?sg(yJ~O_OzumSxTJH^jgOusY@mAQ{W*Ls4jr@mqRipL&|-PrAUJ9 zr6ATJDJ!EicO0lP6K$4cn$ZC|wSYHh32R0M*x*%&{`-)V@U&>`Y4o}n$aCQPm<2B- z-xYW*B3*#Qu(1X1?=$bEzddm(=gT(F+by9Jpt3fN&y9~GdK^|VZjJRXB{)o(27{9w zhnhx@|L0flij=%Fa!{P*Togr1-0V7-*8vWn7>&GfE?`8jQ<(wgvDGg1nD|_Ox(#4A?&JHEoKsQ{Z;xJd-jQi}>PORyplw1YM}n5>!7I ze}Aa*)&h#Yu1@=w9}n4|;0^~{?wO+36CWMXfy!M-K(}+8nJ9z=Ni^tnLCxXu3ABK% zix}2tK*|xgq-ltRd;FxN51f>eJ3oOZG+^d2d?q4yU4GK+z322^!=?`@eF%*p1DUHT z5)}E+R@EYc*Bv$)99+-S0`wEau@&qTB80NtV->A+E+H*Gnz|42tP^qH3w0Z9Uly4eP7zampnWP>LZ0(fYrcP z93y_8A8BF%vOK=25sH0Nr3Fmhg?MRT16VoFFVrFkeH;`)gp?YnZxaW39{zsJ{e-^; zk|LQWLjlnK@bpluh_t`LTVu|4nX!Lh3OJr6$jyhSfYrFR` zC`38Wk*<_iEQ}44Dh%XtwUk7kq(C%U;>M(<8!a0g&V=Yo6_gu(hyDGyG$+PVTZ-9= z`7@%t$3XiVESMQFkM@(12ON*+U4ty`J z{xV%$V3T@WjvJYlZvRCSetwd8e7eOMMC3jl?U=U#OAojwtCUy8tbvCLwiCxd_1-;^ zRW`N4VaZTfXner$`?aNm&Z zIgK}2&rDJ;DIA>~6G7(E;i=(9l-U`0B7EoW$$BohSSSgJtohGyxdaZtgOabTg-^g# z@A~=k1h6XgSDEm%#c7umbbuh2wEPW~x~tm9MOGPHQipXJ7|Q=Olg%sSxh7x*x+=WB z>^4w$fuSm&<`L?jw#uek-z8&}6{46i2{d?D;RdQK>j1>G@MD;XX+!jO2=y`WF0d#< z%8$&n{g0F%%Zx0`t8TPX4U6w%AT2CERw#$##ZbC4a%514@D#{_d0`O3Q&-9#oBoY9 zpntj0jh_7^p4KT%2jNlrKPrJd0aDg6@R;;_St-yebS%b^_M~>5YD-rrv5O7OaQkgY zBrB#WV(g0p7ZVZ|bTIuZJJ{2ztPkmV5$q%o?;Qs@9LLlC&X%U5HM07jt-_?>G9xpE z90SGhse=Bvrl!+Xoz=ETPDC)6RbC5_7JB~K^--#hzIcieFkD_*aZa96IgYHk*J9(x zz>bq>h97nGRTaPJVX8OC64u0kG8NB8rRX$o|Hsphy2kF@6HgtIbxW&kQ133r)z)YHgn)SX?x_k|9rCkYs`=RbgZyuGcKgF|Exin zyt{J8|MXim{qfP+)z;S@VKU4Z{u40rSoK=c6`h3elS&QxY!4*5JN`S~7Xt5m6^<5T zCg#g9Bq=iDb&=`!--Q;FOKQvL;o^nt;f#gjG_UWUCp~Vd5t$X3UcnL)hjmgBP=0 zaW3uEem%f{#2>v*$4}ZnEow%x8DuDZXdY= zESsh~nD9>h9!OJk9O69X#w)VAzxc1CwjcAahMo#{JXS*#A)M1uE&G4>@+4^Yyne0d zLu4^%tuC6T(>rrjIR6a|*Nm}jd6-KL-kl$KD$LO%Yu2aYbRvtIovG!aAsW29`+t?{ zJ^uy^iJqP_!Xjf8ZkThn-&Zp*^T6^DcnI?SDwd9ZY%|9_#@tabRmmegeenlRA~$4{r%|N|==Pk#!lFgVnJO#9#aCzU(idPoJmCFkwP^C8nuRSXBR= zSv*>(Qf9`SFXT2DJ<*To8}_<}a>lb$ssEJlNo2rzeLntwTBLQjcs*T1i)rfQKfx3U z--+Qy~nNJsxCs^1NDUQ6GAs-6o)e?2`t#`|~-RmL-6{=8#PP$FV<|8G`-YW0DO?!ox*;p_V{ ztfp80S8I#JmMWaQJ)WoYUMw^no*MnnaPVIdq))WIrrpJj@G*_=vHqXx|K&|GItdr2 z{u`MrlcZm+3p^l+q7Vx-c8R4S=7|SkV*F1SSN1EB&;L`PPw4q>vh*tLYu*+LMSs$@ z?rtrX8*drx{kyy6AAB*hL{d1?)#&$l1! zHuEcKCLcT=c6w)C<1&A2@cM*)?_#oeX^E()*2C$qe>D0OzFWxI<6j3Db7v?P^RWzO zCqV%Z#h6PnmvIz?3s@7LA82h&s6x^vG8AhI`A5s`B z3|MeFn;3mn+{N>;0k|{sutvbRJi!d#FsZUdbC-eRbM(DCDqU@D~e{Z>#9#4-q0;KFi~E!*?iumC?@4 zV~-UBAN9^urVqsij8^Fk|D7*b-2|5{gaE zdA~@10@m$jn#P1c_n%w zFZ*@O4|OV|K7~+^r|tVPkW%fk-0RxqMm6{>-?!Rj?1aE{Hj*ddI;FD z<^KbJun)^khc8|a82Xm-+$o)`tTWpX?#8QP&lD*=iNJ{^|7e}5bZ-@D_tLvPyG4*s zZn$!(X*1(qv)kr{>OgeyLo^EY9K5EO6}nrPvP}?K@vds{;Gq=W`yxbz`gnO#asT#% z-ZfvjmoHTg{VWw1*w2QrEvPJ1+7I`w-hG%SH+#l8Us7uCTwg-hFn8&Ci=FH^DOcyK z&2NdCKhwcYrH`qH;dvam<7#l+I$ced+si?|26ODTr2XVcqv2n7C%SJYg1_Gy2ik&( zKE+d|eG<>#u~k9uf7I3HRc3wUVjN4J^zXP})MqVCGnSMYF9T%@Y$JY)PgMcIwHn+X zEgV}Dlr&85iTAWqh$?Oir$xCCQe#P|rhU7MY2mD`%)C?Qay`Poy%A2c1axMvt@j`P zDQ8gDv1?5X#`k8YUe$a95SnV51qt$J%ATXWlFdV}m1ar@mgr6`zTT0x{D?xy>S)xn z;Z9>fe}y+bdgKk!#c#Jo-VcfodFBoTXfSe6kxTk^9+;rlhxlDf~Rq|X(n`K z)t%EOyLX`%vz>g8AuMD%+9ePu=4i zH#+Xf(;2FLu*IorisYBBmu?=~*-k5ZonA@mE!pk;{k2PeK+x%bF!xFSzcBX^{~wrp zrAl|^#~xJ$&~%O0`^#hQ*Q0|1B}3vF z6bib*iOHIbDep(2>R+06JKa?Kzg0o4k5IVJuZtbE6zS@y@qeItVpdIQ%HaF4fq}H< z1tj3IL08rOs$jDVMmD8WxC0+^@`(I&QlTYAe&;ZwxrSeqrilXe+G? zs9)@5M92yf?c+%7JJBj&G`n;^g=)XwuCd*X%Y>?v> z--WVw9S1@W&cjY5SpfxGTffpS?*a#^?9iFpow?@!(D`rYowNZKr{;aL(>*_?Eu^~< zBNFl-+}`Vg6zRf)dSRT;fwPe0eVg8@5BFTIrIqQ7Trx~LJb_@~_wZJXUc5|!tv(7; zE7vq2?(n*;@_L_d17QhY?bnB?&i{kUU%r2+2lT*~?kBNR_;U^4<1LMb&%YKlji2`p zJ3TpkYW6>Ty*1#fHi%t%%VC8t5N>bPrTLXS#A` z-EoF-V<}`3OOnlXCkA}EFTuc!>%C-{7^bZuk}>hb5_sf1qBu(^!Z$vfdB2r1{dQxk zT$0h&3e0@FZ6Ow4^P)cWS4WZZGGB>JlIqo#KDAeqZP)fT%N&HTGGet`y@MH7*|OxZ zQxyH03nWRKVR=#)^L(Tg8(sb3qw6?3Cv9<($s-G(p@PU20VcL)MRJsXuGgh@x3%|7 zwfzrwPhuA)X`AJ$>Juz?T0J5iy-= zLRKoY#IYO^)oRFvT-l^M@gL~k%f4f?wStG3JteUTHB<5;8Pa8#aoj^$HrAO9UHc|s z2=w{4LHb@3T(Dx$A~t&*9TMhmZ((nRM*H*OpLr@7eCQbRRjWVmQMycq z?*mf<*MQQFsH;G9U19w4?rz|X1KIW4E_|J<4$07b$+$2{&xR$NTxkp_re^_wz%L3Wo%$urg=Pz4E=^xEN$#D|O5E3VFb$1Z;T)WAO*B8#7nBXDRLj}Up1BRZ{-S)QyD1#%j9jpvVUtZbw!t`_B zX5eB38_n}@qu~H@z+&fW-CF%z*oczq795-%nwb?CA@-GYjg5O#6@vE%oZ~l5ISX?mv8hXFcioQn(uK3vU(Ekn@ z58rnt4dDq}u4)2o#Qnxr3#GqugJ`;W39L`k;0>7>&l&C&<2zN2KcA^%NfKSWj_gDP zd=Fj)Wu};T;Zdnwb<26Qp}8E@z1x%2zI28XTz$q5oF%95`(@VG^C7k7cMdLkr;Ek! zLTdjmGpzev`53(YL(Y@QJ#K&Ro=Toi$Zc?Ghea81}EVRKQP3JZHw8Ho4kdkyz z)P!bW=TEC8UUtd@$A?u23RXu{3@>t2*ZFJX6=g2@ea-d?*r^ddHv3N~@9v)+=^|JW zN`jgKGLr01t{rdYkhHZXaC}+ToFRBVfod#*Qv~d6J*!kHB`I2b2sSM=r%5QMMd;mO z$BO@-8b0@(qTHW(EZF&mXc@&hct$3ojyj>i_pO2}m?8(JRBM9*4e1yz13DIS#E#*`$LLdpoMXx-e(hAQ9}Xjoxf*N;&kpjUO((>F%kro z!?SW$PL4hJ7A}o-sGe1{h#zjh67^T{(ys2-ErGmRoQ@ft1nCwZ8QlRjPUf7V_c9nY zo18N4)^wLUF~Z04hdwU$^UVm1A)rV@{32G=5tR}oF@+c5pllN46|smzx$Uq# zx3&F<#=T)WvujIq6pO75oNuh$o$(Rt`D-$iF+~x-7roi-D>SU;CD2SqwiNJp-w!!+ z98FF?y%!f9Ctdu8{+C~$6=7-1-mOApKQJq@+6XW-Ot1u+i7W^lo>KL0HO_WIx?`@K z?c&`jQcM1ivCOmKU>MP96>k4k`v<3a4k)|CPPJ?J(o>F_7fw+i=z^Gmm;23%jT zIz?~T@K*>PoycHpljn1=l4`4zYvbeA1nnysbBS|dpCwA5<39$~kPHwmiJ6WZFLa(+tds$O89nv8|hH%e+c~=L4;xMyYjXQtCd;l3%yPTl#Ig zD;j3)-Koxb-~2VDx-D6mQ`FXIwhI%j;%b@pWn$M|eXKxUR2)3a-!%rs68)n@ywr#L zx8xcXebl8d@;I*e5w_%1hIW)qWSNNpJJ}XMFLF0!<`TJ!n(v51%7!5FE03jLoa11_ z;oOKhT!7q{UBA7sL_(5MGDl~xZ%%30tR4FX2UIM12J6s8m&mg1ZbrglOPWNedF-t# ztN9>z!<&l*@UH%4H8AY#`u6YJq77MvcW!q^T?}K z;#lPXlNjiOiQYyRVQ|$`snDqI+PV$FRl|>unRw}GN`VbUllYhcrr*mv>W$m>V5bx= zJr#P;+`e<4#TuBXS!6#?T0yRe;pG{VS#Iff=-PNm96;`V7;seHzJ+lR*mTnROa#|r|oKgqYs#GVD93rj)anJOSdnL3-d}Fte&rewP z>5if4Nw)o6x%a-8sq#qD(2;rHuwU7+z=FyK_>$&@nV$4Q zXv0;t5+ZSE&Z?XPt=-?E$&rzf^`F{9M&R_Lx5$+^g&>3Iu{%DdbtxYfW^Y+=P#izy zEPa?ShJyCg@k=UY6S~dc4jGaf5L-idd6Rs?j zt=fWAl^6+*pB-j&hLfSiZYt>zH_Ny+S-Duf@;lED+pSBW?iYtr&t-T2W-s{Y+|AAb zMsJwD`ZyjUT z&e4a;qM%15+-Gey@@EoF{vH_hct-=|@tP){`U}(l=J_^oG<#e z0(_!oKGG?+e~Mg6e9ztUu>*nfj{XVyd9+;qV#F@+dORdove*o@M>Wi>W30ZVc!*)nAsvZVw(#)L=3emAa8*co^|Q6s|9Q=&nbTZ?!BE_J-sM9fH_+ z=FPd6=dhbZH}J4))R$5eOg-BdR{J;DV2)>cBza79Gws6vK(-_{CTi`TKxjk(X0((a zvtk6XWBchROc)Fu*)Cp|4<9fDtYj0e1JlYh@NVjLdN9r9xoT&1^H#&rzk88Qcjw#A ziyxv5Lo0chJ`VFq?Z*Z$3&Hdq{kI0YhmAnx$_zPqtPyrh@e~X0OQ^IN;HkKDxD$CG z`+0ntI#@}E4qtMLMT!(y(&vzLpiW!JhUV5Q6u=p4(|4uA@%4fiZ&0$J^1SN$kD@siSS30|oRsb8^WdrkcAzsaKr0pp$d%y_ za@H>f+G2Eol%*QRQ3i63zcQMfAE&u)=0)X1M~+6;#eAFZp!7M$d?l9#zhiS?Sn9U_ z@*3vF2oVKZbDnLxc<_16_=!o=ol-`vh0YQQwDuNU- zpaV-$lh6HuQk_NGyDIWGt}4~1dzUN zdE#|bk;QFG$Q;-5X(QHIIY(fQMhsdC4vc0XIA{IBf&Q|rl`Ql=6%fo5A`67>(YcV@ z_W<8A@gBQLRgH+}rj$qZB0=CsWo4==K zC;UitKb$8Xf0VmbMjG ztHh=2cmbh{)N8)}%Q>wf1p}6$0yk6f#~PI;KNAXtY3>QJDB=Y;w>PGT-`ahO~FWLdZB>2R6Ez*AJVXF-}`h}78MIkZw z0PkSc6c_fQp<>BVhe$w4t1neb@E35}!M8!o=ZR+nK$;v+>j>p3BU?4fM6JDfyXfaJ zsG|FZS0rHHq`;75$w>$71!ZQAe)OwSZa|R(ZP(itcVXS-usS_cG3)2yZJU5jh%(?# zpYNY5*V|$Z2y?xO{8MO#C}3l(@Kt&`I6i+DTM=Vi zGfTk+bDJn!UhBvAonm9-sk>4KG5vyI;o@se-bse>h4{pGk9@%VU0`g;#1S7n+|x}UVBJRD2Op>QTxOaMZZ9Ur&6xd ztjpqMV}Tmw^$zZKybIs}2_b>!aLe9wl=9{%@>Z|*i#Szdrn;pA=?b4yle@4HfeW(u_K!;0f+G})sjg?n^s_-gCk+>Ts#51+ zcwm7_EYpl$y9yNXdt;kd`ZYOU)&{%g)JEn{X3i7f#jQXrieN3@>{7C(WgpS*UAgk` z$Ih;pEs&NsaoO(5Z>|GQcO2}bBCjZ3#8n}VLK=2MER@!v-{ztM7mI+sO4OS2g9?VW zbXJTB{wsKkR?HPD@WKeWr(HY0S-5zLSy=}CNj&=3B&0p?qyiQWXk0=$UX3nc4K=^P=uN z<_GW7R#IESV>VIhs2V*+%4%o}q~qX6k@`l&dTe}T(66BeliWdr2KNl2PdeB!P|6kJ zNX&3~-fqHi1;nG=M06I~Q3!~vI{FR=)!Q&}kjkdGDTZe<7 zu?P9I6lN+dz#Tr8geQg`eF2<=JfjEuO+{}9Vw^2G)Ob+$I7OnP3!=1NK|JG=x;p11 zwlnV50TG;Uzp%AQ+Mg%hh%g@T*K>LLSsUlEE1(~O1oV{Q6XUXPAdwYWzM19GR)6QP zNE;H*Z$kZ`U?~2D>MnZl>mq) zN;pboaf&#F>*1#BGK~|LUIh`b)14&%`0-?N+t@gS;W~vi91S>j_kHc*5n^0DaoE zTj`tAIMfC+o;u7;SsBda0nsBIyfm4(>E^$FF)2I#rB1GKN0j%#t+x;Bg+TYFS&B*j z&^K*$I&B(^3Foa2*tsxb+CF;~73=#{v`9_h)JkCN=Dg zeIHrfY_QkxF_ckxA1bX5dTPbnzi`+b9?;JJL@QN$(4?2Dx?;ofX7@*Ss=x&$c9ADZZCuJa;K>NPwSrL$BOm%g)sSd50)Pd& z!IUC7CX4@D4S!ctI}!1HiQk+Ald~TgcbyF)0fD-wYh9$O zh^gcu!DOa*h(Vgbs=U2zMk(U!lwXYM)5BSiuGV)ABy-h;fQ1r4jze`wUQjN?qfL)k zaSmO!KJole&F|Hsr0PB_62MQq%lVm8t%4c-b>Ryxne zY0Tu8#l4ZeoTfAaSSQuG6crn$ifvyk9XCiwR4&&yK#F&#IuYRwZQAH+!`@%bEjL!5 z#aCSIi}$nMn}6%r#M|ADh3Ldz<^1fddG?GUBLzjRVW7~83CN%?2)KGBd-!rGGSL{& zV|vV`L=&&BRL@>@U1xy7VTc(yH@4!r4@VoTuG;B1`be=qP?30M^%i;T>W#BYnv^It z+ey|Z!M}Pme9}aI9`#gB%@4^;xmaqSE{uAO_$Gpm1yLPr1 zDHtoa-EJpN=2z|;l{ke<=rimASo%ax&~Cs4Cq(1?S%Wxb?qY}ZNHF@1P!M0y_=0(G zI61*ST|N`hg7spmKl1@0jz7@JzwFxc?T7X^ZQ6e*9Ef?W+*&kS1gHGozbgFqR!h$9 zPY56y6uzY!iDUT9+&CBwhgG7-5cn2Bbl&?Z9m@f#*r=Nn*ki&X8XVac>xb)=S?Syf zDsUDUGX-O^k*JlrW%QM)8U7Lp*&rS<`)MFT1>12dVEt})bQCZ?u!PnQ{%FudcI#WK zx{FoiW6?E@&WpMp#P!LoTYT**4|s60Ei8YJBrC|wq%@&qMjK_uXrs0 z^(I4+X|N`m=YEzh)Lq40g8%ePK8GbH@zGNa{fx8TQPz+=D==;)f3dvJQlb{Y{b&ZE|Kf_T3UrY#DmHV$@<14o?6 zGIK(mS{VA7t_JGH)BA_dqV zEd)3tRvba1A{n>>T5tGeop`@eoKw`cUx(mQ8V7`i$jbi( zY9D|eNgUyF$HO|MU(-9%FWA&)8G9_Ih*3<17Slua?P(a%$SHDm?SEs{sALP9($_}Y zxzU9DMO`0;#+Mdj@Ld3WB}TyPgWITWbzlW#b++8_2=# zO%lmA)~`6}EZa_IOL%~+PTotD`h{5isifm&`iWW2FfCne^u(3SL)Ts_>V4b4MN;N# z^1NJ5LTD9ovFy%HT3*?Z5>{2sYs256T;mshd-I~FYFzTWhL9%t2WJhBsdn;fX{uA~ zZhZ)$U)BI+35&=jz7Lqd>dq}hvN$}h37MI9Ed9AL?A~r5s_ydD&0_K-EE}HY7He8w zG3$!aNZ?v55T8uKiM|4Z&vs^{o@OnubV>4rWrT5pE5F^JEy8>Z%DDGFo*vqC?w;*Y zKGa!NsD94bt^Y>Wg7Z;kZMh^w;g@fc$VW{yJhdb`Z9DLNvEYP?tEsyu3p9plM9Jt! zBWesUw2f%B!)Q-%B5M z7ICTU9g@|0rY!h1zmuwd#aXz$6TXKn28y!p_!jF$ZTbl;U2Vw9=NjTPFRbr3EZKvW zW{*F75(W64$4#Ra*|-a*cb$hGhf|suxLXlA5zOVECrAvy^Ky8lPy)PFS- zkTH1Y?prrB8380Fj9waF=5HS;qi`6UhLEMqkz$F<{?Psr1_j*GXg8Kg_9YD5ve*Y* z+XLIw5>5ls6h==XzR&ediA2(e(PGiAZW>Z~ppvouYZ29tW66L>+%?6#LVgV2u{S25 za9ZAdM}@1^E#O}Z)Rb$`g@7GBbdUWIm^m>oaj#{aKpLU_0~UFfymts>hg2HVxy8`@m-j0miI3r)K7EIhoIiyg+U>2 z0d?5~T`9Rwj7J7NkSU}XNcBYpEHT5D7!WhcJ0_8HAHK!<{AqTIy=4|*F%Myqbck8G zFV|C0V|~KVF7n^`rvLnr|EHeq|DV$5XKJeF1MU&*ePjVQH%yQZMWqg_cQ$vIMRRJv3KLQeY0olQ|Ba0 zOm*n^!pZeyt~_?iFH8lbxloRNp@4|5nubla8+fY~ z`eG!bUh;&_?>Ub_Rt3Sz)-p}Yg@VSdo(y|wgV9xr?yK{M_It2Sg$ULj4o3Tf$_^vJ zmhcCcaZ5MkQO8`&aZi;P*Em8m( z;9Qqfo1{9=T{MedH5JayS#LAe5}2_AWou9Ir}8hRwN0KmSSeskH*u#{hq^{xTgrN zSbahFR;4Qq`5Lh|%K$!a(QG8y)3aAod}2i2OPYVx)TX*&XT|L_|1)b5-UAE=q2Tue zQf4=9r{w$GI8+pocP2Ds-C&bCaMQ5$8Cp0n#iIO90hg9;3a$M6Cm&b4AXjfL;+vFZP?=>Us-Jllb!V$~mUw zn1|=ZLz2UKU*7N551J=j_Oj`4iL>o+35r5#&DeHaEYf>^3x&U=;%{z#9_4mGB$j3F|oD5<83U} z&dyXpH0NnCV*SB-24Z-+U^>-A&B8-Gnh_Dr$QZ2#zoVmXzl*+o8vVEZYNrM8`}S#p z@9DdDr*MBx-}N=*ZJurx)c3tVec$&EfA3VS;FPnitO#6p`~sfn!EPg ztp-=dtmAyCe<_hvTiW|$&grSZ7m-SDy5F`pgI>EmUzUQVzuo+DKcn-07ttsznS+a+$-=`dxRN`%(!E5ntD{<`)3+7)I zvFlRJ+=D7!4*V>WVppgU#q3bl-Q5tg;TS z7{A0?t<#IbISVlLTw0RsdNKde#P;mh$~RuqV+MiBt|y#1QTv}0J=ldF2feU=8aFXf z@K{^$-u{>Ba6yi4V@S&xfepd5|m3$0tfMpfaOL#%AnlhaNY zXIJ3+?8m{HCEvfLDdWkmGHBs&1|iQyB~y+V!6&hs-_#9vvMcLcc(2-8{#F!Xlac;e zNDO%>Iznk3kbZPM$!|H;+ol@nF0bdWQ~j=J9LzGfvO0ArDDj>B+WK<>--Lbq zB6pO=(V3|vtgCJb`0RQw?6f7AR5kt6zK^wG zEssI$sLKQ^SHY?HyMyFw0IKT9ZT84)&FFzZ0yP2w;HmV>6gYb%5z#2$47}VS&Hr(_ zrOMw6Z>?kP9D8m*^mSA&FlXg-_yrFg;+OSIQ~9;3=7*w&B0AuX=tlFezCV7ujPkng z<=ED15Qs-OPyZ1o@#Bl_KjdsD{(4Bbf~A4&43)*`8FeQc_%fB zO~~60f%xE(%W=bV=?017r!;@-&MYcFb3j_z?MOn|jt%kktD{zu8ZI z-)tl;i#oYSuhML|5YHmiuCDkQjFCf9QWObRIn~p#WJs#$krDTIZtq2qgsxkBxJCD` zZ=;2|5RX}PhAlkA`vm&A&L|W!5(Ef5O^bUM;dtQQg1mX|; zAGrOYTa)nQ&$!;%pR*&aeC|^q{hjzkQ)buUcQfwE=rB;8TL(o+QTyyslzi_U?k3uR zD^`|$ot6!}=@GtveU4?V(bb^});Sx=;0D}PXyvV>l68r-f)C|(RQ1J|Vx`(?^xu*^ z12+~}%v;BLFY6LoE=$ocg3NSxk0or|jBaU}^ZhK*$oQHWw%(nNm#Z@9>cIBr7#^~w zm)yD2-j-eHVmjWQ7I06dw0DwgPL1B>OpFd~EvWB;D?^WZ3+ph0N$E04b%J6=jGwHr z<{i7i?nTx&w*xahIOWae*HQxtkhRfj`V;~B?Ox|{D$sMKmH3in2}S3^7Y6)q!|lf_ zw#Ex;g0{LFPfw0XZn7Ga>oo>;QE^biUH-&EEOEIi+mz2wu-LFBh#C6(SjRHlGiC58 zb@dJy^-*!ZBKacGI#B%_%afPl(&?14%)w($?{t-vh_H49PLa^$xu|J z&SVweQkci2#9A9@6}A^B2G@9I_Y32y_00;4_$DjQ&R#9s*(srj5UUo2gztQLWh!=g z3)1aC@xL6z$mo~v34_!t?oJtKVer)q)7;=au^ZNUzoi_%9>071YWkh?;?jn?XjYIy4MQ{@-r{`u=KN{7OYhYM4FF+c z@^e#aF3`#0iamMs#?6&YsqG)s9RL3Ku_;B!xH`!|6i2f=JRacDHb^(QyO?)fl7dVy z_EaY?`4hWfKxmftp!wl7oO672+d|}+yX&5wR3D2VIWi$pD(K!Z+Py5laefZMDwnF(t2|z_1B20<^6M(ca}J-2A!3FPdThdV4oe;70*D9IBe>I zG85myatr=ano=^SI46qj&Jr&yhmCP6iP0+E%X9AEh_!fG_2IbZ_nop=s^wqMPhw#! zxdW{4dp2rk9bf`ebOnn2raM8{sDz~0Sup@Zm zZI@}rvt9ecOe4tuy!f`y_TCLnTooxeNT(?9x3H&Anu=wktpWYVUi8>H*RyD{nyad9JE4d7UcQ{bW=40tl| zL+W0(_nk03L6&`c;LfEUpG&pQ%Qj!b^^jl;DjNUjQGU9T&Qt3{hrZL@nvUp|ba^WM zBw4dQzVbScn6H`C@M33;0O@buGMWfU6?YoS-Fm|MndILKB6^!~FX^xQb>E^azKejk zU$*4F@u~~I`zF@6M}JX6P@9yFnI1zUE-e$R4mgxDQmX-4m;8sOR$9fT_OGMn z=ZxfE~+k9``0pFyH`?2_f(nBO<~rd)^TJ84NFdPhB;kvTDl zs-wj$0qM@>pv`7qVtXg<8jh9(jC}gHW1r~{goiezMDsiRl=#s9mO^uaccI7Wyfs9B zb7osqY%yR2f4SL1%gTj8XKELV&OmzCT?S_sinPzN^UQymgXgBp^DDE`HY^h2pu1k! z92xZT<=fof`+w~asW=qS1^AIm={EeBdO)AQb!Nx6YBuh4uY^rDNH z>gzI$Rakg7>wZ6o;>7)MV)mLZqVk;i@p`-o0rH)L;*DIYp7IFT-R8k9u&VLoSH9GH zcCiq@)#Cy?hOQedUyv0!7#Y+Tw&@*l=I9(XV6a<+j|(S~2(d-X)b>4C;o<+_0x#G> zjO{Uv5oY$|xZB_YahO_OC?kxxY)@k;J~{D}Ebx+HpDZTxcZE7oT-&QqIV1mMV-`gf zDkC82La)S&TD^cM7gPs0uU%ghWaP%>d-06k;Pz7oZqCK!W}uA)0$SL=aN~3-$;l0) z^Z~#7A}Y_*hkh+PMX3R_t&T|0f{rNiD6KcFP88Tr#2dJtWZ=04l+l@Iks|h8be0C3 zIO8p6P#ecR&xdjpND!wWObw&Sks=gymbnE_RhM8dINGhz&(fNJ`bWv=Ga-LFwigq` z6Hdqch#Ji#@MB>hEOKkUdZ)U~XmUmf^}VL(uYO3nwd@&2kWE zI%gfggf*5TqgV~ue;RM7#f9V5qO;6yn-XWVEDAhg9VqYnL}!VhvvlXg3EndW0$K8G zfc9Y$`hTumyl88%#X?M~(A^`_qsh*HptF3UgL6eMgb@><_$S_~+txt``$^ED8;ejh z-jo#lD7VT7YU?$afOaWlXtI}ouTKu2mV z8^)Q6Cl7zVy4wn{7a*tu?3_4WH9)dII|YvmV}iKPLV6mnpr%>1%l@5ld7~q1=O8JL zh=u5P1ixjiHZ|Zzmj&>elad*`jI+|aHzPTix61<^vZ|VpfS8U>x3*8?f}zDZ5!~;2 z8Qsu9N4$*`t9tj29bZ!4;=w5r(Rekap$I?t%cWqfZ$0x)c9krZh(v-DA4=2HNZ%MK z((>*+$YeZbc;Qw3_VIvw4AnGrK2mCU#M52~WoCQ4u)p7_6*7~H4*F(l{uIsB2hb78 zb|HlUld7!pJS7@BW_0FP6XYs&B6R1LgivK{cjF2>F_(qN(>15Yi_*Q=fMN3Oh|A9e z0NFNSG#TjJYYs6y&wdewxDGOEjz=jboN+lAL5x&eTE}5I{i3Y@_x9|7QqyXR`5b7-JAoIM?{bg?DXgdK06~OLe^NUyo%2qs z#9so{0DCncJM#NQkNkAGRhqg>Y9wf;;Lkiz(;qtm9T8JYeyd#Ht_cAnMYMMVZ0mEP9@zKucK&vennWww0*VZI3(m<3y6H5vY(^Yj@zbhvDJ zWsC`hTOFIPgcw>B_U@e>JJ@JHeHM(nzs-y2Uq1757`0#1elP&Kh~fC4$!A9t`T=(> zAt?ukB^k*pY!<*o&Jurk_0b)X0~H_>}slavmt(wmNd`-HI^ox#h3-FwcU3%EMB+yXl1?oh4TeEaFX zGqv&T3nbMtbuGwQtx%Cqn@Z%ko&W%=*&dM8&)Z%;{f2-#3l9g__w)HkpL6*z!S}0z zHI@F;$JCRNq=v9{KPgz4gINqjTj0pXS{~YxTVw_6-0jJx|4KSsAcl8^y_Yr}!xX!z z4_Dschlis>7acSS7=bdLQN}b?i=Us=a9F;Z3mfaZZw3$-c_-zSF~zpKj95#l;wKI6OOqmaL6t<4^C}p!Xpz+{DAuH6j7>V*1 zO6ak*yzq|Zg+rYnxLmIZyvvt^hGr@LVC9=*^ngyD3l#c}=Q`<|Q?Th149Dg8*UnBz zst=`f;AM-gXgtp4^IDiv1$eH5{wQU!mYEM_N0K8JyL~G!P7SCcfhNR`rlw-7wU*d5 zz;Q?CUk`Us4)n~$)torreT6jlNy}{|g%&+I4{(!J6B;bPHaJ)7kV+NV}+};&h;!EU1HIR|K#M%Iznm_Y{@(l#)M93lkH3X=9 zeN5DBbT3gKtgCJB4G86B#44|jhww2HRdDAq#UeiRlmuT)u!Bl6{(9bPj1eM2sKg>tgg~ob{jx! zp2g^oZ%u{=#OB`Qo%1|N#uK5gPE2uqZg%?|mHz#~s}4jSN;;zAMGPO|rXz)ZA}q@D zEkEuYK*sdq;*8UN;Jkr&k&(Or!W(!343 zOdXKIJ_#}%Nwfr1o@eGUVk0CJCsV>{prMO(2(VGFHCB9U_`ZMQb8@hiln?iMtpQ>y zk14XySgo9A#*Qg}gB|wunL?jDOIys2qNv>7)eWDn;wGk9M&hprIVlhDdO8#d#vQdr zqU)Dey^_`sAgl38d_*cqG zuHkfYXSx~>+>=Z07OurnfMh?PJjO>^h(Z46wE1vmUR*zB#?OF!T&OG=l_4uW6hzte zxp^`m;@M3gs!@kU1eu@#Oqh`)JtEF{sUHySNioiM5g7aymtfZYQVcJoFxyT?lzdhM z)!dqyLKlxyf|4juT9U%E$8vdt%FC}4;dK^h$(#wLj9&6FS z%N#2=E?HV~YhCz7+2~|+o$tm9!2(?Ku|*Ohw!;KY&cq$(13*#VOlO-Q- zFT^U%hCq4X zl>BVG%cQQG8C%9KuNA@aLN8Ps9*%(}w6l1DHAK(`W;$K?aTUc^`)Eo{)3@=4brN{J zFIzVBf#z;k6YFX$zchyIkU>4>KwSRjPjt`(k3QI#{xuJOjVflEZDx9+?em_pmusz1 z)@8>(Ry3sQfU2P!vl7NR$t(+^6uivDg>x1%07RWkd9M|x{&)2L5&_*w* zE{Mxtn4?0=T;Rt&9U~KP|J1AwJQyYm(*&~nu;-b5^Vc5?lCfF?QT{4xx4XKC={CCq zc0f3N8YLn7nmq`k$FG??XZYRn0whZP&rbhp*71 zk)`P3`*o;TJno=N2$xi$!h>2nfEPTgTdlkBG5MACG`_^ckjbGRE9z*yt+a;*87PzR zEk5xNd9Ql75naqG)egK9(_QZ^_bO1$;(GE%pdI8zBu(rp!vUeZhgWFJOJNJ!krO0xlY5zrx=uA0Uj+h~w->$(v=zj#0?5;|q>dvv++g zBV?LHqB`y$rFLPQ8%2z{Sa$_AfIKz8hn_h{e!Wm?4v@P@MS1RUar$G0-mB|CC&X4P z_R3kZVj=VA%Mx24IrV-NAs^#V=i#*}BzrMWB+r|4=_EAAaZ0@B3|JkJ=6Y+GE#O&>tJUua?L+T%Btj*OE;N zj@L9dzh3Zq9&m9cF3%A>j*lh1cJX6@V#{^#c|u^hjz?o?Kra>f>R=h_Zle+}X0Q+bl`dQliQbFp2hc^nT?fen%7)NTo~1Zy z(sHhUH?uQB!*(Df_glyrcg_H`rK3;b?GVGqSC)W8F-_}Jx%n2Acr;`@y`sb5o9YHY1V$cu++z+QT{j}o*8>z5(!b3pupvgqDKRCn9+fUS2tw`N`;%VM};lQN)O_>QKgdHR0M$~&w6!WV*B}UtO)?B z{GzC%11skaS`UO?;E>7XFE8zQFcDwKRUQbOAA6pYiSPZjUH(K3kf#*cJo|^Ny&1BZ zl?e+l4nc}k;seQy&F<1739RQR!7Rc|DQyZ>g};%-lAO8%Sef+M=ZYUA)Fg&FV%b*~ zz(tdiVuHr3?4cp2tz7g%XF3!fM4@lwS^4M98)#@+wK-^S{o(exIpNOOfIv>vT=pfj z^VNi)TDOo8d@)jgGwktCrsnhX>_0Rjjet+LYZoeHNYQGSY%VV_#+z6i5jL*=KW0$_I)d6#IkiM{X`Uerdn;VeSq)fia6_| zkpNaW~e{;v_WtHY#)vfO|l=9!Wa0z?52T*z9!1LTKU$bcZ8;~BkYF_j!`T7?> z80R2+|E9r)0Sue?`bOvhK2b6TPXlJ%IRmN#-+I%1d*@~noKqzTyo_ULCOMOKvGaR4 zM89S{JrT3vu2d?tJYY5&KH&Q;VrA{+H{L(c)}M6+khQep?K>fb?U!5z$b{GurtP0! z?1sCw-{-{4sPsKuf9f3aOAkKI`S7C`B34|{#rodJihE7o+08eZUd*$9BQ$??SKlqS zV|E!r-sz}R{Vx}QApYnldZGDG6F&ExyU~*d{5{HgI_8Vxy#&Dz!l+5}9c?NPihWYZ z-J3D;6&%K~>@QHdptN-wU={HW@0Lj%;U8`8yTl??jCx?2XV7<*!M@#{?IU zqQu_RU3+Y#qv^khBC-_!d#v;eoIF?zik&~C%yV4MBi%he zdr|9i%!K$A{0aFY@m&BzDLpfv4j888LIyR#aEWud3p4Sym~G)vN|HGpR+WzUOBx1o z?9?bcP-@)=!WlLD_=1(VmBw#yG~pWxwBvsPe`^A=VGVfmM}OJ}VTAj{WUeAd-BKgy z(t>7Kylna4G||sILAEpsD*P}A}B92Fso61jR~>jHMwrP~-7d$MX-g24+?Fks!- zVQ8_WnTZp{ayBSq&Wb{zwDv9~NES;2X1*88a-<+!w8!Ji^LP6n;9G*p`N|K98_{`hINSw0meMKrw;rMkwXW4p|b@f+u6|;Fl^4pi92CNQbBs8=pYKD zM>`qx06(mt*ixQZ)du-HHZWlhp*yO!}yyVBgi^Xp)Fu6vAcujX0=1Gy=3jv?#%(2t}jR z3jFIO>!i`2wX2xX+T>d2C^BDmW3Vx9f9&T?_@VB#SCi`QxBxX>`If)@xfZ{u zyk^DMgg+_k# zxm>6zM)HL*d&?dAX)Ewz`!kPHCMD!~=P339D{a9UqgKy_<}*teR^%!h9oDbI7F`_K z=130Dxsul?tZJ-P~m8XUgd(HK!IIbX{54lQ`a}AfgBX3T@ zg_;OB1PacAnP<>US#GJC2d=*9R)t4@K9V3s=09{6pIt)ZpGcLK>E_GBYQqWoLwZ|e zelj#E_hi4q4x*Yi+6L&y_pn$(warbo8oap*2s}mTBK0Z`v$c0 zc{T{%?tI&H;~ovqM&HTBKCt?eZlVULm%f%FJfkB9z#!z+%8Qtf)?*|7H#c1hXrC=s zXn{Ai=Slp?=xNY9m*^}7?PU9oae7v_45*f5rj*iz(M;j%I4SrI~sD%6cY+n z5n$>P#@Up=VsdmO0!3``Rs<+qYfJ7*3?3P_NO2Zzv5=Zh=|peYht-Xdd5C}c%c;TQ zG9A%TDIdPf%seaTRz{9B<7@HFQ5C`UZt<>%xzzgza_(LpJ_+_~C;oVdOS|4dKQSbSGBem%_^UHgazT^D9-w9EY+ z4k^+Rm8BAVnFqVQ=@NKz{fAJfn`3`3GZ&CUY*xLF$`*9YmyeuEa%JBs;>w`IF9t|B zlAR=yfp;3yMJCW7sZ!IYMX*8=@D`KuZ@}NK~il~CFqZ( zNp*|YePdeC+V^ub0oqL=uL7gyH(Y(5hSI0mRq(|sOnJr9G_+;L8^czbd**_|{^sZ%4Wg1ek3x`azl zSfOyZ8~=ZTnt(V@!?JUFs;1E)LY`L|y(7hm`by?NF7LD^rwLNjF2W~HQi$XV>Ozn& zF{^~9*UuhWSpW0Y?vD{H_+IK}oUolAX!op}D~k*F6y~U?UYkRYRT@4`zZdOv)8t_6KkB3+(JVhY3t{*mRUu z42J#nS5~;HP88~OhPhrQ-G543kCS2Rt{9xYx}>WYmD65u@2`v?&Ra~q?%qBR+8Yl3 zdY#=-12#zhZT*{;G{#}Sz-+GjQ~En9EDkN$u=%2~bX~Zb zjqeF#QYpE=!?&(48uzyx+-VDy-0r$jC6MyBH6K~TU9QM4@4yAo5u5kFfBau5B*p2v zM&Zg~@W_$r3H)1t3z90s=)dC3g3p!whgQ3JvQ*_u#6p?=S7Slo)_==aTb@r#r+jF9 z;eyd=^04Wq@GoA*Z(tLKX(704j=|VfY5ePmt1J!3}7Nv0~*KMB(t>9!R9JxbHn*RNgV`VhzwfqJO-p znd`OwA3E9unlBW6q3wYt9B0^B`tRmH`eGVV^(>_EjDpk19`jz z74m8&-ko^4A%-FeAM4Ipj$S6wenxgS+Z?!eWPau6AhN=-wQ}$>LM?_%oVb~ZX~moO zWzhfE~2*i)9?b{uTy~zKvE~D8~W!?H7y3>)jBo3&vjtjWtH2p~X33v?+=6Q#{VbDb%zW63pMh?Cs>|e@ATkskS9Es^sku?Qifpf?v zV|YjF;nv<9&hW{0C1K<@^)K!6xaP;h%HpcCo1ruu7gnmp@0>3OudppCv>sAtuqIw5 z3+I2xd%8s+F?=S>xbxxp?n;GycLrE)`*@%Gl>}KS<@UV}N4cy=xr%MGx^}8t(W}=q zoG_d;<|F=>=zAoLNzXd7m1r%R4oSFz^yCWU7OOJ{Z+rg4rlb|W>pd8SjuIX7OwXa@ zf}Yio4}T_C_@YOdAk{mJP~Us_6J~qfn~RZCx3J_hG7gJTm=ISzEGnf6xZly)WqNy2 zDYPTVN+ znzFAhLsPSxk~NIagy5)Ioh@UZNpHb<^Ue5MK>Qk9t>)^ertX9UpZj^HAqk7Z2z?Jr zvB?j%$O;3>R}9H>0iSiTZ(K}*yd2W3D43|-lp>s&CnPH538t~*1C+c;KTO=`ia?lv zk}SnGT^jeH2rRd1&hC4sxcAM>p(tC)LUAZmOg`~i-rPgU9*LZT5aA77xx0bJk31vz zu6VhG_vo{1N~xR!pY8v{aCYgSUM0VU#0pF6+v`x}~fn#T!=@sYwkY(wx|t zOV?tjdOACtl>G)|l@yY7F#F0R>Zh?f7Zxg0@;bw0O8KK?bfwkkvmSv#(&5G_D>y8vAGl{rl2gwas;<{kX>o8P5L2-M(>8x)%jb6vX~o!7838$a5?upvv;q0;eM^g@Tfc&z41Foh!^S`k+0I6oUsS+a(n z7?QR(R7dikW(Kvpj90ptsiou57Qh{Q)0p0yS2Qo_*`Ke|M%e355+nPx#sY}6*dKAU zeQ)d2umHBG`R})PT8x-&m#@V+~BBa5`&BQ5i>F z&=0Wzj}u?vcR}Rft2IqdkBpCR z^aofetr0C5mH3Ru<`1!L)E*4jSJe7?nn7aEnnROy11oQJH^IS8uMPz!%rz+6Kt{G> zJM1L1-tXbpQ8s7RJ5|Z@aa4%VbIlCQ6-PQ4i=pK{w)$HrZ zm$ln&ZEP4mj`hCg%lQn+1e~{mWG1$Lnk{yxCg7F(vyIaRE$i4Xcp77hGog~r*Kn_$ z?+Ciqbxaf=jh1i_QK)g+3fm*iz7xFDMNjMZOKe{Y^6bM)UR{ISC+e+(hbi^Fo*$XR z-2C_h$rtCH9@kJ-YpS9UZJk?9`Ma&EqwBn(HDBP2AF^gVx}5YSCn=(fM3*9=aQpu} z3&T3bhxT0WIaKCEoaC|~5Wk)|G_jo%o-@`J(9O0)LFHMDmYq$0bW_{#GIize`aANK ztxw97^~+$;C<>SN%lSIPLHSzP83bKQWHKo^44lbG>#HWwmRiGXd>t1J_~5{c<9gv_ zBeRWcS;7eLP`s0+yVbA5d$Gv*fuW;_j2_^4!<{WMq!(2NOI^*tw9)|q25&=dA7fJdg zwnYcDgpiP!Iuik3(_@~yh<4xV)-e*Vp~vP*dZ&Jw8e8SXC9`*=leHJ`l}K!`Jmy;T zx5@aWxi;?i<{!qqKGilY6i@F`ahPzeM%PJ`_(Nr2il3%7!pD_Oo+4qp2d(6}+?v%k zq0n|#;(7hxxulcL zBr{;uNd81pdxl2-TvL3sih&*{js#NrY5`@JbX>~mi(PwAaSj_Puy@(KzF$S&kiKoX z)f!QJtLgg|eE#$+C`N~=QB*vg=JSNgKY26yr=v6}R8$i^O5^@z*)v$Orbxjg`;QBl zsma(9iBrK*naQ=1;Id&4h@V>!%qjgG6_IEGKpg%^f0 zu`HAf%ytI)&DY1{!qiZe24>r4rS#Za=5@_4B>3id%_;PEf=m^E>!(--P^^lhN#ab-Y@{BN6L;2$dF>5k zs$7A+V!;kpzv3X)L=4wEs-hZe6n8poB7EXyQWInFFK@qgD}KE_`GU!Eq6%xJ=kn}7 z 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 Ku`+{mxBmf6S{X|K diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline index 9945e2665..adb284ba6 100644 --- a/actors/evm/tests/measurements/mapping_read_n1.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":6896,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":6917,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":6964,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":6855,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":6958,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":6814,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":6916,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":6776,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":6608,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":6579,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":6814,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":7035,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":7046,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":6814,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":6812,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":6836,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":6819,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":6743,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":6771,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":6894,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":6900,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":6765,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":6853,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":6912,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":6999,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":6825,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":6830,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":6748,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":6753,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":6877,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":6959,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":6690,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":6538,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":6707,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":6755,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":6789,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":6784,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":6907,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":6625,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":6825,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":6620,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":7022,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":6901,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":6579,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":6837,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":6830,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":6794,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":6643,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":6602,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":6901,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":6743,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":6666,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":6912,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":6684,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":6661,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":6730,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":6807,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":6766,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":6817,"get_count":6,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":6702,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":6725,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":6561,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":6771,"get_count":5,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":6732,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":6813,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":6834,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":6881,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":6772,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":6875,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":6731,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":6833,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":6693,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":6525,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":6496,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":6731,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":6952,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":6583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":6963,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":6519,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":6731,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":6729,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":6753,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":6736,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":6519,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":6688,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":6811,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":6817,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":6682,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":6770,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":6583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":6916,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":6747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":6665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":6670,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":6647,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":6794,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":6876,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":6607,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":6519,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":6455,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":6624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":6542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":6672,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":6706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":6701,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":6824,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":6542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":6742,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":6537,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":6647,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":6939,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":6818,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":6496,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":6754,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":6747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":6711,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":6560,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":6519,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":6818,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":6660,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":6583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":6829,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":6601,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":6578,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":6647,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":6724,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":6683,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":6734,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":6619,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":6536,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":6642,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":6478,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":6688,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":6649,"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 index 40530f0ec7ed29ff08bfc2508a81a248ac17dd27..42bf9a66d91b331f2c2596843b146fa6487f014b 100644 GIT binary patch literal 17361 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 aJ*Le6Y*upR**xn>uGVdRwUV26!~O@E_g--T literal 17373 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 Sp41-)P(5v9ts>1Ek^dV8;huc} diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline index 6d72c4fc1..0e2267a11 100644 --- a/actors/evm/tests/measurements/mapping_read_n100.jsonline +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -1,100 +1,100 @@ -{"i":0,"series":1,"stats":{"get_bytes":111230,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":1,"series":1,"stats":{"get_bytes":111206,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":2,"series":1,"stats":{"get_bytes":105951,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":3,"series":1,"stats":{"get_bytes":104571,"get_count":150,"put_bytes":0,"put_count":0}} -{"i":4,"series":1,"stats":{"get_bytes":110547,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":5,"series":1,"stats":{"get_bytes":110948,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":6,"series":1,"stats":{"get_bytes":102936,"get_count":144,"put_bytes":0,"put_count":0}} -{"i":7,"series":1,"stats":{"get_bytes":108252,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":8,"series":1,"stats":{"get_bytes":113934,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":9,"series":1,"stats":{"get_bytes":109383,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":10,"series":1,"stats":{"get_bytes":111993,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":11,"series":1,"stats":{"get_bytes":113870,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":12,"series":1,"stats":{"get_bytes":114007,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":13,"series":1,"stats":{"get_bytes":107343,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":14,"series":1,"stats":{"get_bytes":109371,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":15,"series":1,"stats":{"get_bytes":109476,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":16,"series":1,"stats":{"get_bytes":109696,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":17,"series":1,"stats":{"get_bytes":108386,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":18,"series":1,"stats":{"get_bytes":103157,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":19,"series":1,"stats":{"get_bytes":111427,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":20,"series":1,"stats":{"get_bytes":103721,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":21,"series":1,"stats":{"get_bytes":106325,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":22,"series":1,"stats":{"get_bytes":103548,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":23,"series":1,"stats":{"get_bytes":112972,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":24,"series":1,"stats":{"get_bytes":111901,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":25,"series":1,"stats":{"get_bytes":109076,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":26,"series":1,"stats":{"get_bytes":107631,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":27,"series":1,"stats":{"get_bytes":107213,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":28,"series":1,"stats":{"get_bytes":108469,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":29,"series":1,"stats":{"get_bytes":106884,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":30,"series":1,"stats":{"get_bytes":112250,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":31,"series":1,"stats":{"get_bytes":104065,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":32,"series":1,"stats":{"get_bytes":112522,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":33,"series":1,"stats":{"get_bytes":107740,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":34,"series":1,"stats":{"get_bytes":109400,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":35,"series":1,"stats":{"get_bytes":106437,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":36,"series":1,"stats":{"get_bytes":107534,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":37,"series":1,"stats":{"get_bytes":108783,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":38,"series":1,"stats":{"get_bytes":109372,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":39,"series":1,"stats":{"get_bytes":103658,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":40,"series":1,"stats":{"get_bytes":108128,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":41,"series":1,"stats":{"get_bytes":107510,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":42,"series":1,"stats":{"get_bytes":109389,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":43,"series":1,"stats":{"get_bytes":107520,"get_count":155,"put_bytes":0,"put_count":0}} -{"i":44,"series":1,"stats":{"get_bytes":111472,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":45,"series":1,"stats":{"get_bytes":110592,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":46,"series":1,"stats":{"get_bytes":111256,"get_count":165,"put_bytes":0,"put_count":0}} -{"i":47,"series":1,"stats":{"get_bytes":110005,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":48,"series":1,"stats":{"get_bytes":112716,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":49,"series":1,"stats":{"get_bytes":110248,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":50,"series":1,"stats":{"get_bytes":105095,"get_count":146,"put_bytes":0,"put_count":0}} -{"i":51,"series":1,"stats":{"get_bytes":107030,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":52,"series":1,"stats":{"get_bytes":110418,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":53,"series":1,"stats":{"get_bytes":105818,"get_count":143,"put_bytes":0,"put_count":0}} -{"i":54,"series":1,"stats":{"get_bytes":110541,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":55,"series":1,"stats":{"get_bytes":105466,"get_count":139,"put_bytes":0,"put_count":0}} -{"i":56,"series":1,"stats":{"get_bytes":106647,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":57,"series":1,"stats":{"get_bytes":110059,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":58,"series":1,"stats":{"get_bytes":108825,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":59,"series":1,"stats":{"get_bytes":111167,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":60,"series":1,"stats":{"get_bytes":108352,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":61,"series":1,"stats":{"get_bytes":112432,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":62,"series":1,"stats":{"get_bytes":104164,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":63,"series":1,"stats":{"get_bytes":112197,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":64,"series":1,"stats":{"get_bytes":111584,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":65,"series":1,"stats":{"get_bytes":104331,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":66,"series":1,"stats":{"get_bytes":110179,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":67,"series":1,"stats":{"get_bytes":110041,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":68,"series":1,"stats":{"get_bytes":111331,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":69,"series":1,"stats":{"get_bytes":110053,"get_count":158,"put_bytes":0,"put_count":0}} -{"i":70,"series":1,"stats":{"get_bytes":112701,"get_count":161,"put_bytes":0,"put_count":0}} -{"i":71,"series":1,"stats":{"get_bytes":106722,"get_count":153,"put_bytes":0,"put_count":0}} -{"i":72,"series":1,"stats":{"get_bytes":108028,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":73,"series":1,"stats":{"get_bytes":107353,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":74,"series":1,"stats":{"get_bytes":105914,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":75,"series":1,"stats":{"get_bytes":112559,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":76,"series":1,"stats":{"get_bytes":109448,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":77,"series":1,"stats":{"get_bytes":107305,"get_count":147,"put_bytes":0,"put_count":0}} -{"i":78,"series":1,"stats":{"get_bytes":108057,"get_count":149,"put_bytes":0,"put_count":0}} -{"i":79,"series":1,"stats":{"get_bytes":108528,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":80,"series":1,"stats":{"get_bytes":108947,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":81,"series":1,"stats":{"get_bytes":112263,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":82,"series":1,"stats":{"get_bytes":108677,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":83,"series":1,"stats":{"get_bytes":104024,"get_count":142,"put_bytes":0,"put_count":0}} -{"i":84,"series":1,"stats":{"get_bytes":113162,"get_count":156,"put_bytes":0,"put_count":0}} -{"i":85,"series":1,"stats":{"get_bytes":107969,"get_count":145,"put_bytes":0,"put_count":0}} -{"i":86,"series":1,"stats":{"get_bytes":109762,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":87,"series":1,"stats":{"get_bytes":107555,"get_count":152,"put_bytes":0,"put_count":0}} -{"i":88,"series":1,"stats":{"get_bytes":108148,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":89,"series":1,"stats":{"get_bytes":111888,"get_count":162,"put_bytes":0,"put_count":0}} -{"i":90,"series":1,"stats":{"get_bytes":114413,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":91,"series":1,"stats":{"get_bytes":107470,"get_count":157,"put_bytes":0,"put_count":0}} -{"i":92,"series":1,"stats":{"get_bytes":114171,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":93,"series":1,"stats":{"get_bytes":108055,"get_count":163,"put_bytes":0,"put_count":0}} -{"i":94,"series":1,"stats":{"get_bytes":109424,"get_count":151,"put_bytes":0,"put_count":0}} -{"i":95,"series":1,"stats":{"get_bytes":110065,"get_count":160,"put_bytes":0,"put_count":0}} -{"i":96,"series":1,"stats":{"get_bytes":106262,"get_count":148,"put_bytes":0,"put_count":0}} -{"i":97,"series":1,"stats":{"get_bytes":108110,"get_count":159,"put_bytes":0,"put_count":0}} -{"i":98,"series":1,"stats":{"get_bytes":106477,"get_count":154,"put_bytes":0,"put_count":0}} -{"i":99,"series":1,"stats":{"get_bytes":109022,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":0,"series":1,"stats":{"get_bytes":111147,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":111123,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":105868,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":104488,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":110464,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":110865,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":102853,"get_count":144,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":108169,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":113851,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":109300,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":111910,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":113787,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":113924,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":107260,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":109288,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":109393,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":109613,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":108303,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":103074,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":111344,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":103638,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":106242,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":103465,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":112889,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":111818,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":108993,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":107548,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":107130,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":108386,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":106801,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":112167,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":103982,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":112439,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":107657,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":109317,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":106354,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":107451,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":108700,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":109289,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":103575,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":108045,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":107427,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":109306,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":107437,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":111389,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":110509,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":111173,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":109922,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":112633,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":110165,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":105012,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":106947,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":110335,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":105735,"get_count":143,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":110458,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":105383,"get_count":139,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":106564,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":109976,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":108742,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":111084,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":108269,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":112349,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":104081,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":112114,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":111501,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":104248,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":110096,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":109958,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":111248,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":109970,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":112618,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":106639,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":107945,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":107270,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":105831,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":112476,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":109365,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":107222,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":107974,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":108445,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":108864,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":112180,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":108594,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":103941,"get_count":142,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":113079,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":107886,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":109679,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":107472,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":108065,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":111805,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":114330,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":107387,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":114088,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":107972,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":109341,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":109982,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":106179,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":108027,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":106394,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":108939,"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 index a0e4484c991a26acc37caaa171ac9d68d3763ce6..14763fc6a54c72b615c749c936a016ee59925e6c 100644 GIT binary patch literal 19436 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⋙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